搜索完成
@ -37,6 +37,14 @@
|
|||||||
BF02B8222E2FAB1600172177 /* BRAboutUsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B8212E2FAB1600172177 /* BRAboutUsViewController.swift */; };
|
BF02B8222E2FAB1600172177 /* BRAboutUsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B8212E2FAB1600172177 /* BRAboutUsViewController.swift */; };
|
||||||
BF02B8242E2FAEB500172177 /* BRAboutUsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B8232E2FAEB500172177 /* BRAboutUsCell.swift */; };
|
BF02B8242E2FAEB500172177 /* BRAboutUsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B8232E2FAEB500172177 /* BRAboutUsCell.swift */; };
|
||||||
BF02B8262E2FB36A00172177 /* BRAboutUsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B8252E2FB36A00172177 /* BRAboutUsHeaderView.swift */; };
|
BF02B8262E2FB36A00172177 /* BRAboutUsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B8252E2FB36A00172177 /* BRAboutUsHeaderView.swift */; };
|
||||||
|
BF02B8282E30821B00172177 /* BRSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B8272E30821B00172177 /* BRSearchViewController.swift */; };
|
||||||
|
BF02B82D2E30855300172177 /* BRSearchTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B82C2E30855300172177 /* BRSearchTextView.swift */; };
|
||||||
|
BF02B82F2E30895700172177 /* BRSearchRecordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B82E2E30895700172177 /* BRSearchRecordView.swift */; };
|
||||||
|
BF02B8312E30897700172177 /* BRSearchHomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B8302E30897700172177 /* BRSearchHomeView.swift */; };
|
||||||
|
BF02B8332E308E4300172177 /* BRSearchRecordTagCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B8322E308E4300172177 /* BRSearchRecordTagCell.swift */; };
|
||||||
|
BF02B8362E30ACEE00172177 /* BRSearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B8352E30ACEE00172177 /* BRSearchViewModel.swift */; };
|
||||||
|
BF02B8392E30B30400172177 /* AlignedCollectionViewFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B8382E30B30400172177 /* AlignedCollectionViewFlowLayout.swift */; };
|
||||||
|
BF02B83B2E30BB4C00172177 /* BRHotSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF02B83A2E30BB4C00172177 /* BRHotSearchView.swift */; };
|
||||||
BF0DBDD12E0D4E150035F6B4 /* BRTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0DBDD02E0D4E150035F6B4 /* BRTabBar.swift */; };
|
BF0DBDD12E0D4E150035F6B4 /* BRTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0DBDD02E0D4E150035F6B4 /* BRTabBar.swift */; };
|
||||||
BF3338E82E15219500B10F76 /* UINavigationBar+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338E72E15218F00B10F76 /* UINavigationBar+BRAdd.swift */; };
|
BF3338E82E15219500B10F76 /* UINavigationBar+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338E72E15218F00B10F76 /* UINavigationBar+BRAdd.swift */; };
|
||||||
BF3338EA2E152B8100B10F76 /* BRPlayerCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338E92E152B8100B10F76 /* BRPlayerCache.swift */; };
|
BF3338EA2E152B8100B10F76 /* BRPlayerCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338E92E152B8100B10F76 /* BRPlayerCache.swift */; };
|
||||||
@ -48,6 +56,9 @@
|
|||||||
BF3338F92E16178700B10F76 /* BRDetailControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338F82E16178700B10F76 /* BRDetailControlView.swift */; };
|
BF3338F92E16178700B10F76 /* BRDetailControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338F82E16178700B10F76 /* BRDetailControlView.swift */; };
|
||||||
BF3338FB2E161CF900B10F76 /* NSNumber+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338FA2E161CF000B10F76 /* NSNumber+BRAdd.swift */; };
|
BF3338FB2E161CF900B10F76 /* NSNumber+BRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338FA2E161CF000B10F76 /* NSNumber+BRAdd.swift */; };
|
||||||
BF3338FD2E1626B000B10F76 /* BRPlayerControlProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338FC2E1626A500B10F76 /* BRPlayerControlProtocol.swift */; };
|
BF3338FD2E1626B000B10F76 /* BRPlayerControlProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3338FC2E1626A500B10F76 /* BRPlayerControlProtocol.swift */; };
|
||||||
|
BF3A56812E30C08F009E5CF9 /* BRHotSearchTagCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3A56802E30C08F009E5CF9 /* BRHotSearchTagCell.swift */; };
|
||||||
|
BF3A56832E30C561009E5CF9 /* BRSearchResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3A56822E30C561009E5CF9 /* BRSearchResultView.swift */; };
|
||||||
|
BF3A56852E30CA78009E5CF9 /* BRSearchResultCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3A56842E30CA78009E5CF9 /* BRSearchResultCell.swift */; };
|
||||||
BF692AEB2E0A475D00A5C2DA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AE12E0A475D00A5C2DA /* AppDelegate.swift */; };
|
BF692AEB2E0A475D00A5C2DA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AE12E0A475D00A5C2DA /* AppDelegate.swift */; };
|
||||||
BF692AEC2E0A475D00A5C2DA /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AE82E0A475D00A5C2DA /* SceneDelegate.swift */; };
|
BF692AEC2E0A475D00A5C2DA /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AE82E0A475D00A5C2DA /* SceneDelegate.swift */; };
|
||||||
BF692AEE2E0A475D00A5C2DA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF692AE22E0A475D00A5C2DA /* Assets.xcassets */; };
|
BF692AEE2E0A475D00A5C2DA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF692AE22E0A475D00A5C2DA /* Assets.xcassets */; };
|
||||||
@ -171,6 +182,14 @@
|
|||||||
BF02B8212E2FAB1600172177 /* BRAboutUsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRAboutUsViewController.swift; sourceTree = "<group>"; };
|
BF02B8212E2FAB1600172177 /* BRAboutUsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRAboutUsViewController.swift; sourceTree = "<group>"; };
|
||||||
BF02B8232E2FAEB500172177 /* BRAboutUsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRAboutUsCell.swift; sourceTree = "<group>"; };
|
BF02B8232E2FAEB500172177 /* BRAboutUsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRAboutUsCell.swift; sourceTree = "<group>"; };
|
||||||
BF02B8252E2FB36A00172177 /* BRAboutUsHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRAboutUsHeaderView.swift; sourceTree = "<group>"; };
|
BF02B8252E2FB36A00172177 /* BRAboutUsHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRAboutUsHeaderView.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B8272E30821B00172177 /* BRSearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSearchViewController.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B82C2E30855300172177 /* BRSearchTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSearchTextView.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B82E2E30895700172177 /* BRSearchRecordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSearchRecordView.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B8302E30897700172177 /* BRSearchHomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSearchHomeView.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B8322E308E4300172177 /* BRSearchRecordTagCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSearchRecordTagCell.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B8352E30ACEE00172177 /* BRSearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSearchViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B8382E30B30400172177 /* AlignedCollectionViewFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlignedCollectionViewFlowLayout.swift; sourceTree = "<group>"; };
|
||||||
|
BF02B83A2E30BB4C00172177 /* BRHotSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRHotSearchView.swift; sourceTree = "<group>"; };
|
||||||
BF0DBDD02E0D4E150035F6B4 /* BRTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRTabBar.swift; sourceTree = "<group>"; };
|
BF0DBDD02E0D4E150035F6B4 /* BRTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRTabBar.swift; sourceTree = "<group>"; };
|
||||||
BF3338E72E15218F00B10F76 /* UINavigationBar+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationBar+BRAdd.swift"; sourceTree = "<group>"; };
|
BF3338E72E15218F00B10F76 /* UINavigationBar+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationBar+BRAdd.swift"; sourceTree = "<group>"; };
|
||||||
BF3338E92E152B8100B10F76 /* BRPlayerCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPlayerCache.swift; sourceTree = "<group>"; };
|
BF3338E92E152B8100B10F76 /* BRPlayerCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPlayerCache.swift; sourceTree = "<group>"; };
|
||||||
@ -182,6 +201,9 @@
|
|||||||
BF3338F82E16178700B10F76 /* BRDetailControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRDetailControlView.swift; sourceTree = "<group>"; };
|
BF3338F82E16178700B10F76 /* BRDetailControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRDetailControlView.swift; sourceTree = "<group>"; };
|
||||||
BF3338FA2E161CF000B10F76 /* NSNumber+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSNumber+BRAdd.swift"; sourceTree = "<group>"; };
|
BF3338FA2E161CF000B10F76 /* NSNumber+BRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSNumber+BRAdd.swift"; sourceTree = "<group>"; };
|
||||||
BF3338FC2E1626A500B10F76 /* BRPlayerControlProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPlayerControlProtocol.swift; sourceTree = "<group>"; };
|
BF3338FC2E1626A500B10F76 /* BRPlayerControlProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRPlayerControlProtocol.swift; sourceTree = "<group>"; };
|
||||||
|
BF3A56802E30C08F009E5CF9 /* BRHotSearchTagCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRHotSearchTagCell.swift; sourceTree = "<group>"; };
|
||||||
|
BF3A56822E30C561009E5CF9 /* BRSearchResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSearchResultView.swift; sourceTree = "<group>"; };
|
||||||
|
BF3A56842E30CA78009E5CF9 /* BRSearchResultCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRSearchResultCell.swift; sourceTree = "<group>"; };
|
||||||
BF692AC92E0A475500A5C2DA /* BeeReel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BeeReel.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
BF692AC92E0A475500A5C2DA /* BeeReel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BeeReel.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BF692AE12E0A475D00A5C2DA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
BF692AE12E0A475D00A5C2DA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
BF692AE22E0A475D00A5C2DA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
BF692AE22E0A475D00A5C2DA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
@ -394,6 +416,55 @@
|
|||||||
path = WebView;
|
path = WebView;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
BF02B8292E30851900172177 /* Search */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF02B82A2E30852700172177 /* Controller */,
|
||||||
|
BF02B82B2E30852F00172177 /* View */,
|
||||||
|
BF02B8342E30ACCE00172177 /* ViewModel */,
|
||||||
|
);
|
||||||
|
path = Search;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF02B82A2E30852700172177 /* Controller */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF02B8272E30821B00172177 /* BRSearchViewController.swift */,
|
||||||
|
);
|
||||||
|
path = Controller;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF02B82B2E30852F00172177 /* View */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF3A56822E30C561009E5CF9 /* BRSearchResultView.swift */,
|
||||||
|
BF3A56842E30CA78009E5CF9 /* BRSearchResultCell.swift */,
|
||||||
|
BF02B8302E30897700172177 /* BRSearchHomeView.swift */,
|
||||||
|
BF02B82C2E30855300172177 /* BRSearchTextView.swift */,
|
||||||
|
BF02B82E2E30895700172177 /* BRSearchRecordView.swift */,
|
||||||
|
BF02B8322E308E4300172177 /* BRSearchRecordTagCell.swift */,
|
||||||
|
BF02B83A2E30BB4C00172177 /* BRHotSearchView.swift */,
|
||||||
|
BF3A56802E30C08F009E5CF9 /* BRHotSearchTagCell.swift */,
|
||||||
|
);
|
||||||
|
path = View;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF02B8342E30ACCE00172177 /* ViewModel */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF02B8352E30ACEE00172177 /* BRSearchViewModel.swift */,
|
||||||
|
);
|
||||||
|
path = ViewModel;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF02B8372E30B2F800172177 /* AlignedCollectionViewFlowLayout */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF02B8382E30B30400172177 /* AlignedCollectionViewFlowLayout.swift */,
|
||||||
|
);
|
||||||
|
path = AlignedCollectionViewFlowLayout;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
BF3338ED2E15566C00B10F76 /* Explore */ = {
|
BF3338ED2E15566C00B10F76 /* Explore */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -490,6 +561,7 @@
|
|||||||
BF692AF52E0A47D400A5C2DA /* Class */ = {
|
BF692AF52E0A47D400A5C2DA /* Class */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
BF02B8292E30851900172177 /* Search */,
|
||||||
BF02B8102E2F83A100172177 /* Mine */,
|
BF02B8102E2F83A100172177 /* Mine */,
|
||||||
BF02B8052E2F612200172177 /* Favorites */,
|
BF02B8052E2F612200172177 /* Favorites */,
|
||||||
BF3338ED2E15566C00B10F76 /* Explore */,
|
BF3338ED2E15566C00B10F76 /* Explore */,
|
||||||
@ -515,6 +587,7 @@
|
|||||||
BF692AF72E0A480E00A5C2DA /* Thirdparty */ = {
|
BF692AF72E0A480E00A5C2DA /* Thirdparty */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
BF02B8372E30B2F800172177 /* AlignedCollectionViewFlowLayout */,
|
||||||
BFC676A62E12AF04006659E5 /* FlowLayout */,
|
BFC676A62E12AF04006659E5 /* FlowLayout */,
|
||||||
BFC676612E0E2C8E006659E5 /* WMZBanner */,
|
BFC676612E0E2C8E006659E5 /* WMZBanner */,
|
||||||
BF692B292E0A84F700A5C2DA /* JXUUID */,
|
BF692B292E0A84F700A5C2DA /* JXUUID */,
|
||||||
@ -1016,6 +1089,7 @@
|
|||||||
BFC676732E0E938B006659E5 /* BRTableView.swift in Sources */,
|
BFC676732E0E938B006659E5 /* BRTableView.swift in Sources */,
|
||||||
BFC676932E126A62006659E5 /* BRSpotlightNewMainCell.swift in Sources */,
|
BFC676932E126A62006659E5 /* BRSpotlightNewMainCell.swift in Sources */,
|
||||||
BFC6768D2E123D6E006659E5 /* AttributedString+BRAdd.swift in Sources */,
|
BFC6768D2E123D6E006659E5 /* AttributedString+BRAdd.swift in Sources */,
|
||||||
|
BF02B8392E30B30400172177 /* AlignedCollectionViewFlowLayout.swift in Sources */,
|
||||||
BF3338F52E1616B200B10F76 /* BRExploreControlView.swift in Sources */,
|
BF3338F52E1616B200B10F76 /* BRExploreControlView.swift in Sources */,
|
||||||
BF692B132E0A7B9000A5C2DA /* BRUserInfo.swift in Sources */,
|
BF692B132E0A7B9000A5C2DA /* BRUserInfo.swift in Sources */,
|
||||||
BF692B042E0A76D200A5C2DA /* BRLoginManager.swift in Sources */,
|
BF692B042E0A76D200A5C2DA /* BRLoginManager.swift in Sources */,
|
||||||
@ -1031,6 +1105,7 @@
|
|||||||
BF02B7E12E2DE64200172177 /* BRVideoProgressView.swift in Sources */,
|
BF02B7E12E2DE64200172177 /* BRVideoProgressView.swift in Sources */,
|
||||||
BF692B542E0AA8FA00A5C2DA /* BRCollectionView.swift in Sources */,
|
BF692B542E0AA8FA00A5C2DA /* BRCollectionView.swift in Sources */,
|
||||||
BF3338E82E15219500B10F76 /* UINavigationBar+BRAdd.swift in Sources */,
|
BF3338E82E15219500B10F76 /* UINavigationBar+BRAdd.swift in Sources */,
|
||||||
|
BF02B82D2E30855300172177 /* BRSearchTextView.swift in Sources */,
|
||||||
BF692B472E0A9B7900A5C2DA /* BRPlayer.swift in Sources */,
|
BF692B472E0A9B7900A5C2DA /* BRPlayer.swift in Sources */,
|
||||||
BF692B6E2E0BD4CB00A5C2DA /* BRHomeHeaderView.swift in Sources */,
|
BF692B6E2E0BD4CB00A5C2DA /* BRHomeHeaderView.swift in Sources */,
|
||||||
BF692AFA2E0A6F0900A5C2DA /* BRNetwork.swift in Sources */,
|
BF692AFA2E0A6F0900A5C2DA /* BRNetwork.swift in Sources */,
|
||||||
@ -1039,14 +1114,17 @@
|
|||||||
BF692B7C2E0D3C1300A5C2DA /* BRVideoInfoModel.swift in Sources */,
|
BF692B7C2E0D3C1300A5C2DA /* BRVideoInfoModel.swift in Sources */,
|
||||||
BFC6766D2E0E3A8D006659E5 /* BRImageView.swift in Sources */,
|
BFC6766D2E0E3A8D006659E5 /* BRImageView.swift in Sources */,
|
||||||
BF02B7ED2E2E390500172177 /* BRScrollView.swift in Sources */,
|
BF02B7ED2E2E390500172177 /* BRScrollView.swift in Sources */,
|
||||||
|
BF3A56812E30C08F009E5CF9 /* BRHotSearchTagCell.swift in Sources */,
|
||||||
BF02B8132E2F83C200172177 /* BRMineViewController.swift in Sources */,
|
BF02B8132E2F83C200172177 /* BRMineViewController.swift in Sources */,
|
||||||
BF02B8222E2FAB1600172177 /* BRAboutUsViewController.swift in Sources */,
|
BF02B8222E2FAB1600172177 /* BRAboutUsViewController.swift in Sources */,
|
||||||
BFC6766F2E0E3B5C006659E5 /* UIImageView+BRAdd.swift in Sources */,
|
BFC6766F2E0E3B5C006659E5 /* UIImageView+BRAdd.swift in Sources */,
|
||||||
BF692B782E0D3A1200A5C2DA /* BRHomeModuleItem.swift in Sources */,
|
BF692B782E0D3A1200A5C2DA /* BRHomeModuleItem.swift in Sources */,
|
||||||
BF692B5A2E0AAADD00A5C2DA /* BRPlayerListCell.swift in Sources */,
|
BF692B5A2E0AAADD00A5C2DA /* BRPlayerListCell.swift in Sources */,
|
||||||
|
BF02B8312E30897700172177 /* BRSearchHomeView.swift in Sources */,
|
||||||
BF692B162E0A7CD600A5C2DA /* BRHUD.swift in Sources */,
|
BF692B162E0A7CD600A5C2DA /* BRHUD.swift in Sources */,
|
||||||
BF3338F72E16176900B10F76 /* BRDetailPlayerCell.swift in Sources */,
|
BF3338F72E16176900B10F76 /* BRDetailPlayerCell.swift in Sources */,
|
||||||
BF3338EA2E152B8100B10F76 /* BRPlayerCache.swift in Sources */,
|
BF3338EA2E152B8100B10F76 /* BRPlayerCache.swift in Sources */,
|
||||||
|
BF3A56832E30C561009E5CF9 /* BRSearchResultView.swift in Sources */,
|
||||||
BFC676952E126BBF006659E5 /* BRSpotlightNewCell.swift in Sources */,
|
BFC676952E126BBF006659E5 /* BRSpotlightNewCell.swift in Sources */,
|
||||||
BF692B402E0A8FA100A5C2DA /* UIColor+BRAdd.swift in Sources */,
|
BF692B402E0A8FA100A5C2DA /* UIColor+BRAdd.swift in Sources */,
|
||||||
BF692B102E0A7B4300A5C2DA /* BRUserDefaultsKey.swift in Sources */,
|
BF692B102E0A7B4300A5C2DA /* BRUserDefaultsKey.swift in Sources */,
|
||||||
@ -1054,6 +1132,7 @@
|
|||||||
BFC676912E126248006659E5 /* BRSpotlightTopCell.swift in Sources */,
|
BFC676912E126248006659E5 /* BRSpotlightTopCell.swift in Sources */,
|
||||||
BFC676B72E137DFC006659E5 /* BRPopularPicksCell.swift in Sources */,
|
BFC676B72E137DFC006659E5 /* BRPopularPicksCell.swift in Sources */,
|
||||||
BF692B422E0A8FB500A5C2DA /* UIFont+BRAdd.swift in Sources */,
|
BF692B422E0A8FB500A5C2DA /* UIFont+BRAdd.swift in Sources */,
|
||||||
|
BF02B8362E30ACEE00172177 /* BRSearchViewModel.swift in Sources */,
|
||||||
BF692AEC2E0A475D00A5C2DA /* SceneDelegate.swift in Sources */,
|
BF692AEC2E0A475D00A5C2DA /* SceneDelegate.swift in Sources */,
|
||||||
BF692B492E0A9D0E00A5C2DA /* UIView+BRAdd.swift in Sources */,
|
BF692B492E0A9D0E00A5C2DA /* UIView+BRAdd.swift in Sources */,
|
||||||
BF02B7F82E2F211A00172177 /* BRHomeCategoriesMainCell.swift in Sources */,
|
BF02B7F82E2F211A00172177 /* BRHomeCategoriesMainCell.swift in Sources */,
|
||||||
@ -1078,6 +1157,8 @@
|
|||||||
BF692B582E0AAA6F00A5C2DA /* UIScreen+BRAdd.swift in Sources */,
|
BF692B582E0AAA6F00A5C2DA /* UIScreen+BRAdd.swift in Sources */,
|
||||||
BF692B1F2E0A804600A5C2DA /* BRLocalizedManager.swift in Sources */,
|
BF692B1F2E0A804600A5C2DA /* BRLocalizedManager.swift in Sources */,
|
||||||
BF02B7E92E2E29E900172177 /* BREpisodeSelectorCell.swift in Sources */,
|
BF02B7E92E2E29E900172177 /* BREpisodeSelectorCell.swift in Sources */,
|
||||||
|
BF02B83B2E30BB4C00172177 /* BRHotSearchView.swift in Sources */,
|
||||||
|
BF02B8332E308E4300172177 /* BRSearchRecordTagCell.swift in Sources */,
|
||||||
BF692B612E0B814F00A5C2DA /* BRTabBarItemContentView.swift in Sources */,
|
BF692B612E0B814F00A5C2DA /* BRTabBarItemContentView.swift in Sources */,
|
||||||
BF02B7F12E2E55E300172177 /* BRRateSelectorView.swift in Sources */,
|
BF02B7F12E2E55E300172177 /* BRRateSelectorView.swift in Sources */,
|
||||||
BF692B012E0A74A200A5C2DA /* BRDefine.swift in Sources */,
|
BF692B012E0A74A200A5C2DA /* BRDefine.swift in Sources */,
|
||||||
@ -1120,14 +1201,17 @@
|
|||||||
BF692B562E0AA92100A5C2DA /* BRCollectionViewCell.swift in Sources */,
|
BF692B562E0AA92100A5C2DA /* BRCollectionViewCell.swift in Sources */,
|
||||||
BF02B7E32E2E08BD00172177 /* BRDetailEpButton.swift in Sources */,
|
BF02B7E32E2E08BD00172177 /* BRDetailEpButton.swift in Sources */,
|
||||||
BF692B072E0A771C00A5C2DA /* BRModel.swift in Sources */,
|
BF692B072E0A771C00A5C2DA /* BRModel.swift in Sources */,
|
||||||
|
BF02B82F2E30895700172177 /* BRSearchRecordView.swift in Sources */,
|
||||||
BF692B752E0D39D000A5C2DA /* BRListModel.swift in Sources */,
|
BF692B752E0D39D000A5C2DA /* BRListModel.swift in Sources */,
|
||||||
BF02B8172E2F881200172177 /* BRMineItem.swift in Sources */,
|
BF02B8172E2F881200172177 /* BRMineItem.swift in Sources */,
|
||||||
BFC676B92E1385FC006659E5 /* BRPopularPicksSmallCell.swift in Sources */,
|
BFC676B92E1385FC006659E5 /* BRPopularPicksSmallCell.swift in Sources */,
|
||||||
BF692B512E0AA8C600A5C2DA /* BRPlayerListViewController.swift in Sources */,
|
BF692B512E0AA8C600A5C2DA /* BRPlayerListViewController.swift in Sources */,
|
||||||
|
BF02B8282E30821B00172177 /* BRSearchViewController.swift in Sources */,
|
||||||
BFC676522E0D4EFD006659E5 /* BRHomeViewModel.swift in Sources */,
|
BFC676522E0D4EFD006659E5 /* BRHomeViewModel.swift in Sources */,
|
||||||
BFC676A72E12AF04006659E5 /* WaterfallMutiSectionFlowLayout.swift in Sources */,
|
BFC676A72E12AF04006659E5 /* WaterfallMutiSectionFlowLayout.swift in Sources */,
|
||||||
BF692B2A2E0A84F700A5C2DA /* JXUUID.m in Sources */,
|
BF692B2A2E0A84F700A5C2DA /* JXUUID.m in Sources */,
|
||||||
BF692B2B2E0A84F700A5C2DA /* PDKeyChain.m in Sources */,
|
BF692B2B2E0A84F700A5C2DA /* PDKeyChain.m in Sources */,
|
||||||
|
BF3A56852E30CA78009E5CF9 /* BRSearchResultCell.swift in Sources */,
|
||||||
BF02B7FA2E2F225D00172177 /* BRHomeCategoryModel.swift in Sources */,
|
BF02B7FA2E2F225D00172177 /* BRHomeCategoryModel.swift in Sources */,
|
||||||
BFC6766B2E0E395F006659E5 /* BRHomeHeaderBannerCell.swift in Sources */,
|
BFC6766B2E0E395F006659E5 /* BRHomeHeaderBannerCell.swift in Sources */,
|
||||||
);
|
);
|
||||||
|
@ -6,13 +6,14 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import FDFullscreenPopGesture
|
||||||
|
|
||||||
class BRNavigationController: UINavigationController {
|
class BRNavigationController: UINavigationController {
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
fd_fullscreenPopGestureRecognizer.isEnabled = true
|
||||||
|
|
||||||
// Do any additional setup after loading the view.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
|
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
|
||||||
|
@ -10,3 +10,24 @@ import SmartCodable
|
|||||||
extension String: SmartCodable {
|
extension String: SmartCodable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
|
||||||
|
func br_range(of searchString: String) -> [NSRange] {
|
||||||
|
|
||||||
|
do {
|
||||||
|
let newSearch = searchString.lowercased()
|
||||||
|
let newText = self.lowercased()
|
||||||
|
|
||||||
|
let regex = try NSRegularExpression(pattern: newSearch)
|
||||||
|
let matches = regex.matches(in: newText, range: NSRange(self.startIndex..., in: newText))
|
||||||
|
|
||||||
|
let ranges = matches.map { $0.range }
|
||||||
|
|
||||||
|
return ranges
|
||||||
|
} catch {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -94,4 +94,32 @@ extension UIColor {
|
|||||||
static func colorB7B7B7(alpha: CGFloat = 1) -> UIColor {
|
static func colorB7B7B7(alpha: CGFloat = 1) -> UIColor {
|
||||||
return UIColor(rgb: 0xB7B7B7, alpha: alpha)
|
return UIColor(rgb: 0xB7B7B7, alpha: alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func colorF2F2F2(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return UIColor(rgb: 0xF2F2F2, alpha: alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func colorFFF3F5(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return UIColor(rgb: 0xFFF3F5, alpha: alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func colorE4FEFF(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return UIColor(rgb: 0xE4FEFF, alpha: alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func colorFFFDF1(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return UIColor(rgb: 0xFFFDF1, alpha: alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func colorFFD8DE(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return UIColor(rgb: 0xFFD8DE, alpha: alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func color82F8FF(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return UIColor(rgb: 0x82F8FF, alpha: alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func colorFFEFA1(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return UIColor(rgb: 0xFFEFA1, alpha: alpha)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,4 +81,30 @@ class BRHomeAPI {
|
|||||||
completer?(response.data)
|
completer?(response.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///热门搜索
|
||||||
|
static func requestHotSearchList(completer: ((_ list: [BRShortModel]?) -> Void)?) {
|
||||||
|
var param = BRNetworkParameters(path: "/search/hots")
|
||||||
|
param.isLoding = false
|
||||||
|
param.isToast = false
|
||||||
|
param.method = .get
|
||||||
|
|
||||||
|
BRNetwork.request(parameters: param) { (response: BRNetworkResponse<BRListModel<BRShortModel>>) in
|
||||||
|
completer?(response.data?.list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///搜索
|
||||||
|
static func requestSearch(text: String, completer: ((_ list: [BRShortModel]?) -> Void)?) {
|
||||||
|
var param = BRNetworkParameters(path: "/search")
|
||||||
|
param.method = .get
|
||||||
|
param.parameters = [
|
||||||
|
"search" : text
|
||||||
|
]
|
||||||
|
|
||||||
|
BRNetwork.request(parameters: param) { (response: BRNetworkResponse<BRListModel<BRShortModel>>) in
|
||||||
|
completer?(response.data?.list)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,8 @@ extension BRNetworkTarget: TargetType {
|
|||||||
"system-type" : "ios",
|
"system-type" : "ios",
|
||||||
"idfa" : JXUUID.idfa(),
|
"idfa" : JXUUID.idfa(),
|
||||||
"model" : UIDevice.br_machineModelName(),
|
"model" : UIDevice.br_machineModelName(),
|
||||||
"authorization" : userToken
|
"authorization" : userToken,
|
||||||
|
"device-gaid" : JXUUID.idfv()
|
||||||
]
|
]
|
||||||
return dic
|
return dic
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ class BRExploreViewController: BRPlayerListViewController {
|
|||||||
private lazy var searchButton: UIButton = {
|
private lazy var searchButton: UIButton = {
|
||||||
let button = UIButton(type: .custom)
|
let button = UIButton(type: .custom)
|
||||||
button.setImage(UIImage(named: "search_icon_01"), for: .normal)
|
button.setImage(UIImage(named: "search_icon_01"), for: .normal)
|
||||||
|
button.addTarget(self, action: #selector(handleSearchButton), for: .touchUpInside)
|
||||||
return button
|
return button
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -158,7 +159,6 @@ extension BRExploreViewController {
|
|||||||
BRVideoAPI.requestFavorite(isFavorite: isFavorite, shortPlayId: shortPlayId, videoId: shortModel?.short_play_video_id) {
|
BRVideoAPI.requestFavorite(isFavorite: isFavorite, shortPlayId: shortPlayId, videoId: shortModel?.short_play_video_id) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func handleEpisodeButton() {
|
@objc private func handleEpisodeButton() {
|
||||||
@ -167,6 +167,11 @@ extension BRExploreViewController {
|
|||||||
self.navigationController?.pushViewController(vc, animated: true)
|
self.navigationController?.pushViewController(vc, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func handleSearchButton() {
|
||||||
|
let vc = BRSearchViewController()
|
||||||
|
self.navigationController?.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,6 +15,12 @@ class BRFavoritesViewController: BRViewController {
|
|||||||
///播放历史
|
///播放历史
|
||||||
private var playHistoryModel: BRShortModel? {
|
private var playHistoryModel: BRShortModel? {
|
||||||
didSet {
|
didSet {
|
||||||
|
if let _ = playHistoryModel {
|
||||||
|
self.collectionViewLayout.headerReferenceSize = .init(width: UIScreen.width, height: 180)
|
||||||
|
} else {
|
||||||
|
self.collectionViewLayout.headerReferenceSize = .zero
|
||||||
|
}
|
||||||
|
|
||||||
collectionView.reloadData()
|
collectionView.reloadData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,7 +39,7 @@ class BRFavoritesViewController: BRViewController {
|
|||||||
layout.minimumInteritemSpacing = 11
|
layout.minimumInteritemSpacing = 11
|
||||||
layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15)
|
layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15)
|
||||||
layout.itemSize = .init(width: width, height: height)
|
layout.itemSize = .init(width: width, height: height)
|
||||||
layout.headerReferenceSize = .init(width: UIScreen.width, height: 180)
|
layout.headerReferenceSize = .zero
|
||||||
return layout
|
return layout
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -41,6 +47,12 @@ class BRFavoritesViewController: BRViewController {
|
|||||||
let collectionView = BRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
let collectionView = BRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
||||||
collectionView.delegate = self
|
collectionView.delegate = self
|
||||||
collectionView.dataSource = self
|
collectionView.dataSource = self
|
||||||
|
collectionView.br_addRefreshHeader { [weak self] in
|
||||||
|
self?.handleHeaderRefresh(nil)
|
||||||
|
}
|
||||||
|
collectionView.br_addRefreshBackFooter { [weak self] in
|
||||||
|
self?.handleFooterRefresh(nil)
|
||||||
|
}
|
||||||
collectionView.register(BRFavoritesCell.self, forCellWithReuseIdentifier: "cell")
|
collectionView.register(BRFavoritesCell.self, forCellWithReuseIdentifier: "cell")
|
||||||
collectionView.register(BRFavoritesHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "headerView")
|
collectionView.register(BRFavoritesHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "headerView")
|
||||||
return collectionView
|
return collectionView
|
||||||
@ -71,7 +83,26 @@ class BRFavoritesViewController: BRViewController {
|
|||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
self.navigationController?.setNavigationBarHidden(true, animated: true)
|
self.navigationController?.setNavigationBarHidden(true, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
if self.hasViewDidAppear {
|
||||||
|
requestPlayHistorys()
|
||||||
|
}
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func handleHeaderRefresh(_ completer: (() -> Void)?) {
|
||||||
|
self.requestPlayHistorys()
|
||||||
|
self.requestDataList(page: 1) { [weak self] in
|
||||||
|
self?.collectionView.br_endHeaderRefreshing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func handleFooterRefresh(_ completer: (() -> Void)?) {
|
||||||
|
self.requestDataList(page: self.page + 1) { [weak self] in
|
||||||
|
self?.collectionView.br_endFooterRefreshing()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -133,6 +133,7 @@ class BRHomeViewController: BRViewController {
|
|||||||
private lazy var searchButton: UIButton = {
|
private lazy var searchButton: UIButton = {
|
||||||
let button = UIButton(type: .custom)
|
let button = UIButton(type: .custom)
|
||||||
button.setImage(UIImage(named: "search_button_01"), for: .normal)
|
button.setImage(UIImage(named: "search_button_01"), for: .normal)
|
||||||
|
button.addTarget(self, action: #selector(handleSearchButton), for: .touchUpInside)
|
||||||
return button
|
return button
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -237,6 +238,14 @@ extension BRHomeViewController {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension BRHomeViewController {
|
||||||
|
@objc private func handleSearchButton() {
|
||||||
|
let vc = BRSearchViewController()
|
||||||
|
|
||||||
|
self.navigationController?.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension BRHomeViewController {
|
extension BRHomeViewController {
|
||||||
|
|
||||||
private func requestHomeData(completer: (() -> Void)? = nil) {
|
private func requestHomeData(completer: (() -> Void)? = nil) {
|
||||||
|
@ -15,22 +15,6 @@ class BRShortModel: BRModel, SmartCodable {
|
|||||||
case r_720 = "720"
|
case r_720 = "720"
|
||||||
case r_1080 = "1080"
|
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 {
|
var toString: String {
|
||||||
return "\(self.rawValue)P"
|
return "\(self.rawValue)P"
|
||||||
}
|
}
|
||||||
@ -67,11 +51,6 @@ class BRShortModel: BRModel, SmartCodable {
|
|||||||
|
|
||||||
var revolution: VideoRevolution?
|
var revolution: VideoRevolution?
|
||||||
|
|
||||||
@IgnoredKey
|
|
||||||
var titleAttributedString: NSAttributedString?
|
|
||||||
@IgnoredKey
|
|
||||||
var br_isSelected: Bool?
|
|
||||||
|
|
||||||
|
|
||||||
static func mappingForKey() -> [SmartKeyTransformer]? {
|
static func mappingForKey() -> [SmartKeyTransformer]? {
|
||||||
return [
|
return [
|
||||||
|
@ -33,7 +33,7 @@ class BRAboutUsViewController: BRViewController {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var headerView: BRAboutUsHeaderView = {
|
private lazy var headerView: BRAboutUsHeaderView = {
|
||||||
let view = BRAboutUsHeaderView(frame: .init(x: 0, y: 0, width: UIScreen.width, height: 200))
|
let view = BRAboutUsHeaderView(frame: .init(x: 0, y: 0, width: UIScreen.width, height: 250))
|
||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ class BRMineViewController: BRViewController {
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
BRMineItem(type: .web, icon: UIImage(named: "mine_item_icon_01"), title: "Privacy Policy".localized, url: kSBPrivacyPolicyWebUrl),
|
BRMineItem(type: .web, icon: UIImage(named: "mine_item_icon_01"), title: "Privacy Policy".localized, url: kSBPrivacyPolicyWebUrl),
|
||||||
BRMineItem(type: .helpCenter, icon: UIImage(named: "mine_item_icon_02"), title: "Help Center".localized, url: ""),
|
// BRMineItem(type: .helpCenter, icon: UIImage(named: "mine_item_icon_02"), title: "Help Center".localized, url: ""),
|
||||||
BRMineItem(type: .web, icon: UIImage(named: "mine_item_icon_03"), title: "User Agreement".localized, url: kSBUserAgreementWebUrl),
|
BRMineItem(type: .web, icon: UIImage(named: "mine_item_icon_03"), title: "User Agreement".localized, url: kSBUserAgreementWebUrl),
|
||||||
BRMineItem(type: .aboutUs, icon: UIImage(named: "mine_item_icon_04"), title: "About Us".localized, url: ""),
|
BRMineItem(type: .aboutUs, icon: UIImage(named: "mine_item_icon_04"), title: "About Us".localized, url: ""),
|
||||||
]
|
]
|
||||||
@ -41,9 +41,14 @@ class BRMineViewController: BRViewController {
|
|||||||
return tableView
|
return tableView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
NotificationCenter.default.removeObserver(self)
|
||||||
|
}
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
self.edgesForExtendedLayout = [.top, .bottom]
|
self.edgesForExtendedLayout = [.top, .bottom]
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(userInfoUpdateNotification), name: BRLoginManager.userInfoUpdateNotification, object: nil)
|
||||||
|
|
||||||
br_setupUI()
|
br_setupUI()
|
||||||
}
|
}
|
||||||
@ -53,6 +58,10 @@ class BRMineViewController: BRViewController {
|
|||||||
self.navigationController?.setNavigationBarHidden(true, animated: true)
|
self.navigationController?.setNavigationBarHidden(true, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
BRLoginManager.manager.updateUserInfo(completer: nil)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,6 +86,14 @@ extension BRMineViewController {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension BRMineViewController {
|
||||||
|
@objc private func userInfoUpdateNotification() {
|
||||||
|
self.tableView.reloadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//MARK: -------------- UITableViewDelegate UITableViewDataSource --------------
|
//MARK: -------------- UITableViewDelegate UITableViewDataSource --------------
|
||||||
extension BRMineViewController: UITableViewDelegate, UITableViewDataSource {
|
extension BRMineViewController: UITableViewDelegate, UITableViewDataSource {
|
||||||
|
|
||||||
|
@ -9,12 +9,73 @@ import UIKit
|
|||||||
|
|
||||||
class BRAboutUsHeaderView: UIView {
|
class BRAboutUsHeaderView: UIView {
|
||||||
|
|
||||||
/*
|
|
||||||
// Only override draw() if you perform custom drawing.
|
|
||||||
// An empty implementation adversely affects performance during animation.
|
private lazy var appLogoView: UIImageView = {
|
||||||
override func draw(_ rect: CGRect) {
|
let imageView = UIImageView(image: UIImage(named: "logo_icon_01"))
|
||||||
// Drawing code
|
imageView.layer.cornerRadius = 8
|
||||||
|
imageView.layer.masksToBounds = true
|
||||||
|
imageView.isUserInteractionEnabled = true
|
||||||
|
let tap = UITapGestureRecognizer(target: self, action: #selector(handleLogoImageView))
|
||||||
|
imageView.addGestureRecognizer(tap)
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var nameLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontBold(ofSize: 18)
|
||||||
|
label.textColor = .color1C1C1C()
|
||||||
|
label.text = kBRAPPName
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var versionLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontRegular(ofSize: 12)
|
||||||
|
label.textColor = .color1C1C1C(alpha: 0.8)
|
||||||
|
label.text = "version:\(kBRAPPVersion)"
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
br_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func handleLogoImageView() {
|
||||||
|
guard let url = URL(string: BRWebBaseURL) else { return }
|
||||||
|
UIApplication.shared.open(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BRAboutUsHeaderView {
|
||||||
|
|
||||||
|
private func br_setupUI() {
|
||||||
|
addSubview(appLogoView)
|
||||||
|
addSubview(nameLabel)
|
||||||
|
addSubview(versionLabel)
|
||||||
|
|
||||||
|
appLogoView.snp.makeConstraints { make in
|
||||||
|
make.centerX.equalToSuperview()
|
||||||
|
make.top.equalToSuperview().offset(50)
|
||||||
|
make.width.height.equalTo(100)
|
||||||
|
}
|
||||||
|
|
||||||
|
nameLabel.snp.makeConstraints { make in
|
||||||
|
make.centerX.equalToSuperview()
|
||||||
|
make.top.equalTo(appLogoView.snp.bottom).offset(10)
|
||||||
|
}
|
||||||
|
|
||||||
|
versionLabel.snp.makeConstraints { make in
|
||||||
|
make.centerX.equalToSuperview()
|
||||||
|
make.top.equalTo(nameLabel.snp.bottom).offset(5)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ class BRVideoDetailViewController: BRPlayerListViewController {
|
|||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
self.delegate = self
|
self.delegate = self
|
||||||
self.dataSource = self
|
self.dataSource = self
|
||||||
|
self.fd_interactivePopDisabled = true
|
||||||
|
|
||||||
self.requestDetailData()
|
self.requestDetailData()
|
||||||
|
|
||||||
|
119
BeeReel/Class/Search/Controller/BRSearchViewController.swift
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
//
|
||||||
|
// BRSearchViewController.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class BRSearchViewController: BRViewController {
|
||||||
|
|
||||||
|
private lazy var viewModel = BRSearchViewModel()
|
||||||
|
|
||||||
|
private lazy var bgView: UIView = {
|
||||||
|
let view = UIImageView(image: UIImage(named: "搜索bg"))
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var backButton: UIButton = {
|
||||||
|
let button = UIButton(type: .custom)
|
||||||
|
button.setImage(UIImage(named: "nav_back_icon_02"), for: .normal)
|
||||||
|
button.addTarget(self, action: #selector(handleNavBack), for: .touchUpInside)
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var textView: BRSearchTextView = {
|
||||||
|
let view = BRSearchTextView()
|
||||||
|
view.didSearch = { [weak self] text in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.search(text: text)
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var homeView: BRSearchHomeView = {
|
||||||
|
let view = BRSearchHomeView()
|
||||||
|
view.searchText = { [weak self] text in
|
||||||
|
self?.search(text: text)
|
||||||
|
}
|
||||||
|
view.viewModel = self.viewModel
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var resultView: BRSearchResultView = {
|
||||||
|
let view = BRSearchResultView()
|
||||||
|
view.isHidden = true
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
self.view.backgroundColor = .colorFFFFFF()
|
||||||
|
br_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
self.navigationController?.setNavigationBarHidden(true, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BRSearchViewController {
|
||||||
|
|
||||||
|
private func search(text: String) {
|
||||||
|
self.viewModel.addSearchRecord(text: text)
|
||||||
|
self.resultView.search(text: text)
|
||||||
|
|
||||||
|
if self.textView.text != text {
|
||||||
|
self.textView.text = text
|
||||||
|
}
|
||||||
|
if text.isEmpty {
|
||||||
|
self.homeView.isHidden = false
|
||||||
|
self.resultView.isHidden = true
|
||||||
|
} else {
|
||||||
|
self.homeView.isHidden = true
|
||||||
|
self.resultView.isHidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension BRSearchViewController {
|
||||||
|
|
||||||
|
private func br_setupUI() {
|
||||||
|
view.addSubview(bgView)
|
||||||
|
view.addSubview(backButton)
|
||||||
|
view.addSubview(textView)
|
||||||
|
view.addSubview(homeView)
|
||||||
|
view.addSubview(resultView)
|
||||||
|
|
||||||
|
bgView.snp.makeConstraints { make in
|
||||||
|
make.left.top.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
backButton.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(15)
|
||||||
|
make.top.equalToSuperview().offset(UIScreen.statusBarHeight + 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
textView.snp.makeConstraints { make in
|
||||||
|
make.centerY.equalTo(backButton)
|
||||||
|
make.left.equalTo(backButton.snp.right).offset(10)
|
||||||
|
make.right.equalToSuperview().offset(-15)
|
||||||
|
}
|
||||||
|
|
||||||
|
homeView.snp.makeConstraints { make in
|
||||||
|
make.left.right.bottom.equalToSuperview()
|
||||||
|
make.top.equalTo(textView.snp.bottom).offset(20)
|
||||||
|
}
|
||||||
|
|
||||||
|
resultView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalTo(homeView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
67
BeeReel/Class/Search/View/BRHotSearchTagCell.swift
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
//
|
||||||
|
// BRHotSearchTagCell.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class BRHotSearchTagCell: BRCollectionViewCell {
|
||||||
|
|
||||||
|
var model: BRShortModel? {
|
||||||
|
didSet {
|
||||||
|
label.text = model?.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var bgColor: UIColor? {
|
||||||
|
didSet {
|
||||||
|
self.contentView.backgroundColor = bgColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var borderColor: UIColor? {
|
||||||
|
didSet {
|
||||||
|
self.contentView.layer.borderColor = borderColor?.cgColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private lazy var hotIconImageView: UIImageView = {
|
||||||
|
let imageView = UIImageView(image: UIImage(named: "hot_icon_04"))
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var label: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontRegular(ofSize: 12)
|
||||||
|
label.textColor = .color000000()
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.contentView.layer.cornerRadius = 8
|
||||||
|
self.contentView.layer.masksToBounds = true
|
||||||
|
self.contentView.layer.borderWidth = 1
|
||||||
|
|
||||||
|
contentView.addSubview(hotIconImageView)
|
||||||
|
contentView.addSubview(label)
|
||||||
|
|
||||||
|
hotIconImageView.snp.makeConstraints { make in
|
||||||
|
make.centerY.equalToSuperview()
|
||||||
|
make.left.equalToSuperview().offset(9)
|
||||||
|
}
|
||||||
|
|
||||||
|
label.snp.makeConstraints { make in
|
||||||
|
make.centerY.equalToSuperview()
|
||||||
|
make.left.equalTo(hotIconImageView.snp.right).offset(4)
|
||||||
|
make.right.equalToSuperview().offset(-10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
}
|
115
BeeReel/Class/Search/View/BRHotSearchView.swift
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
//
|
||||||
|
// BRHotSearchView.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class BRHotSearchView: UIView {
|
||||||
|
|
||||||
|
var listArr: [BRShortModel] = [] {
|
||||||
|
didSet {
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tagBgColorArr: [UIColor] = [.colorFFF3F5(), .colorE4FEFF(), .colorFFFDF1()]
|
||||||
|
var tagBorderColorArr: [UIColor] = [.colorFFD8DE(), .color82F8FF(), .colorFFEFA1()]
|
||||||
|
|
||||||
|
var searchText: ((_ text: String) -> Void)?
|
||||||
|
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontMedium(ofSize: 15)
|
||||||
|
label.textColor = .color1C1C1C()
|
||||||
|
label.text = "Most Searched".localized
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var collectionViewLayout: AlignedCollectionViewFlowLayout = {
|
||||||
|
let layout = AlignedCollectionViewFlowLayout(horizontalAlignment: .left, verticalAlignment: .top)
|
||||||
|
layout.estimatedItemSize = .init(width: 30, height: 28)
|
||||||
|
layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15)
|
||||||
|
layout.minimumInteritemSpacing = 12
|
||||||
|
layout.minimumLineSpacing = 10
|
||||||
|
return layout
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var collectionView: BRCollectionView = {
|
||||||
|
let collectionView = BRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
||||||
|
collectionView.delegate = self
|
||||||
|
collectionView.dataSource = self
|
||||||
|
collectionView.addObserver(self, forKeyPath: "contentSize", context: nil)
|
||||||
|
collectionView.register(BRHotSearchTagCell.self, forCellWithReuseIdentifier: "tagCell")
|
||||||
|
return collectionView
|
||||||
|
}()
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.collectionView.removeObserver(self, forKeyPath: "contentSize")
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
br_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||||
|
if keyPath == "contentSize" {
|
||||||
|
let height = self.collectionView.contentSize.height
|
||||||
|
self.collectionView.snp.updateConstraints { make in
|
||||||
|
make.height.equalTo(height + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BRHotSearchView {
|
||||||
|
|
||||||
|
private func br_setupUI() {
|
||||||
|
addSubview(titleLabel)
|
||||||
|
addSubview(collectionView)
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(15)
|
||||||
|
make.top.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
collectionView.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview()
|
||||||
|
make.right.equalToSuperview()
|
||||||
|
make.top.equalToSuperview().offset(31)
|
||||||
|
make.height.equalTo(1)
|
||||||
|
make.bottom.equalToSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BRHotSearchView: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
let colorIndex = indexPath.row % 3
|
||||||
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "tagCell", for: indexPath) as! BRHotSearchTagCell
|
||||||
|
cell.model = listArr[indexPath.row]
|
||||||
|
cell.bgColor = tagBgColorArr[colorIndex]
|
||||||
|
cell.borderColor = tagBorderColorArr[colorIndex]
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return listArr.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
guard let text = self.listArr[indexPath.row].name, !text.isEmpty else { return }
|
||||||
|
self.searchText?(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
123
BeeReel/Class/Search/View/BRSearchHomeView.swift
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
//
|
||||||
|
// BRSearchHomeView.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class BRSearchHomeView: UIView {
|
||||||
|
|
||||||
|
weak var viewModel: BRSearchViewModel? {
|
||||||
|
didSet {
|
||||||
|
recordView.listArr = viewModel?.recordList ?? []
|
||||||
|
viewModel?.addObserver(self, forKeyPath: "recordList", context: nil)
|
||||||
|
updateLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var searchText: ((_ text: String) -> Void)?
|
||||||
|
|
||||||
|
private lazy var scrollView: BRScrollView = {
|
||||||
|
let scrollView = BRScrollView()
|
||||||
|
return scrollView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var stackView: UIStackView = {
|
||||||
|
let view = UIStackView()
|
||||||
|
view.axis = .vertical
|
||||||
|
view.spacing = 30
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var recordView: BRSearchRecordView = {
|
||||||
|
let view = BRSearchRecordView()
|
||||||
|
view.clickCleanButton = { [weak self] in
|
||||||
|
self?.viewModel?.clearSearchRecord()
|
||||||
|
}
|
||||||
|
view.searchText = { [weak self] text in
|
||||||
|
self?.searchText?(text)
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var hotView: BRHotSearchView = {
|
||||||
|
let view = BRHotSearchView()
|
||||||
|
view.searchText = { [weak self] text in
|
||||||
|
self?.searchText?(text)
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.viewModel?.removeObserver(self, forKeyPath: "recordList")
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
br_setupUI()
|
||||||
|
|
||||||
|
requestHotSearch()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||||
|
if keyPath == "recordList" {
|
||||||
|
recordView.listArr = viewModel?.recordList ?? []
|
||||||
|
updateLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateLayout() {
|
||||||
|
stackView.br_removeAllArrangedSubview()
|
||||||
|
|
||||||
|
if self.viewModel?.recordList.isEmpty == false {
|
||||||
|
stackView.addArrangedSubview(recordView)
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.viewModel?.hotSearchList.isEmpty == false {
|
||||||
|
stackView.addArrangedSubview(hotView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BRSearchHomeView {
|
||||||
|
|
||||||
|
private func br_setupUI() {
|
||||||
|
addSubview(scrollView)
|
||||||
|
scrollView.addSubview(stackView)
|
||||||
|
|
||||||
|
scrollView.snp.makeConstraints { make in
|
||||||
|
make.left.right.top.equalToSuperview()
|
||||||
|
make.bottom.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
stackView.snp.makeConstraints { make in
|
||||||
|
make.left.top.equalToSuperview()
|
||||||
|
make.width.equalTo(UIScreen.width)
|
||||||
|
make.bottom.equalToSuperview().offset(-(UIScreen.tabbarSafeBottomMargin + 10))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BRSearchHomeView {
|
||||||
|
|
||||||
|
private func requestHotSearch() {
|
||||||
|
|
||||||
|
BRHomeAPI.requestHotSearchList { [weak self] list in
|
||||||
|
guard let self = self else { return }
|
||||||
|
guard let list = list else { return }
|
||||||
|
self.viewModel?.hotSearchList = list
|
||||||
|
self.hotView.listArr = list
|
||||||
|
self.updateLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
45
BeeReel/Class/Search/View/BRSearchRecordTagCell.swift
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// BRSearchRecordTagCell.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class BRSearchRecordTagCell: BRCollectionViewCell {
|
||||||
|
|
||||||
|
var text: String? {
|
||||||
|
didSet {
|
||||||
|
label.text = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var label: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontRegular(ofSize: 12)
|
||||||
|
label.textColor = .color1C1C1C()
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
contentView.layer.cornerRadius = 8
|
||||||
|
contentView.layer.masksToBounds = true
|
||||||
|
contentView.backgroundColor = .colorF2F2F2()
|
||||||
|
|
||||||
|
contentView.addSubview(label)
|
||||||
|
|
||||||
|
label.snp.makeConstraints { make in
|
||||||
|
make.centerY.equalToSuperview()
|
||||||
|
make.left.equalToSuperview().offset(10)
|
||||||
|
make.centerX.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
130
BeeReel/Class/Search/View/BRSearchRecordView.swift
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
//
|
||||||
|
// BRSearchRecordView.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class BRSearchRecordView: UIView {
|
||||||
|
|
||||||
|
var listArr: [String] = [] {
|
||||||
|
didSet {
|
||||||
|
collectionView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var clickCleanButton: (() -> Void)?
|
||||||
|
var searchText: ((_ text: String) -> Void)?
|
||||||
|
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontMedium(ofSize: 13)
|
||||||
|
label.textColor = .color777777()
|
||||||
|
label.text = "Last Searched".localized
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var collectionViewLayout: AlignedCollectionViewFlowLayout = {
|
||||||
|
let layout = AlignedCollectionViewFlowLayout(horizontalAlignment: .left, verticalAlignment: .top)
|
||||||
|
layout.estimatedItemSize = .init(width: 30, height: 28)
|
||||||
|
layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 0)
|
||||||
|
layout.minimumInteritemSpacing = 10
|
||||||
|
layout.minimumLineSpacing = 10
|
||||||
|
return layout
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var collectionView: BRCollectionView = {
|
||||||
|
let collectionView = BRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
||||||
|
collectionView.delegate = self
|
||||||
|
collectionView.dataSource = self
|
||||||
|
collectionView.addObserver(self, forKeyPath: "contentSize", context: nil)
|
||||||
|
collectionView.register(BRSearchRecordTagCell.self, forCellWithReuseIdentifier: "tagCell")
|
||||||
|
return collectionView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var deleteButton: UIButton = {
|
||||||
|
let button = UIButton(type: .custom)
|
||||||
|
button.setImage(UIImage(named: "delete_icon_01"), for: .normal)
|
||||||
|
button.setContentHuggingPriority(.required, for: .horizontal)
|
||||||
|
button.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||||
|
button.addTarget(self, action: #selector(handleDeleteButton), for: .touchUpInside)
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.collectionView.removeObserver(self, forKeyPath: "contentSize")
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
br_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func handleDeleteButton() {
|
||||||
|
self.clickCleanButton?()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||||
|
if keyPath == "contentSize" {
|
||||||
|
let height = self.collectionView.contentSize.height
|
||||||
|
self.collectionView.snp.updateConstraints { make in
|
||||||
|
make.height.equalTo(height + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BRSearchRecordView {
|
||||||
|
|
||||||
|
private func br_setupUI() {
|
||||||
|
addSubview(titleLabel)
|
||||||
|
addSubview(collectionView)
|
||||||
|
addSubview(deleteButton)
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(15)
|
||||||
|
make.top.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
collectionView.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview()
|
||||||
|
make.top.equalToSuperview().offset(28)
|
||||||
|
make.right.equalTo(deleteButton.snp.left).offset(-15)
|
||||||
|
make.height.equalTo(1)
|
||||||
|
make.bottom.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteButton.snp.makeConstraints { make in
|
||||||
|
make.right.equalToSuperview().offset(-15)
|
||||||
|
make.top.equalToSuperview().offset(33)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- UICollectionViewDelegate UICollectionViewDataSource --------------
|
||||||
|
extension BRSearchRecordView: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "tagCell", for: indexPath) as! BRSearchRecordTagCell
|
||||||
|
cell.text = listArr[indexPath.row]
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return listArr.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
self.searchText?(listArr[indexPath.row])
|
||||||
|
}
|
||||||
|
}
|
145
BeeReel/Class/Search/View/BRSearchResultCell.swift
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
//
|
||||||
|
// BRSearchResultCell.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class BRSearchResultCell: BRCollectionViewCell {
|
||||||
|
|
||||||
|
var highlightText: String?
|
||||||
|
|
||||||
|
var model: BRShortModel? {
|
||||||
|
didSet {
|
||||||
|
coverView.br_setImage(url: model?.image_url)
|
||||||
|
descLabel.text = model?.br_description
|
||||||
|
hotView.setNeedsUpdateConfiguration()
|
||||||
|
categoryLabel.text = model?.category?.first
|
||||||
|
|
||||||
|
|
||||||
|
let name = model?.name ?? ""
|
||||||
|
let ranges = name.br_range(of: highlightText ?? "")
|
||||||
|
let nameString = NSMutableAttributedString(string: name)
|
||||||
|
|
||||||
|
ranges.forEach {
|
||||||
|
nameString.yy_setColor(.colorFF7489(), range: $0)
|
||||||
|
}
|
||||||
|
|
||||||
|
nameLabel.attributedText = nameString
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var bgView: UIView = {
|
||||||
|
let view = UIImageView(image: UIImage(named: "Vector"))
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var coverView: BRImageView = {
|
||||||
|
let view = BRImageView()
|
||||||
|
view.layer.cornerRadius = 8
|
||||||
|
view.layer.masksToBounds = true
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var nameLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontMedium(ofSize: 14)
|
||||||
|
label.textColor = .color1C1C1C()
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var categoryLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontRegular(ofSize: 10)
|
||||||
|
label.textColor = .color899D00()
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var descLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontRegular(ofSize: 10)
|
||||||
|
label.textColor = .color777777()
|
||||||
|
label.numberOfLines = 2
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var hotView: UIButton = {
|
||||||
|
var config = UIButton.Configuration.plain()
|
||||||
|
config.image = UIImage(named: "hot_icon_02")
|
||||||
|
config.imagePadding = 2
|
||||||
|
config.contentInsets = .init(top: 0, leading: 0, bottom: 0, trailing: 0)
|
||||||
|
|
||||||
|
let button = UIButton(configuration: config)
|
||||||
|
button.configurationUpdateHandler = { [weak self] button in
|
||||||
|
guard let self = self else { return }
|
||||||
|
let count = model?.watch_total ?? 0
|
||||||
|
var str = "\(count)"
|
||||||
|
if count > 1000 {
|
||||||
|
let num = NSNumber(value: Float(count) / 1000)
|
||||||
|
str = num.br_toString(maximumFractionDigits: 1) + "k"
|
||||||
|
}
|
||||||
|
button.configuration?.attributedTitle = AttributedString.br_createAttributedString(string: str, 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 BRSearchResultCell {
|
||||||
|
|
||||||
|
private func br_setupUI() {
|
||||||
|
contentView.addSubview(bgView)
|
||||||
|
bgView.addSubview(coverView)
|
||||||
|
bgView.addSubview(nameLabel)
|
||||||
|
bgView.addSubview(categoryLabel)
|
||||||
|
bgView.addSubview(descLabel)
|
||||||
|
bgView.addSubview(hotView)
|
||||||
|
|
||||||
|
bgView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
coverView.snp.makeConstraints { make in
|
||||||
|
make.centerY.equalToSuperview()
|
||||||
|
make.left.equalToSuperview().offset(7)
|
||||||
|
make.width.equalTo(90)
|
||||||
|
make.height.equalTo(112)
|
||||||
|
}
|
||||||
|
|
||||||
|
nameLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalTo(coverView.snp.right).offset(20)
|
||||||
|
make.top.equalToSuperview().offset(16)
|
||||||
|
make.right.lessThanOrEqualToSuperview().offset(-10)
|
||||||
|
}
|
||||||
|
|
||||||
|
categoryLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalTo(nameLabel)
|
||||||
|
make.top.equalToSuperview().offset(41)
|
||||||
|
}
|
||||||
|
|
||||||
|
descLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalTo(nameLabel)
|
||||||
|
make.top.equalToSuperview().offset(55)
|
||||||
|
make.right.lessThanOrEqualToSuperview().offset(-10)
|
||||||
|
}
|
||||||
|
|
||||||
|
hotView.snp.makeConstraints { make in
|
||||||
|
make.left.equalTo(nameLabel)
|
||||||
|
make.bottom.equalToSuperview().offset(-16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
112
BeeReel/Class/Search/View/BRSearchResultView.swift
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
//
|
||||||
|
// BRSearchResultView.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class BRSearchResultView: UIView {
|
||||||
|
|
||||||
|
private var searchText: String = ""
|
||||||
|
|
||||||
|
private var listArr: [BRShortModel] = []
|
||||||
|
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.textColor = .color1C1C1C()
|
||||||
|
label.font = .fontMedium(ofSize: 15)
|
||||||
|
label.text = "Search Results".localized
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
||||||
|
let layout = UICollectionViewFlowLayout()
|
||||||
|
layout.itemSize = .init(width: UIScreen.width - 30, height: 126)
|
||||||
|
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.contentInset = .init(top: 0, left: 0, bottom: UIScreen.tabbarSafeBottomMargin + 10, right: 0)
|
||||||
|
collectionView.register(BRSearchResultCell.self, forCellWithReuseIdentifier: "cell")
|
||||||
|
return collectionView
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
br_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func search(text: String) {
|
||||||
|
self.searchText = text
|
||||||
|
BRAppTool.keyWindow?.endEditing(true)
|
||||||
|
|
||||||
|
if text.isEmpty {
|
||||||
|
self.listArr.removeAll()
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
} else {
|
||||||
|
requestSearch(text: text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BRSearchResultView {
|
||||||
|
|
||||||
|
private func br_setupUI() {
|
||||||
|
addSubview(titleLabel)
|
||||||
|
addSubview(collectionView)
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(15)
|
||||||
|
make.top.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
collectionView.snp.makeConstraints { make in
|
||||||
|
make.left.right.bottom.equalToSuperview()
|
||||||
|
make.top.equalTo(titleLabel.snp.bottom).offset(10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BRSearchResultView: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||||
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! BRSearchResultCell
|
||||||
|
cell.highlightText = self.searchText
|
||||||
|
cell.model = self.listArr[indexPath.row]
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
self.listArr.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension BRSearchResultView {
|
||||||
|
|
||||||
|
private func requestSearch(text: String) {
|
||||||
|
|
||||||
|
|
||||||
|
BRHomeAPI.requestSearch(text: searchText) { [weak self] list in
|
||||||
|
guard let self = self else { return }
|
||||||
|
guard text == self.searchText else { return }
|
||||||
|
|
||||||
|
self.listArr = list ?? []
|
||||||
|
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
76
BeeReel/Class/Search/View/BRSearchTextView.swift
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
//
|
||||||
|
// BRSearchTextView.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class BRSearchTextView: UIView {
|
||||||
|
|
||||||
|
override var intrinsicContentSize: CGSize {
|
||||||
|
return .init(width: UIScreen.width, height: 40)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var didSearch: ((_ text: String) -> Void)?
|
||||||
|
|
||||||
|
|
||||||
|
var text: String? {
|
||||||
|
get {
|
||||||
|
return textField.text
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
textField.text = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var textField: UITextField = {
|
||||||
|
let placeholder = NSMutableAttributedString(string: "Hot Picks Waiting for You".localized)
|
||||||
|
placeholder.yy_font = .fontRegular(ofSize: 14)
|
||||||
|
placeholder.yy_color = .colorD3D3D3()
|
||||||
|
|
||||||
|
let textField = UITextField()
|
||||||
|
textField.attributedPlaceholder = placeholder
|
||||||
|
textField.font = .fontRegular(ofSize: 14)
|
||||||
|
textField.textColor = .color1C1C1C()
|
||||||
|
textField.returnKeyType = .search
|
||||||
|
textField.delegate = self
|
||||||
|
return textField
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
self.layer.cornerRadius = 20
|
||||||
|
self.layer.masksToBounds = true
|
||||||
|
self.layer.borderColor = UIColor.color1C1C1C().cgColor
|
||||||
|
self.layer.borderWidth = 1
|
||||||
|
self.backgroundColor = .colorFFFFFF()
|
||||||
|
|
||||||
|
self.addSubview(textField)
|
||||||
|
|
||||||
|
textField.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(20)
|
||||||
|
make.right.equalToSuperview().offset(-20)
|
||||||
|
make.top.bottom.equalToSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- UITextFieldDelegate --------------
|
||||||
|
extension BRSearchTextView: UITextFieldDelegate {
|
||||||
|
|
||||||
|
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||||
|
textField.resignFirstResponder()
|
||||||
|
if let text = textField.text {
|
||||||
|
self.didSearch?(text)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
44
BeeReel/Class/Search/ViewModel/BRSearchViewModel.swift
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//
|
||||||
|
// BRSearchViewModel.swift
|
||||||
|
// BeeReel
|
||||||
|
//
|
||||||
|
// Created by 湖南秦九 on 2025/7/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class BRSearchViewModel: NSObject {
|
||||||
|
static let searchRecordUserDefaultKey = "BRSearchViewModel.searchRecordUserDefaultKey"
|
||||||
|
|
||||||
|
///历史记录
|
||||||
|
@objc dynamic private(set) var recordList: [String] = (UserDefaults.standard.object(forKey: BRSearchViewModel.searchRecordUserDefaultKey) as? [String]) ?? []
|
||||||
|
|
||||||
|
var hotSearchList: [BRShortModel] = []
|
||||||
|
|
||||||
|
func addSearchRecord(text: String) {
|
||||||
|
guard !text.isEmpty else { return }
|
||||||
|
var list = recordList
|
||||||
|
|
||||||
|
for (index, value) in list.enumerated() {
|
||||||
|
if value == text {
|
||||||
|
list.remove(at: index)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list.insert(text, at: 0)
|
||||||
|
|
||||||
|
if list.count > 10 {
|
||||||
|
list.removeLast()
|
||||||
|
}
|
||||||
|
recordList = list
|
||||||
|
|
||||||
|
UserDefaults.standard.set(list, forKey: BRSearchViewModel.searchRecordUserDefaultKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearSearchRecord() {
|
||||||
|
recordList.removeAll()
|
||||||
|
UserDefaults.standard.set(recordList, forKey: BRSearchViewModel.searchRecordUserDefaultKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -14,6 +14,8 @@ extension AppDelegate {
|
|||||||
|
|
||||||
//设置刷新控件的语言
|
//设置刷新控件的语言
|
||||||
MJRefreshConfig.default.languageCode = BRLocalizedManager.manager.mjLocalizedKey
|
MJRefreshConfig.default.languageCode = BRLocalizedManager.manager.mjLocalizedKey
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,70 +33,6 @@ class BRLoginManager {
|
|||||||
UserDefaults.br_setObject(token, forKey: kBRLoginTokenDefaultsKey)
|
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)?) {
|
func updateUserInfo(completer: (() -> Void)?) {
|
||||||
@ -119,8 +55,6 @@ class BRLoginManager {
|
|||||||
|
|
||||||
extension BRLoginManager {
|
extension BRLoginManager {
|
||||||
|
|
||||||
///登录状态发生变化
|
|
||||||
@objc static let loginStateDidChangeNotification = NSNotification.Name(rawValue: "BRLoginManager.loginStateDidChangeNotification")
|
|
||||||
///用户信息更新
|
///用户信息更新
|
||||||
@objc static let userInfoUpdateNotification = NSNotification.Name(rawValue: "BRLoginManager.userInfoUpdateNotification")
|
@objc static let userInfoUpdateNotification = NSNotification.Name(rawValue: "BRLoginManager.userInfoUpdateNotification")
|
||||||
|
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
22
BeeReel/Sources/Assets.xcassets/LaunchScreen/launch_screen_bg_image.imageset/Contents.json
vendored
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/LaunchScreen/launch_screen_bg_image.imageset/bg@2x.png
vendored
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
BeeReel/Sources/Assets.xcassets/LaunchScreen/launch_screen_bg_image.imageset/bg@3x.png
vendored
Normal file
After Width: | Height: | Size: 108 KiB |
22
BeeReel/Sources/Assets.xcassets/LaunchScreen/launch_screen_logo.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "logo@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "logo@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/LaunchScreen/launch_screen_logo.imageset/logo@2x.png
vendored
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
BeeReel/Sources/Assets.xcassets/LaunchScreen/launch_screen_logo.imageset/logo@3x.png
vendored
Normal file
After Width: | Height: | Size: 73 KiB |
22
BeeReel/Sources/Assets.xcassets/LaunchScreen/launch_screen_mark.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "装饰@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "装饰@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/LaunchScreen/launch_screen_mark.imageset/装饰@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
BeeReel/Sources/Assets.xcassets/LaunchScreen/launch_screen_mark.imageset/装饰@3x.png
vendored
Normal file
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 9.3 KiB |
22
BeeReel/Sources/Assets.xcassets/LaunchScreen/launch_screen_text.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Binge in Minutes, Love for Hours@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Binge in Minutes, Love for Hours@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
44
BeeReel/Sources/Assets.xcassets/icon/Vector.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Vector@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"resizing" : {
|
||||||
|
"cap-insets" : {
|
||||||
|
"left" : 265,
|
||||||
|
"right" : 23
|
||||||
|
},
|
||||||
|
"center" : {
|
||||||
|
"mode" : "tile",
|
||||||
|
"width" : 1
|
||||||
|
},
|
||||||
|
"mode" : "3-part-horizontal"
|
||||||
|
},
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Vector@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"resizing" : {
|
||||||
|
"cap-insets" : {
|
||||||
|
"left" : 398,
|
||||||
|
"right" : 34
|
||||||
|
},
|
||||||
|
"center" : {
|
||||||
|
"mode" : "tile",
|
||||||
|
"width" : 1
|
||||||
|
},
|
||||||
|
"mode" : "3-part-horizontal"
|
||||||
|
},
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/Vector.imageset/Vector@2x.png
vendored
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
BeeReel/Sources/Assets.xcassets/icon/Vector.imageset/Vector@3x.png
vendored
Normal file
After Width: | Height: | Size: 5.4 KiB |
22
BeeReel/Sources/Assets.xcassets/icon/delete_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Frame@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Frame@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/delete_icon_01.imageset/Frame@2x.png
vendored
Normal file
After Width: | Height: | Size: 625 B |
BIN
BeeReel/Sources/Assets.xcassets/icon/delete_icon_01.imageset/Frame@3x.png
vendored
Normal file
After Width: | Height: | Size: 894 B |
22
BeeReel/Sources/Assets.xcassets/icon/hot_icon_04.imageset/Contents.json
vendored
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/hot_icon_04.imageset/hot@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
BeeReel/Sources/Assets.xcassets/icon/hot_icon_04.imageset/hot@3x.png
vendored
Normal file
After Width: | Height: | Size: 2.9 KiB |
22
BeeReel/Sources/Assets.xcassets/icon/logo_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "logo@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "logo@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/logo_icon_01.imageset/logo@2x.png
vendored
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
BeeReel/Sources/Assets.xcassets/icon/logo_icon_01.imageset/logo@3x.png
vendored
Normal file
After Width: | Height: | Size: 73 KiB |
22
BeeReel/Sources/Assets.xcassets/icon/搜索bg.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "搜索bg@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "搜索bg.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
BeeReel/Sources/Assets.xcassets/icon/搜索bg.imageset/搜索bg.png
vendored
Normal file
After Width: | Height: | Size: 142 KiB |
BIN
BeeReel/Sources/Assets.xcassets/icon/搜索bg.imageset/搜索bg@2x.png
vendored
Normal file
After Width: | Height: | Size: 80 KiB |
@ -1,7 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||||
|
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
@ -11,10 +13,35 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="launch_screen_bg_image" translatesAutoresizingMaskIntoConstraints="NO" id="hT7-Oq-bJG">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="393" height="812"/>
|
||||||
|
</imageView>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="launch_screen_logo" translatesAutoresizingMaskIntoConstraints="NO" id="Z0n-Iq-hXz">
|
||||||
|
<rect key="frame" x="146.66666666666666" y="184" width="100" height="100"/>
|
||||||
|
</imageView>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="launch_screen_mark" translatesAutoresizingMaskIntoConstraints="NO" id="PKP-Wp-5ij">
|
||||||
|
<rect key="frame" x="90" y="296" width="17" height="20"/>
|
||||||
|
</imageView>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="launch_screen_text" translatesAutoresizingMaskIntoConstraints="NO" id="RC7-kh-wFK">
|
||||||
|
<rect key="frame" x="100.00000000000001" y="305" width="193.33333333333337" height="42.666666666666686"/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="hT7-Oq-bJG" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="01Q-Rg-CU2"/>
|
||||||
|
<constraint firstItem="Z0n-Iq-hXz" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="0gz-Qt-gtK"/>
|
||||||
|
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="hT7-Oq-bJG" secondAttribute="trailing" id="8hs-wA-AyO"/>
|
||||||
|
<constraint firstItem="RC7-kh-wFK" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="SKU-aQ-5GE"/>
|
||||||
|
<constraint firstItem="RC7-kh-wFK" firstAttribute="top" secondItem="Z0n-Iq-hXz" secondAttribute="bottom" constant="21" id="TMS-rr-qCL"/>
|
||||||
|
<constraint firstItem="hT7-Oq-bJG" firstAttribute="top" secondItem="Z0n-Iq-hXz" secondAttribute="top" constant="-184" id="iDf-05-NHI"/>
|
||||||
|
<constraint firstItem="RC7-kh-wFK" firstAttribute="leading" secondItem="PKP-Wp-5ij" secondAttribute="leading" constant="10" id="khV-ML-M0f"/>
|
||||||
|
<constraint firstItem="PKP-Wp-5ij" firstAttribute="top" secondItem="RC7-kh-wFK" secondAttribute="top" constant="-9" id="uz3-8A-aS2"/>
|
||||||
|
<constraint firstItem="hT7-Oq-bJG" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="yZa-qG-DSc"/>
|
||||||
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
@ -22,4 +49,10 @@
|
|||||||
<point key="canvasLocation" x="53" y="375"/>
|
<point key="canvasLocation" x="53" y="375"/>
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
|
<resources>
|
||||||
|
<image name="launch_screen_bg_image" width="375" height="812"/>
|
||||||
|
<image name="launch_screen_logo" width="100" height="100"/>
|
||||||
|
<image name="launch_screen_mark" width="17" height="20"/>
|
||||||
|
<image name="launch_screen_text" width="193.33332824707031" height="42.666667938232422"/>
|
||||||
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
@ -6,3 +6,4 @@
|
|||||||
#import <YYText/YYTextWeakProxy.h>
|
#import <YYText/YYTextWeakProxy.h>
|
||||||
#import <WMZPageController/WMZPageController.h>
|
#import <WMZPageController/WMZPageController.h>
|
||||||
#import "WMZBannerView.h"
|
#import "WMZBannerView.h"
|
||||||
|
#import <FDFullscreenPopGesture/UINavigationController+FDFullscreenPopGesture.h>
|
||||||
|
@ -111,6 +111,39 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Hot Picks Waiting for You" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Hot Picks Waiting for You"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Last Searched" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Last Searched"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Most Searched" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Most Searched"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"New Releases" : {
|
"New Releases" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -144,6 +177,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Search Results" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Search Results"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Speed" : {
|
"Speed" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -167,7 +211,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Third-party information sharing list" : {
|
"Third-party information sharing list" : {
|
||||||
"extractionState" : "manual"
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Third-party information sharing list"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Top 10" : {
|
"Top 10" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
|
432
BeeReel/Thirdparty/AlignedCollectionViewFlowLayout/AlignedCollectionViewFlowLayout.swift
vendored
Normal file
@ -0,0 +1,432 @@
|
|||||||
|
//
|
||||||
|
// AlignedCollectionViewFlowLayout.swift
|
||||||
|
//
|
||||||
|
// Created by Mischa Hildebrand on 12/04/2017.
|
||||||
|
// Copyright © 2017 Mischa Hildebrand.
|
||||||
|
//
|
||||||
|
// Licensed under the terms of the MIT license:
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
// MARK: - 🦆 Type definitions
|
||||||
|
|
||||||
|
/// An abstract protocol that defines an alignment.
|
||||||
|
protocol Alignment {}
|
||||||
|
|
||||||
|
/// Defines an alignment for UI elements.
|
||||||
|
public enum HorizontalAlignment: Alignment {
|
||||||
|
case left
|
||||||
|
case justified
|
||||||
|
case right
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines a vertical alignment for UI elements.
|
||||||
|
public enum VerticalAlignment: Alignment {
|
||||||
|
case top
|
||||||
|
case center
|
||||||
|
case bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes an axis with respect to which items can be aligned.
|
||||||
|
private struct AlignmentAxis<A: Alignment> {
|
||||||
|
|
||||||
|
/// Determines how items are aligned relative to the axis.
|
||||||
|
let alignment: A
|
||||||
|
|
||||||
|
/// Defines the position of the axis.
|
||||||
|
/// * If the `Alignment` is horizontal, the alignment axis is vertical and this is the position on the `x` axis.
|
||||||
|
/// * If the `Alignment` is vertical, the alignment axis is horizontal and this is the position on the `y` axis.
|
||||||
|
let position: CGFloat
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// A `UICollectionViewFlowLayout` subclass that gives you control
|
||||||
|
/// over the horizontal and vertical alignment of the cells.
|
||||||
|
/// You can use it to align the cells like words in a left- or right-aligned text
|
||||||
|
/// and you can specify how the cells are vertically aligned in their row.
|
||||||
|
open class AlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {
|
||||||
|
|
||||||
|
// MARK: - 🔶 Properties
|
||||||
|
|
||||||
|
/// Determines how the cells are horizontally aligned in a row.
|
||||||
|
/// - Note: The default is `.justified`.
|
||||||
|
public var horizontalAlignment: HorizontalAlignment = .justified
|
||||||
|
|
||||||
|
/// Determines how the cells are vertically aligned in a row.
|
||||||
|
/// - Note: The default is `.center`.
|
||||||
|
public var verticalAlignment: VerticalAlignment = .center
|
||||||
|
|
||||||
|
/// The vertical axis with respect to which the cells are horizontally aligned.
|
||||||
|
/// For a `justified` alignment the alignment axis is not defined and this value is `nil`.
|
||||||
|
fileprivate var alignmentAxis: AlignmentAxis<HorizontalAlignment>? {
|
||||||
|
switch horizontalAlignment {
|
||||||
|
case .left:
|
||||||
|
return AlignmentAxis(alignment: HorizontalAlignment.left, position: sectionInset.left)
|
||||||
|
case .right:
|
||||||
|
guard let collectionViewWidth = collectionView?.frame.size.width else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return AlignmentAxis(alignment: HorizontalAlignment.right, position: collectionViewWidth - sectionInset.right)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The width of the area inside the collection view that can be filled with cells.
|
||||||
|
private var contentWidth: CGFloat? {
|
||||||
|
guard let collectionViewWidth = collectionView?.frame.size.width else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return collectionViewWidth - sectionInset.left - sectionInset.right
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - 👶 Initialization
|
||||||
|
|
||||||
|
/// The designated initializer.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - horizontalAlignment: Specifies how the cells are horizontally aligned in a row. --
|
||||||
|
/// (Default: `.justified`)
|
||||||
|
/// - verticalAlignment: Specified how the cells are vertically aligned in a row. --
|
||||||
|
/// (Default: `.center`)
|
||||||
|
public init(horizontalAlignment: HorizontalAlignment = .justified, verticalAlignment: VerticalAlignment = .center) {
|
||||||
|
super.init()
|
||||||
|
self.horizontalAlignment = horizontalAlignment
|
||||||
|
self.verticalAlignment = verticalAlignment
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init?(coder aDecoder: NSCoder) {
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - 🅾️ Overrides
|
||||||
|
|
||||||
|
override open func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
|
||||||
|
|
||||||
|
// 💡 IDEA:
|
||||||
|
// The approach for computing a cell's frame is to create a rectangle that covers the current line.
|
||||||
|
// Then we check if the preceding cell's frame intersects with this rectangle.
|
||||||
|
// If it does, the current item is not the first item in the line. Otherwise it is.
|
||||||
|
// (Vice-versa for right-aligned cells.)
|
||||||
|
//
|
||||||
|
// +---------+----------------------------------------------------------------+---------+
|
||||||
|
// | | | |
|
||||||
|
// | | +------------+ | |
|
||||||
|
// | | | | | |
|
||||||
|
// | section |- - -|- - - - - - |- - - - +---------------------+ - - - - - - -| section |
|
||||||
|
// | inset | |intersection| | | line rect | inset |
|
||||||
|
// | |- - -|- - - - - - |- - - - +---------------------+ - - - - - - -| |
|
||||||
|
// | (left) | | | current item | (right) |
|
||||||
|
// | | +------------+ | |
|
||||||
|
// | | previous item | |
|
||||||
|
// +---------+----------------------------------------------------------------+---------+
|
||||||
|
//
|
||||||
|
// ℹ️ We need this rather complicated approach because the first item in a line
|
||||||
|
// is not always left-aligned and the last item in a line is not always right-aligned:
|
||||||
|
// If there is only one item in a line UICollectionViewFlowLayout will center it.
|
||||||
|
|
||||||
|
// We may not change the original layout attributes or UICollectionViewFlowLayout might complain.
|
||||||
|
guard let layoutAttributes = super.layoutAttributesForItem(at: indexPath)?.copy() as? UICollectionViewLayoutAttributes else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// For a justified layout there's nothing to do here
|
||||||
|
// as UICollectionViewFlowLayout justifies the items in a line by default.
|
||||||
|
if horizontalAlignment != .justified {
|
||||||
|
layoutAttributes.alignHorizontally(collectionViewLayout: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For a vertically centered layout there's nothing to do here
|
||||||
|
// as UICollectionViewFlowLayout center-aligns the items in a line by default.
|
||||||
|
if verticalAlignment != .center {
|
||||||
|
layoutAttributes.alignVertically(collectionViewLayout: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
return layoutAttributes
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
|
||||||
|
// We may not change the original layout attributes or UICollectionViewFlowLayout might complain.
|
||||||
|
let layoutAttributesObjects = copy(super.layoutAttributesForElements(in: rect))
|
||||||
|
layoutAttributesObjects?.forEach({ (layoutAttributes) in
|
||||||
|
setFrame(forLayoutAttributes: layoutAttributes)
|
||||||
|
})
|
||||||
|
return layoutAttributesObjects
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - 👷 Private layout helpers
|
||||||
|
|
||||||
|
/// Sets the frame for the passed layout attributes object by calling the `layoutAttributesForItem(at:)` function.
|
||||||
|
private func setFrame(forLayoutAttributes layoutAttributes: UICollectionViewLayoutAttributes) {
|
||||||
|
if layoutAttributes.representedElementCategory == .cell { // Do not modify header views etc.
|
||||||
|
let indexPath = layoutAttributes.indexPath
|
||||||
|
if let newFrame = layoutAttributesForItem(at: indexPath)?.frame {
|
||||||
|
layoutAttributes.frame = newFrame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A function to access the `super` implementation of `layoutAttributesForItem(at:)` externally.
|
||||||
|
///
|
||||||
|
/// - Parameter indexPath: The index path of the item for which to return the layout attributes.
|
||||||
|
/// - Returns: The unmodified layout attributes for the item at the specified index path
|
||||||
|
/// as computed by `UICollectionViewFlowLayout`.
|
||||||
|
fileprivate func originalLayoutAttribute(forItemAt indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
|
||||||
|
return super.layoutAttributesForItem(at: indexPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if the `firstItemAttributes`' frame is in the same line
|
||||||
|
/// as the `secondItemAttributes`' frame.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - firstItemAttributes: The first layout attributes object to be compared.
|
||||||
|
/// - secondItemAttributes: The second layout attributes object to be compared.
|
||||||
|
/// - Returns: `true` if the frames of the two layout attributes are in the same line, else `false`.
|
||||||
|
/// `false` is also returned when the layout's `collectionView` property is `nil`.
|
||||||
|
fileprivate func isFrame(for firstItemAttributes: UICollectionViewLayoutAttributes, inSameLineAsFrameFor secondItemAttributes: UICollectionViewLayoutAttributes) -> Bool {
|
||||||
|
guard let lineWidth = contentWidth else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let firstItemFrame = firstItemAttributes.frame
|
||||||
|
let lineFrame = CGRect(x: sectionInset.left,
|
||||||
|
y: firstItemFrame.origin.y,
|
||||||
|
width: lineWidth,
|
||||||
|
height: firstItemFrame.size.height)
|
||||||
|
return lineFrame.intersects(secondItemAttributes.frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines the layout attributes objects for all items displayed in the same line as the item
|
||||||
|
/// represented by the passed `layoutAttributes` object.
|
||||||
|
///
|
||||||
|
/// - Parameter layoutAttributes: The layout attributed that represents the reference item.
|
||||||
|
/// - Returns: The layout attributes objects representing all other items in the same line.
|
||||||
|
/// The passed `layoutAttributes` object itself is always contained in the returned array.
|
||||||
|
fileprivate func layoutAttributes(forItemsInLineWith layoutAttributes: UICollectionViewLayoutAttributes) -> [UICollectionViewLayoutAttributes] {
|
||||||
|
guard let lineWidth = contentWidth else {
|
||||||
|
return [layoutAttributes]
|
||||||
|
}
|
||||||
|
var lineFrame = layoutAttributes.frame
|
||||||
|
lineFrame.origin.x = sectionInset.left
|
||||||
|
lineFrame.size.width = lineWidth
|
||||||
|
return super.layoutAttributesForElements(in: lineFrame) ?? []
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copmutes the alignment axis with which to align the items represented by the `layoutAttributes` objects vertically.
|
||||||
|
///
|
||||||
|
/// - Parameter layoutAttributes: The layout attributes objects to be vertically aligned.
|
||||||
|
/// - Returns: The axis with respect to which the layout attributes can be aligned
|
||||||
|
/// or `nil` if the `layoutAttributes` array is empty.
|
||||||
|
private func verticalAlignmentAxisForLine(with layoutAttributes: [UICollectionViewLayoutAttributes]) -> AlignmentAxis<VerticalAlignment>? {
|
||||||
|
|
||||||
|
guard let firstAttribute = layoutAttributes.first else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch verticalAlignment {
|
||||||
|
case .top:
|
||||||
|
let minY = layoutAttributes.reduce(CGFloat.greatestFiniteMagnitude) { min($0, $1.frame.minY) }
|
||||||
|
return AlignmentAxis(alignment: .top, position: minY)
|
||||||
|
|
||||||
|
case .bottom:
|
||||||
|
let maxY = layoutAttributes.reduce(0) { max($0, $1.frame.maxY) }
|
||||||
|
return AlignmentAxis(alignment: .bottom, position: maxY)
|
||||||
|
|
||||||
|
default:
|
||||||
|
let centerY = firstAttribute.center.y
|
||||||
|
return AlignmentAxis(alignment: .center, position: centerY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the axis with which to align the item represented by the `currentLayoutAttributes` vertically.
|
||||||
|
///
|
||||||
|
/// - Parameter currentLayoutAttributes: The layout attributes representing the item to be vertically aligned.
|
||||||
|
/// - Returns: The axis with respect to which the item can be aligned.
|
||||||
|
fileprivate func verticalAlignmentAxis(for currentLayoutAttributes: UICollectionViewLayoutAttributes) -> AlignmentAxis<VerticalAlignment>? {
|
||||||
|
let layoutAttributesInLine = layoutAttributes(forItemsInLineWith: currentLayoutAttributes)
|
||||||
|
// It's okay to force-unwrap here because we pass a non-empty array.
|
||||||
|
return verticalAlignmentAxisForLine(with: layoutAttributesInLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a deep copy of the passed array by copying all its items.
|
||||||
|
///
|
||||||
|
/// - Parameter layoutAttributesArray: The array to be copied.
|
||||||
|
/// - Returns: A deep copy of the passed array.
|
||||||
|
private func copy(_ layoutAttributesArray: [UICollectionViewLayoutAttributes]?) -> [UICollectionViewLayoutAttributes]? {
|
||||||
|
return layoutAttributesArray?.map{ $0.copy() } as? [UICollectionViewLayoutAttributes]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - 👷 Layout attributes helpers
|
||||||
|
|
||||||
|
fileprivate extension UICollectionViewLayoutAttributes {
|
||||||
|
|
||||||
|
private var currentSection: Int {
|
||||||
|
return indexPath.section
|
||||||
|
}
|
||||||
|
|
||||||
|
private var currentItem: Int {
|
||||||
|
return indexPath.item
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The index path for the item preceding the item represented by this layout attributes object.
|
||||||
|
private var precedingIndexPath: IndexPath {
|
||||||
|
return IndexPath(item: currentItem - 1, section: currentSection)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The index path for the item following the item represented by this layout attributes object.
|
||||||
|
private var followingIndexPath: IndexPath {
|
||||||
|
return IndexPath(item: currentItem + 1, section: currentSection)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the item represetend by this layout attributes object is the first item in the line.
|
||||||
|
///
|
||||||
|
/// - Parameter collectionViewLayout: The layout for which to perform the check.
|
||||||
|
/// - Returns: `true` if the represented item is the first item in the line, else `false`.
|
||||||
|
func isRepresentingFirstItemInLine(collectionViewLayout: AlignedCollectionViewFlowLayout) -> Bool {
|
||||||
|
if currentItem <= 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if let layoutAttributesForPrecedingItem = collectionViewLayout.originalLayoutAttribute(forItemAt: precedingIndexPath) {
|
||||||
|
return !collectionViewLayout.isFrame(for: self, inSameLineAsFrameFor: layoutAttributesForPrecedingItem)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the item represetend by this layout attributes object is the last item in the line.
|
||||||
|
///
|
||||||
|
/// - Parameter collectionViewLayout: The layout for which to perform the check.
|
||||||
|
/// - Returns: `true` if the represented item is the last item in the line, else `false`.
|
||||||
|
func isRepresentingLastItemInLine(collectionViewLayout: AlignedCollectionViewFlowLayout) -> Bool {
|
||||||
|
guard let itemCount = collectionViewLayout.collectionView?.numberOfItems(inSection: currentSection) else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentItem >= itemCount - 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if let layoutAttributesForFollowingItem = collectionViewLayout.originalLayoutAttribute(forItemAt: followingIndexPath) {
|
||||||
|
return !collectionViewLayout.isFrame(for: self, inSameLineAsFrameFor: layoutAttributesForFollowingItem)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves the layout attributes object's frame so that it is aligned horizontally with the alignment axis.
|
||||||
|
func align(toAlignmentAxis alignmentAxis: AlignmentAxis<HorizontalAlignment>) {
|
||||||
|
switch alignmentAxis.alignment {
|
||||||
|
case .left:
|
||||||
|
frame.origin.x = alignmentAxis.position
|
||||||
|
case .right:
|
||||||
|
frame.origin.x = alignmentAxis.position - frame.size.width
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves the layout attributes object's frame so that it is aligned vertically with the alignment axis.
|
||||||
|
func align(toAlignmentAxis alignmentAxis: AlignmentAxis<VerticalAlignment>) {
|
||||||
|
switch alignmentAxis.alignment {
|
||||||
|
case .top:
|
||||||
|
frame.origin.y = alignmentAxis.position
|
||||||
|
case .bottom:
|
||||||
|
frame.origin.y = alignmentAxis.position - frame.size.height
|
||||||
|
default:
|
||||||
|
center.y = alignmentAxis.position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Positions the frame right of the preceding item's frame, leaving a spacing between the frames
|
||||||
|
/// as defined by the collection view layout's `minimumInteritemSpacing`.
|
||||||
|
///
|
||||||
|
/// - Parameter collectionViewLayout: The layout on which to perfom the calculations.
|
||||||
|
private func alignToPrecedingItem(collectionViewLayout: AlignedCollectionViewFlowLayout) {
|
||||||
|
let itemSpacing = collectionViewLayout.minimumInteritemSpacing
|
||||||
|
|
||||||
|
if let precedingItemAttributes = collectionViewLayout.layoutAttributesForItem(at: precedingIndexPath) {
|
||||||
|
frame.origin.x = precedingItemAttributes.frame.maxX + itemSpacing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Positions the frame left of the following item's frame, leaving a spacing between the frames
|
||||||
|
/// as defined by the collection view layout's `minimumInteritemSpacing`.
|
||||||
|
///
|
||||||
|
/// - Parameter collectionViewLayout: The layout on which to perfom the calculations.
|
||||||
|
private func alignToFollowingItem(collectionViewLayout: AlignedCollectionViewFlowLayout) {
|
||||||
|
let itemSpacing = collectionViewLayout.minimumInteritemSpacing
|
||||||
|
|
||||||
|
if let followingItemAttributes = collectionViewLayout.layoutAttributesForItem(at: followingIndexPath) {
|
||||||
|
frame.origin.x = followingItemAttributes.frame.minX - itemSpacing - frame.size.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Aligns the frame horizontally as specified by the collection view layout's `horizontalAlignment`.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - collectionViewLayout: The layout providing the alignment information.
|
||||||
|
func alignHorizontally(collectionViewLayout: AlignedCollectionViewFlowLayout) {
|
||||||
|
|
||||||
|
guard let alignmentAxis = collectionViewLayout.alignmentAxis else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch collectionViewLayout.horizontalAlignment {
|
||||||
|
|
||||||
|
case .left:
|
||||||
|
if isRepresentingFirstItemInLine(collectionViewLayout: collectionViewLayout) {
|
||||||
|
align(toAlignmentAxis: alignmentAxis)
|
||||||
|
} else {
|
||||||
|
alignToPrecedingItem(collectionViewLayout: collectionViewLayout)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .right:
|
||||||
|
if isRepresentingLastItemInLine(collectionViewLayout: collectionViewLayout) {
|
||||||
|
align(toAlignmentAxis: alignmentAxis)
|
||||||
|
} else {
|
||||||
|
alignToFollowingItem(collectionViewLayout: collectionViewLayout)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Aligns the frame vertically as specified by the collection view layout's `verticalAlignment`.
|
||||||
|
///
|
||||||
|
/// - Parameter collectionViewLayout: The layout providing the alignment information.
|
||||||
|
func alignVertically(collectionViewLayout: AlignedCollectionViewFlowLayout) {
|
||||||
|
guard let alignmentAxis = collectionViewLayout.verticalAlignmentAxis(for: self) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
align(toAlignmentAxis: alignmentAxis)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
2
BeeReel/Thirdparty/JXUUID/JXUUID.h
vendored
@ -15,6 +15,6 @@
|
|||||||
/**
|
/**
|
||||||
重新安装app后,会发生变化
|
重新安装app后,会发生变化
|
||||||
*/
|
*/
|
||||||
+ (nonnull NSString *)systemUUID;
|
+ (nonnull NSString *)idfv;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
11
BeeReel/Thirdparty/JXUUID/JXUUID.m
vendored
@ -23,7 +23,7 @@ static NSString *const uuidKey = @"com.JXUUID";
|
|||||||
uuid = [PDKeyChain objectForKey:uuidKey];
|
uuid = [PDKeyChain objectForKey:uuidKey];
|
||||||
if (uuid && uuid.length > 0) {
|
if (uuid && uuid.length > 0) {
|
||||||
} else {
|
} else {
|
||||||
uuid = [[NSUUID UUID] UUIDString];
|
uuid = [self idfv];
|
||||||
[PDKeyChain setObject:uuid forKey:uuidKey];
|
[PDKeyChain setObject:uuid forKey:uuidKey];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -31,15 +31,10 @@ static NSString *const uuidKey = @"com.JXUUID";
|
|||||||
}
|
}
|
||||||
+ (nonnull NSString *)idfa
|
+ (nonnull NSString *)idfa
|
||||||
{
|
{
|
||||||
static NSString *idfa;
|
return [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
|
||||||
static dispatch_once_t onceToken;
|
|
||||||
dispatch_once(&onceToken, ^{
|
|
||||||
idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
|
|
||||||
});
|
|
||||||
return idfa;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (nonnull NSString *)systemUUID
|
+ (nonnull NSString *)idfv
|
||||||
{
|
{
|
||||||
return [UIDevice currentDevice].identifierForVendor.UUIDString;
|
return [UIDevice currentDevice].identifierForVendor.UUIDString;
|
||||||
}
|
}
|
||||||
|
2
Podfile
@ -21,7 +21,6 @@ target 'BeeReel' do
|
|||||||
pod 'Kingfisher' #图片加载
|
pod 'Kingfisher' #图片加载
|
||||||
pod 'SnapKit' #布局
|
pod 'SnapKit' #布局
|
||||||
pod 'Toast' #吐司提示
|
pod 'Toast' #吐司提示
|
||||||
# pod 'YYKit' #工具类
|
|
||||||
pod 'SJVideoPlayer' #播放器
|
pod 'SJVideoPlayer' #播放器
|
||||||
pod 'SJMediaCacheServer' #播放器缓存
|
pod 'SJMediaCacheServer' #播放器缓存
|
||||||
pod 'WMZPageController' #分页控制器
|
pod 'WMZPageController' #分页控制器
|
||||||
@ -30,5 +29,6 @@ target 'BeeReel' do
|
|||||||
pod 'FSPagerView' #banner
|
pod 'FSPagerView' #banner
|
||||||
pod 'MJRefresh' #刷新控件
|
pod 'MJRefresh' #刷新控件
|
||||||
pod 'HWPanModal' #底部弹出控制器
|
pod 'HWPanModal' #底部弹出控制器
|
||||||
|
pod 'FDFullscreenPopGesture' #全屏手势返回
|
||||||
|
|
||||||
end
|
end
|
||||||
|