diff --git a/.gitignore b/.gitignore index 3542e5b..0ced315 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ xcuserdata/ *.dSYM.zip *.dSYM + ## Playgrounds timeline.xctimeline playground.xcworkspace @@ -38,7 +39,8 @@ playground.xcworkspace # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # -# Pods/ +Pods/ +Podfile.lock # # Add this line if you want to avoid checking in source code from the Xcode workspace # *.xcworkspace diff --git a/Podfile b/Podfile new file mode 100644 index 0000000..307f827 --- /dev/null +++ b/Podfile @@ -0,0 +1,35 @@ +# Uncomment the next line to define a global platform for your project + platform :ios, '13.0' + +source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git' + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['ENABLE_BITCODE'] = 'NO' + config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" + config.build_settings['EXCLUDED_ARCHITECTURES'] = 'i386' + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' + end + end +end + +target 'SynthReel' do + use_frameworks! + + pod 'YYCategories' + pod 'YYText' + pod 'Kingfisher' + pod 'SmartCodable' + pod 'Moya' + pod 'SVProgressHUD' + pod 'Toast' + pod 'JXSegmentedView' + pod 'JXPagingView/Paging' + pod 'FSPagerView' + pod 'JXPlayer', '~> 0.1.8' + pod 'MJRefresh' + pod 'collection-view-layouts/TagsLayout' + pod 'HWPanModal' + +end diff --git a/SynthReel.xcodeproj/project.pbxproj b/SynthReel.xcodeproj/project.pbxproj new file mode 100644 index 0000000..57e8af3 --- /dev/null +++ b/SynthReel.xcodeproj/project.pbxproj @@ -0,0 +1,1091 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 03980F4F2ECEB91C0006E317 /* SRRecommendPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03980F4E2ECEB91C0006E317 /* SRRecommendPlayerViewModel.swift */; }; + 03980F512ECEBEE20006E317 /* SRRecommendPlayerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03980F502ECEBEE20006E317 /* SRRecommendPlayerCell.swift */; }; + 03980F532ECEDEAB0006E317 /* SRRecommendPlayerControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03980F522ECEDEAB0006E317 /* SRRecommendPlayerControlView.swift */; }; + 03980F592ECEED190006E317 /* SRMyShortViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03980F582ECEED190006E317 /* SRMyShortViewController.swift */; }; + 03B1A8392EC5C8D6006C353F /* SRHud.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8382EC5C8CE006C353F /* SRHud.swift */; }; + 03B1A83B2EC5C8E0006C353F /* SRToast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A83A2EC5C8DB006C353F /* SRToast.swift */; }; + 03B1A83E2EC5C91E006C353F /* SRTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A83D2EC5C91A006C353F /* SRTool.swift */; }; + 03B1A8402EC5CA37006C353F /* AppDelegate+Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A83F2EC5CA2E006C353F /* AppDelegate+Config.swift */; }; + 03B1A8432EC5CB99006C353F /* SRTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8422EC5CB99006C353F /* SRTabBarController.swift */; }; + 03B1A8452EC5CBBB006C353F /* SRNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8442EC5CBBB006C353F /* SRNavigationController.swift */; }; + 03B1A8472EC5CBCF006C353F /* SRViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8462EC5CBCF006C353F /* SRViewController.swift */; }; + 03B1A84A2EC5CE37006C353F /* ESTabBarController in Frameworks */ = {isa = PBXBuildFile; productRef = 03B1A8492EC5CE37006C353F /* ESTabBarController */; }; + 03B1A84D2EC5DA43006C353F /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 03B1A84C2EC5DA43006C353F /* SnapKit */; }; + 03B1A8502EC5DB2E006C353F /* SRHomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A84F2EC5DB2E006C353F /* SRHomeViewController.swift */; }; + 03B1A8532EC5E12E006C353F /* UIScreen+SRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8522EC5E129006C353F /* UIScreen+SRAdd.swift */; }; + 03B1A8552EC5E434006C353F /* UIFont+SRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8542EC5E429006C353F /* UIFont+SRAdd.swift */; }; + 03B1A8582EC5E4F1006C353F /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 03B1A8562EC5E4F1006C353F /* Localizable.strings */; }; + 03B1A8D52EC6CF37006C353F /* SRHomeChildViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8D42EC6CF37006C353F /* SRHomeChildViewController.swift */; }; + 03B1A8D82EC6D051006C353F /* SRCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8D72EC6D051006C353F /* SRCollectionView.swift */; }; + 03B1A8DC2EC6D0EB006C353F /* SRHomeChildCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8DA2EC6D0EB006C353F /* SRHomeChildCell.swift */; }; + 03B1A8DD2EC6D0EB006C353F /* SRHomeChildCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 03B1A8DB2EC6D0EB006C353F /* SRHomeChildCell.xib */; }; + 03B1A8E12EC6D6D3006C353F /* SRHomeMenuDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8E02EC6D6D3006C353F /* SRHomeMenuDataSource.swift */; }; + 03B1A8E32EC6F577006C353F /* SRHomeMenuCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8E22EC6F577006C353F /* SRHomeMenuCell.swift */; }; + 03B1A8E52EC715E1006C353F /* SRHomeApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8E42EC715DA006C353F /* SRHomeApi.swift */; }; + 03B1A8E72EC7175D006C353F /* SRCategoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8E62EC7175D006C353F /* SRCategoryModel.swift */; }; + 03B1A8E92EC721CD006C353F /* SRLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8E82EC721CD006C353F /* SRLabel.swift */; }; + 03B1A8ED2EC72C1F006C353F /* SRHomeModuleItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8EC2EC72C18006C353F /* SRHomeModuleItem.swift */; }; + 03B1A8EF2EC72C78006C353F /* SRShortModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8EE2EC72C78006C353F /* SRShortModel.swift */; }; + 03B1A8F12EC72DD7006C353F /* SRHomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8F02EC72DD7006C353F /* SRHomeViewModel.swift */; }; + 03B1A8F32EC809C5006C353F /* SRHomeHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8F22EC809C5006C353F /* SRHomeHeaderView.swift */; }; + 03B1A8F52EC81277006C353F /* SRHomeBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8F42EC81277006C353F /* SRHomeBannerView.swift */; }; + 03B1A8F92EC813BC006C353F /* SRScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8F82EC813BC006C353F /* SRScrollView.swift */; }; + 03B1A8FB2EC818BE006C353F /* UIStackView+SRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8FA2EC818B6006C353F /* UIStackView+SRAdd.swift */; }; + 03B1A8FD2EC81C62006C353F /* SRHomeBannerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8FC2EC81C62006C353F /* SRHomeBannerCell.swift */; }; + 03B1A8FF2EC81C92006C353F /* SRImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A8FE2EC81C92006C353F /* SRImageView.swift */; }; + 03B1A9012EC852B2006C353F /* SRHomeModuleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9002EC852B2006C353F /* SRHomeModuleView.swift */; }; + 03B1A9032EC8555B006C353F /* SRHomeYouLikeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9022EC8555B006C353F /* SRHomeYouLikeView.swift */; }; + 03B1A9052EC857B3006C353F /* SRHomeYouLikeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9042EC857B3006C353F /* SRHomeYouLikeCell.swift */; }; + 03B1A9072EC86656006C353F /* SRGradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9062EC86656006C353F /* SRGradientView.swift */; }; + 03B1A9092ECAAF55006C353F /* SRHomeTopChartsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9082ECAAF55006C353F /* SRHomeTopChartsView.swift */; }; + 03B1A90B2ECAB2EA006C353F /* SRHomeTopChartsContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A90A2ECAB2EA006C353F /* SRHomeTopChartsContentView.swift */; }; + 03B1A90D2ECAC51A006C353F /* NSNumber+SRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A90C2ECAC512006C353F /* NSNumber+SRAdd.swift */; }; + 03B1A90F2ECAC768006C353F /* SRHomeBingeWorthyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A90E2ECAC768006C353F /* SRHomeBingeWorthyView.swift */; }; + 03B1A9112ECAC927006C353F /* SRHomeBingeWorthyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9102ECAC927006C353F /* SRHomeBingeWorthyCell.swift */; }; + 03B1A9132ECAED04006C353F /* SRHomeViralHitsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9122ECAED04006C353F /* SRHomeViralHitsView.swift */; }; + 03B1A9152ECAEE63006C353F /* SRHomeViralHitsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9142ECAEE63006C353F /* SRHomeViralHitsCell.swift */; }; + 03B1A9172ECAF14F006C353F /* SRHomeHotView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9162ECAF14F006C353F /* SRHomeHotView.swift */; }; + 03B1A9192ECAF2E6006C353F /* SRHomePremiereNowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9182ECAF2E6006C353F /* SRHomePremiereNowView.swift */; }; + 03B1A91B2ECAFFD6006C353F /* UIView+SRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A91A2ECAFFCB006C353F /* UIView+SRAdd.swift */; }; + 03B1A91D2ECB2424006C353F /* UIScrollView+SRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A91C2ECB241B006C353F /* UIScrollView+SRAdd.swift */; }; + 03B1A91F2ECB2A0E006C353F /* SRHomeBannerMiniCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A91E2ECB2A0E006C353F /* SRHomeBannerMiniCell.swift */; }; + 03B1A9222ECB456C006C353F /* SRDetailPlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9212ECB456C006C353F /* SRDetailPlayerViewController.swift */; }; + 03B1A9262ECBFF31006C353F /* SRShortPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9252ECBFF31006C353F /* SRShortPlayerViewModel.swift */; }; + 03B1A9282ECC05B1006C353F /* SRShortDetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9272ECC05B1006C353F /* SRShortDetailModel.swift */; }; + 03B1A92A2ECC0738006C353F /* SRShortApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9292ECC072C006C353F /* SRShortApi.swift */; }; + 03B1A92C2ECC0A7A006C353F /* SRShortDetailPlayerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A92B2ECC0A7A006C353F /* SRShortDetailPlayerCell.swift */; }; + 03B1A92E2ECC0D7E006C353F /* SRShortDetailControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A92D2ECC0D7E006C353F /* SRShortDetailControlView.swift */; }; + 03B1A9302ECC10D1006C353F /* SRSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A92F2ECC10D1006C353F /* SRSearchViewController.swift */; }; + 03B1A9322ECC1167006C353F /* SRSearchHomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9312ECC1167006C353F /* SRSearchHomeView.swift */; }; + 03B1A9342ECC12D9006C353F /* SRSearchTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9332ECC12D9006C353F /* SRSearchTextView.swift */; }; + 03B1A9362ECC1D1D006C353F /* SRSearchRecordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9352ECC1D1D006C353F /* SRSearchRecordView.swift */; }; + 03B1A9382ECC210D006C353F /* SRSearchRecordCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9372ECC210D006C353F /* SRSearchRecordCell.swift */; }; + 03B1A93A2ECC3F54006C353F /* SRSearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9392ECC3F54006C353F /* SRSearchViewModel.swift */; }; + 03B1A93C2ECC406E006C353F /* SRHotSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A93B2ECC406E006C353F /* SRHotSearchView.swift */; }; + 03B1A93E2ECC4568006C353F /* SRTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A93D2ECC4568006C353F /* SRTableView.swift */; }; + 03B1A9402ECC45BA006C353F /* SRTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A93F2ECC45BA006C353F /* SRTableViewCell.swift */; }; + 03B1A9422ECC4632006C353F /* SRHotSearchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9412ECC4632006C353F /* SRHotSearchCell.swift */; }; + 03B1A9442ECC4ED9006C353F /* SRSearchResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9432ECC4ED9006C353F /* SRSearchResultView.swift */; }; + 03B1A9462ECC5679006C353F /* SRSearchResultCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9452ECC5679006C353F /* SRSearchResultCell.swift */; }; + 03B1A9482ECC6669006C353F /* SRProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9472ECC6669006C353F /* SRProgressView.swift */; }; + 03B1A94A2ECC79AB006C353F /* SREpSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A9492ECC79AB006C353F /* SREpSelectorView.swift */; }; + 03B1A94C2ECC7A2D006C353F /* SRPanModalContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A94B2ECC7A2D006C353F /* SRPanModalContentView.swift */; }; + 03B1A94E2ECD604B006C353F /* SREpSelectorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A94D2ECD604B006C353F /* SREpSelectorCell.swift */; }; + 03B1A9502ECEB4E6006C353F /* SRRecommendPlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B1A94F2ECEB4E6006C353F /* SRRecommendPlayerViewController.swift */; }; + 03E9A7C92EC47177000D1067 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7BF2EC47177000D1067 /* AppDelegate.swift */; }; + 03E9A7CA2EC47177000D1067 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7C62EC47177000D1067 /* SceneDelegate.swift */; }; + 03E9A7CC2EC47177000D1067 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 03E9A7C02EC47177000D1067 /* Assets.xcassets */; }; + 03E9A7CE2EC47177000D1067 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 03E9A7C32EC47177000D1067 /* LaunchScreen.storyboard */; }; + 03E9A7D72EC47A23000D1067 /* SRResponseCryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7D62EC479FE000D1067 /* SRResponseCryptor.swift */; }; + 03E9A7D92EC47B90000D1067 /* SRNetworkReachableManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7D82EC47B90000D1067 /* SRNetworkReachableManager.swift */; }; + 03E9A7DB2EC485BE000D1067 /* SRNetwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7DA2EC485B3000D1067 /* SRNetwork.swift */; }; + 03E9A7DD2EC485E1000D1067 /* SRNetworkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7DC2EC485DD000D1067 /* SRNetworkModel.swift */; }; + 03E9A7E22EC494F9000D1067 /* SRTargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7E12EC494F9000D1067 /* SRTargetType.swift */; }; + 03E9A7E42EC49593000D1067 /* SRUrlPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7E32EC49584000D1067 /* SRUrlPath.swift */; }; + 03E9A7E72EC49819000D1067 /* SRDefine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7E62EC49813000D1067 /* SRDefine.swift */; }; + 03E9A7EA2EC4995D000D1067 /* SRKeychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7E92EC4995D000D1067 /* SRKeychain.swift */; }; + 03E9A7EC2EC499A9000D1067 /* SRDeviceId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7EB2EC499A9000D1067 /* SRDeviceId.swift */; }; + 03E9A7EF2EC4A8AF000D1067 /* SRAccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7EE2EC4A8AF000D1067 /* SRAccountManager.swift */; }; + 03E9A7F22EC4A8F6000D1067 /* UserDefaults+SRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7F12EC4A8EC000D1067 /* UserDefaults+SRAdd.swift */; }; + 03E9A7F42EC4A94D000D1067 /* SRAccountToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7F32EC4A94D000D1067 /* SRAccountToken.swift */; }; + 03E9A7F62EC4A9B1000D1067 /* SRUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7F52EC4A9B1000D1067 /* SRUserInfo.swift */; }; + 03E9A7F82EC4AA54000D1067 /* SRUserDefaultsKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7F72EC4AA4F000D1067 /* SRUserDefaultsKey.swift */; }; + 03E9A7FA2EC56D03000D1067 /* String+SRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7F92EC56CFC000D1067 /* String+SRAdd.swift */; }; + 03E9A7FD2EC57658000D1067 /* SRUserApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7FC2EC5763B000D1067 /* SRUserApi.swift */; }; + 47BB39E2DD30787FA591F8EB /* Pods_SynthReel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9255BF4D4B1CFDDB5CFFB43 /* Pods_SynthReel.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 03980F4E2ECEB91C0006E317 /* SRRecommendPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRRecommendPlayerViewModel.swift; sourceTree = ""; }; + 03980F502ECEBEE20006E317 /* SRRecommendPlayerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRRecommendPlayerCell.swift; sourceTree = ""; }; + 03980F522ECEDEAB0006E317 /* SRRecommendPlayerControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRRecommendPlayerControlView.swift; sourceTree = ""; }; + 03980F582ECEED190006E317 /* SRMyShortViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRMyShortViewController.swift; sourceTree = ""; }; + 03B1A8382EC5C8CE006C353F /* SRHud.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHud.swift; sourceTree = ""; }; + 03B1A83A2EC5C8DB006C353F /* SRToast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRToast.swift; sourceTree = ""; }; + 03B1A83D2EC5C91A006C353F /* SRTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRTool.swift; sourceTree = ""; }; + 03B1A83F2EC5CA2E006C353F /* AppDelegate+Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Config.swift"; sourceTree = ""; }; + 03B1A8422EC5CB99006C353F /* SRTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRTabBarController.swift; sourceTree = ""; }; + 03B1A8442EC5CBBB006C353F /* SRNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRNavigationController.swift; sourceTree = ""; }; + 03B1A8462EC5CBCF006C353F /* SRViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRViewController.swift; sourceTree = ""; }; + 03B1A84F2EC5DB2E006C353F /* SRHomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeViewController.swift; sourceTree = ""; }; + 03B1A8522EC5E129006C353F /* UIScreen+SRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScreen+SRAdd.swift"; sourceTree = ""; }; + 03B1A8542EC5E429006C353F /* UIFont+SRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+SRAdd.swift"; sourceTree = ""; }; + 03B1A8572EC5E4F1006C353F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + 03B1A8692EC6BD49006C353F /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; }; + 03B1A8D42EC6CF37006C353F /* SRHomeChildViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeChildViewController.swift; sourceTree = ""; }; + 03B1A8D72EC6D051006C353F /* SRCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCollectionView.swift; sourceTree = ""; }; + 03B1A8DA2EC6D0EB006C353F /* SRHomeChildCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeChildCell.swift; sourceTree = ""; }; + 03B1A8DB2EC6D0EB006C353F /* SRHomeChildCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SRHomeChildCell.xib; sourceTree = ""; }; + 03B1A8E02EC6D6D3006C353F /* SRHomeMenuDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeMenuDataSource.swift; sourceTree = ""; }; + 03B1A8E22EC6F577006C353F /* SRHomeMenuCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeMenuCell.swift; sourceTree = ""; }; + 03B1A8E42EC715DA006C353F /* SRHomeApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeApi.swift; sourceTree = ""; }; + 03B1A8E62EC7175D006C353F /* SRCategoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCategoryModel.swift; sourceTree = ""; }; + 03B1A8E82EC721CD006C353F /* SRLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRLabel.swift; sourceTree = ""; }; + 03B1A8EC2EC72C18006C353F /* SRHomeModuleItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeModuleItem.swift; sourceTree = ""; }; + 03B1A8EE2EC72C78006C353F /* SRShortModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRShortModel.swift; sourceTree = ""; }; + 03B1A8F02EC72DD7006C353F /* SRHomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeViewModel.swift; sourceTree = ""; }; + 03B1A8F22EC809C5006C353F /* SRHomeHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeHeaderView.swift; sourceTree = ""; }; + 03B1A8F42EC81277006C353F /* SRHomeBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeBannerView.swift; sourceTree = ""; }; + 03B1A8F82EC813BC006C353F /* SRScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRScrollView.swift; sourceTree = ""; }; + 03B1A8FA2EC818B6006C353F /* UIStackView+SRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+SRAdd.swift"; sourceTree = ""; }; + 03B1A8FC2EC81C62006C353F /* SRHomeBannerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeBannerCell.swift; sourceTree = ""; }; + 03B1A8FE2EC81C92006C353F /* SRImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRImageView.swift; sourceTree = ""; }; + 03B1A9002EC852B2006C353F /* SRHomeModuleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeModuleView.swift; sourceTree = ""; }; + 03B1A9022EC8555B006C353F /* SRHomeYouLikeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeYouLikeView.swift; sourceTree = ""; }; + 03B1A9042EC857B3006C353F /* SRHomeYouLikeCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeYouLikeCell.swift; sourceTree = ""; }; + 03B1A9062EC86656006C353F /* SRGradientView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRGradientView.swift; sourceTree = ""; }; + 03B1A9082ECAAF55006C353F /* SRHomeTopChartsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeTopChartsView.swift; sourceTree = ""; }; + 03B1A90A2ECAB2EA006C353F /* SRHomeTopChartsContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeTopChartsContentView.swift; sourceTree = ""; }; + 03B1A90C2ECAC512006C353F /* NSNumber+SRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSNumber+SRAdd.swift"; sourceTree = ""; }; + 03B1A90E2ECAC768006C353F /* SRHomeBingeWorthyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeBingeWorthyView.swift; sourceTree = ""; }; + 03B1A9102ECAC927006C353F /* SRHomeBingeWorthyCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeBingeWorthyCell.swift; sourceTree = ""; }; + 03B1A9122ECAED04006C353F /* SRHomeViralHitsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeViralHitsView.swift; sourceTree = ""; }; + 03B1A9142ECAEE63006C353F /* SRHomeViralHitsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeViralHitsCell.swift; sourceTree = ""; }; + 03B1A9162ECAF14F006C353F /* SRHomeHotView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeHotView.swift; sourceTree = ""; }; + 03B1A9182ECAF2E6006C353F /* SRHomePremiereNowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomePremiereNowView.swift; sourceTree = ""; }; + 03B1A91A2ECAFFCB006C353F /* UIView+SRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+SRAdd.swift"; sourceTree = ""; }; + 03B1A91C2ECB241B006C353F /* UIScrollView+SRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScrollView+SRAdd.swift"; sourceTree = ""; }; + 03B1A91E2ECB2A0E006C353F /* SRHomeBannerMiniCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHomeBannerMiniCell.swift; sourceTree = ""; }; + 03B1A9212ECB456C006C353F /* SRDetailPlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRDetailPlayerViewController.swift; sourceTree = ""; }; + 03B1A9252ECBFF31006C353F /* SRShortPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRShortPlayerViewModel.swift; sourceTree = ""; }; + 03B1A9272ECC05B1006C353F /* SRShortDetailModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRShortDetailModel.swift; sourceTree = ""; }; + 03B1A9292ECC072C006C353F /* SRShortApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRShortApi.swift; sourceTree = ""; }; + 03B1A92B2ECC0A7A006C353F /* SRShortDetailPlayerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRShortDetailPlayerCell.swift; sourceTree = ""; }; + 03B1A92D2ECC0D7E006C353F /* SRShortDetailControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRShortDetailControlView.swift; sourceTree = ""; }; + 03B1A92F2ECC10D1006C353F /* SRSearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRSearchViewController.swift; sourceTree = ""; }; + 03B1A9312ECC1167006C353F /* SRSearchHomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRSearchHomeView.swift; sourceTree = ""; }; + 03B1A9332ECC12D9006C353F /* SRSearchTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRSearchTextView.swift; sourceTree = ""; }; + 03B1A9352ECC1D1D006C353F /* SRSearchRecordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRSearchRecordView.swift; sourceTree = ""; }; + 03B1A9372ECC210D006C353F /* SRSearchRecordCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRSearchRecordCell.swift; sourceTree = ""; }; + 03B1A9392ECC3F54006C353F /* SRSearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRSearchViewModel.swift; sourceTree = ""; }; + 03B1A93B2ECC406E006C353F /* SRHotSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHotSearchView.swift; sourceTree = ""; }; + 03B1A93D2ECC4568006C353F /* SRTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRTableView.swift; sourceTree = ""; }; + 03B1A93F2ECC45BA006C353F /* SRTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRTableViewCell.swift; sourceTree = ""; }; + 03B1A9412ECC4632006C353F /* SRHotSearchCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHotSearchCell.swift; sourceTree = ""; }; + 03B1A9432ECC4ED9006C353F /* SRSearchResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRSearchResultView.swift; sourceTree = ""; }; + 03B1A9452ECC5679006C353F /* SRSearchResultCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRSearchResultCell.swift; sourceTree = ""; }; + 03B1A9472ECC6669006C353F /* SRProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRProgressView.swift; sourceTree = ""; }; + 03B1A9492ECC79AB006C353F /* SREpSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SREpSelectorView.swift; sourceTree = ""; }; + 03B1A94B2ECC7A2D006C353F /* SRPanModalContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRPanModalContentView.swift; sourceTree = ""; }; + 03B1A94D2ECD604B006C353F /* SREpSelectorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SREpSelectorCell.swift; sourceTree = ""; }; + 03B1A94F2ECEB4E6006C353F /* SRRecommendPlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRRecommendPlayerViewController.swift; sourceTree = ""; }; + 03E9A7A72EC4716A000D1067 /* SynthReel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SynthReel.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 03E9A7BF2EC47177000D1067 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 03E9A7C02EC47177000D1067 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 03E9A7C12EC47177000D1067 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 03E9A7C22EC47177000D1067 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 03E9A7C62EC47177000D1067 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 03E9A7D62EC479FE000D1067 /* SRResponseCryptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRResponseCryptor.swift; sourceTree = ""; }; + 03E9A7D82EC47B90000D1067 /* SRNetworkReachableManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRNetworkReachableManager.swift; sourceTree = ""; }; + 03E9A7DA2EC485B3000D1067 /* SRNetwork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRNetwork.swift; sourceTree = ""; }; + 03E9A7DC2EC485DD000D1067 /* SRNetworkModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRNetworkModel.swift; sourceTree = ""; }; + 03E9A7E12EC494F9000D1067 /* SRTargetType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRTargetType.swift; sourceTree = ""; }; + 03E9A7E32EC49584000D1067 /* SRUrlPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRUrlPath.swift; sourceTree = ""; }; + 03E9A7E62EC49813000D1067 /* SRDefine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRDefine.swift; sourceTree = ""; }; + 03E9A7E92EC4995D000D1067 /* SRKeychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRKeychain.swift; sourceTree = ""; }; + 03E9A7EB2EC499A9000D1067 /* SRDeviceId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRDeviceId.swift; sourceTree = ""; }; + 03E9A7EE2EC4A8AF000D1067 /* SRAccountManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRAccountManager.swift; sourceTree = ""; }; + 03E9A7F12EC4A8EC000D1067 /* UserDefaults+SRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+SRAdd.swift"; sourceTree = ""; }; + 03E9A7F32EC4A94D000D1067 /* SRAccountToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRAccountToken.swift; sourceTree = ""; }; + 03E9A7F52EC4A9B1000D1067 /* SRUserInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRUserInfo.swift; sourceTree = ""; }; + 03E9A7F72EC4AA4F000D1067 /* SRUserDefaultsKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRUserDefaultsKey.swift; sourceTree = ""; }; + 03E9A7F92EC56CFC000D1067 /* String+SRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+SRAdd.swift"; sourceTree = ""; }; + 03E9A7FC2EC5763B000D1067 /* SRUserApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRUserApi.swift; sourceTree = ""; }; + 59DC746604B26E9FF802D317 /* Pods-SynthReel.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SynthReel.debug.xcconfig"; path = "Target Support Files/Pods-SynthReel/Pods-SynthReel.debug.xcconfig"; sourceTree = ""; }; + AA88214030574193B51DE563 /* Pods-SynthReel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SynthReel.release.xcconfig"; path = "Target Support Files/Pods-SynthReel/Pods-SynthReel.release.xcconfig"; sourceTree = ""; }; + F9255BF4D4B1CFDDB5CFFB43 /* Pods_SynthReel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SynthReel.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 03E9A7A42EC4716A000D1067 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 03B1A84D2EC5DA43006C353F /* SnapKit in Frameworks */, + 03B1A84A2EC5CE37006C353F /* ESTabBarController in Frameworks */, + 47BB39E2DD30787FA591F8EB /* Pods_SynthReel.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 03980F542ECEEC990006E317 /* MyShort */ = { + isa = PBXGroup; + children = ( + 03980F552ECEECB30006E317 /* VC */, + 03980F562ECEECBC0006E317 /* V */, + 03980F572ECEECCC0006E317 /* M */, + ); + path = MyShort; + sourceTree = ""; + }; + 03980F552ECEECB30006E317 /* VC */ = { + isa = PBXGroup; + children = ( + 03980F582ECEED190006E317 /* SRMyShortViewController.swift */, + ); + path = VC; + sourceTree = ""; + }; + 03980F562ECEECBC0006E317 /* V */ = { + isa = PBXGroup; + children = ( + ); + path = V; + sourceTree = ""; + }; + 03980F572ECEECCC0006E317 /* M */ = { + isa = PBXGroup; + children = ( + ); + path = M; + sourceTree = ""; + }; + 03B1A8372EC5C8BF006C353F /* SRHud */ = { + isa = PBXGroup; + children = ( + 03B1A83A2EC5C8DB006C353F /* SRToast.swift */, + 03B1A8382EC5C8CE006C353F /* SRHud.swift */, + ); + path = SRHud; + sourceTree = ""; + }; + 03B1A83C2EC5C90F006C353F /* Tool */ = { + isa = PBXGroup; + children = ( + 03B1A83D2EC5C91A006C353F /* SRTool.swift */, + ); + path = Tool; + sourceTree = ""; + }; + 03B1A8412EC5CB4F006C353F /* ViewController */ = { + isa = PBXGroup; + children = ( + 03B1A8422EC5CB99006C353F /* SRTabBarController.swift */, + 03B1A8442EC5CBBB006C353F /* SRNavigationController.swift */, + 03B1A8462EC5CBCF006C353F /* SRViewController.swift */, + ); + path = ViewController; + sourceTree = ""; + }; + 03B1A84E2EC5DB13006C353F /* Home */ = { + isa = PBXGroup; + children = ( + 03B1A8512EC5DB32006C353F /* VC */, + 03B1A8D92EC6D0BA006C353F /* V */, + 03B1A8DE2EC6D698006C353F /* M */, + 03B1A8DF2EC6D69F006C353F /* VM */, + ); + path = Home; + sourceTree = ""; + }; + 03B1A8512EC5DB32006C353F /* VC */ = { + isa = PBXGroup; + children = ( + 03B1A84F2EC5DB2E006C353F /* SRHomeViewController.swift */, + 03B1A8D42EC6CF37006C353F /* SRHomeChildViewController.swift */, + 03B1A92F2ECC10D1006C353F /* SRSearchViewController.swift */, + ); + path = VC; + sourceTree = ""; + }; + 03B1A8592EC6BC22006C353F /* Thirdparty */ = { + isa = PBXGroup; + children = ( + ); + path = Thirdparty; + sourceTree = ""; + }; + 03B1A8D62EC6D03A006C353F /* View */ = { + isa = PBXGroup; + children = ( + 03B1A8D72EC6D051006C353F /* SRCollectionView.swift */, + 03B1A8E82EC721CD006C353F /* SRLabel.swift */, + 03B1A8F82EC813BC006C353F /* SRScrollView.swift */, + 03B1A8FE2EC81C92006C353F /* SRImageView.swift */, + 03B1A9062EC86656006C353F /* SRGradientView.swift */, + 03B1A93D2ECC4568006C353F /* SRTableView.swift */, + 03B1A93F2ECC45BA006C353F /* SRTableViewCell.swift */, + 03B1A94B2ECC7A2D006C353F /* SRPanModalContentView.swift */, + ); + path = View; + sourceTree = ""; + }; + 03B1A8D92EC6D0BA006C353F /* V */ = { + isa = PBXGroup; + children = ( + 03B1A8DA2EC6D0EB006C353F /* SRHomeChildCell.swift */, + 03B1A8DB2EC6D0EB006C353F /* SRHomeChildCell.xib */, + 03B1A8E22EC6F577006C353F /* SRHomeMenuCell.swift */, + 03B1A8F22EC809C5006C353F /* SRHomeHeaderView.swift */, + 03B1A8F42EC81277006C353F /* SRHomeBannerView.swift */, + 03B1A8FC2EC81C62006C353F /* SRHomeBannerCell.swift */, + 03B1A91E2ECB2A0E006C353F /* SRHomeBannerMiniCell.swift */, + 03B1A9022EC8555B006C353F /* SRHomeYouLikeView.swift */, + 03B1A9042EC857B3006C353F /* SRHomeYouLikeCell.swift */, + 03B1A9002EC852B2006C353F /* SRHomeModuleView.swift */, + 03B1A9082ECAAF55006C353F /* SRHomeTopChartsView.swift */, + 03B1A90A2ECAB2EA006C353F /* SRHomeTopChartsContentView.swift */, + 03B1A9162ECAF14F006C353F /* SRHomeHotView.swift */, + 03B1A90E2ECAC768006C353F /* SRHomeBingeWorthyView.swift */, + 03B1A9102ECAC927006C353F /* SRHomeBingeWorthyCell.swift */, + 03B1A9122ECAED04006C353F /* SRHomeViralHitsView.swift */, + 03B1A9142ECAEE63006C353F /* SRHomeViralHitsCell.swift */, + 03B1A9182ECAF2E6006C353F /* SRHomePremiereNowView.swift */, + 03B1A9332ECC12D9006C353F /* SRSearchTextView.swift */, + 03B1A9312ECC1167006C353F /* SRSearchHomeView.swift */, + 03B1A9352ECC1D1D006C353F /* SRSearchRecordView.swift */, + 03B1A9372ECC210D006C353F /* SRSearchRecordCell.swift */, + 03B1A93B2ECC406E006C353F /* SRHotSearchView.swift */, + 03B1A9412ECC4632006C353F /* SRHotSearchCell.swift */, + 03B1A9432ECC4ED9006C353F /* SRSearchResultView.swift */, + 03B1A9452ECC5679006C353F /* SRSearchResultCell.swift */, + ); + path = V; + sourceTree = ""; + }; + 03B1A8DE2EC6D698006C353F /* M */ = { + isa = PBXGroup; + children = ( + 03B1A8E62EC7175D006C353F /* SRCategoryModel.swift */, + 03B1A8EC2EC72C18006C353F /* SRHomeModuleItem.swift */, + ); + path = M; + sourceTree = ""; + }; + 03B1A8DF2EC6D69F006C353F /* VM */ = { + isa = PBXGroup; + children = ( + 03B1A8E02EC6D6D3006C353F /* SRHomeMenuDataSource.swift */, + 03B1A8F02EC72DD7006C353F /* SRHomeViewModel.swift */, + 03B1A9392ECC3F54006C353F /* SRSearchViewModel.swift */, + ); + path = VM; + sourceTree = ""; + }; + 03B1A8EA2EC72BFB006C353F /* Player */ = { + isa = PBXGroup; + children = ( + 03B1A9202ECB2F25006C353F /* VC */, + 03B1A9242ECBFF03006C353F /* V */, + 03B1A8EB2EC72C0E006C353F /* M */, + 03B1A9232ECBFEF9006C353F /* VM */, + ); + path = Player; + sourceTree = ""; + }; + 03B1A8EB2EC72C0E006C353F /* M */ = { + isa = PBXGroup; + children = ( + 03B1A8EE2EC72C78006C353F /* SRShortModel.swift */, + 03B1A9272ECC05B1006C353F /* SRShortDetailModel.swift */, + ); + path = M; + sourceTree = ""; + }; + 03B1A9202ECB2F25006C353F /* VC */ = { + isa = PBXGroup; + children = ( + 03B1A9212ECB456C006C353F /* SRDetailPlayerViewController.swift */, + 03B1A94F2ECEB4E6006C353F /* SRRecommendPlayerViewController.swift */, + ); + path = VC; + sourceTree = ""; + }; + 03B1A9232ECBFEF9006C353F /* VM */ = { + isa = PBXGroup; + children = ( + 03B1A9252ECBFF31006C353F /* SRShortPlayerViewModel.swift */, + 03980F4E2ECEB91C0006E317 /* SRRecommendPlayerViewModel.swift */, + ); + path = VM; + sourceTree = ""; + }; + 03B1A9242ECBFF03006C353F /* V */ = { + isa = PBXGroup; + children = ( + 03B1A92B2ECC0A7A006C353F /* SRShortDetailPlayerCell.swift */, + 03B1A92D2ECC0D7E006C353F /* SRShortDetailControlView.swift */, + 03980F502ECEBEE20006E317 /* SRRecommendPlayerCell.swift */, + 03980F522ECEDEAB0006E317 /* SRRecommendPlayerControlView.swift */, + 03B1A9472ECC6669006C353F /* SRProgressView.swift */, + 03B1A9492ECC79AB006C353F /* SREpSelectorView.swift */, + 03B1A94D2ECD604B006C353F /* SREpSelectorCell.swift */, + ); + path = V; + sourceTree = ""; + }; + 03E9A79E2EC4716A000D1067 = { + isa = PBXGroup; + children = ( + 03E9A7C82EC47177000D1067 /* SynthReel */, + 03E9A7A82EC4716A000D1067 /* Products */, + 8CAFCEC2C631CAE75726D90C /* Pods */, + CF8628F3EB7A59852BCAF359 /* Frameworks */, + ); + sourceTree = ""; + }; + 03E9A7A82EC4716A000D1067 /* Products */ = { + isa = PBXGroup; + children = ( + 03E9A7A72EC4716A000D1067 /* SynthReel.app */, + ); + name = Products; + sourceTree = ""; + }; + 03E9A7C82EC47177000D1067 /* SynthReel */ = { + isa = PBXGroup; + children = ( + 03E9A7D02EC471D6000D1067 /* Delegate */, + 03E9A7D22EC47204000D1067 /* Base */, + 03E9A7D32EC4720F000D1067 /* Class */, + 03E9A7D12EC471F3000D1067 /* Source */, + 03E9A7D42EC4764A000D1067 /* Libs */, + 03B1A8592EC6BC22006C353F /* Thirdparty */, + ); + path = SynthReel; + sourceTree = ""; + }; + 03E9A7D02EC471D6000D1067 /* Delegate */ = { + isa = PBXGroup; + children = ( + 03E9A7BF2EC47177000D1067 /* AppDelegate.swift */, + 03E9A7C62EC47177000D1067 /* SceneDelegate.swift */, + 03B1A83F2EC5CA2E006C353F /* AppDelegate+Config.swift */, + ); + path = Delegate; + sourceTree = ""; + }; + 03E9A7D12EC471F3000D1067 /* Source */ = { + isa = PBXGroup; + children = ( + 03E9A7C02EC47177000D1067 /* Assets.xcassets */, + 03E9A7C12EC47177000D1067 /* Info.plist */, + 03E9A7C32EC47177000D1067 /* LaunchScreen.storyboard */, + 03B1A8562EC5E4F1006C353F /* Localizable.strings */, + 03B1A8692EC6BD49006C353F /* Bridging-Header.h */, + ); + path = Source; + sourceTree = ""; + }; + 03E9A7D22EC47204000D1067 /* Base */ = { + isa = PBXGroup; + children = ( + 03B1A8412EC5CB4F006C353F /* ViewController */, + 03B1A8D62EC6D03A006C353F /* View */, + 03E9A7F02EC4A8DD000D1067 /* Extension */, + 03E9A7E52EC49804000D1067 /* Define */, + 03E9A7D52EC478C7000D1067 /* Networking */, + 03E9A7FB2EC5761A000D1067 /* API */, + ); + path = Base; + sourceTree = ""; + }; + 03E9A7D32EC4720F000D1067 /* Class */ = { + isa = PBXGroup; + children = ( + 03980F542ECEEC990006E317 /* MyShort */, + 03B1A84E2EC5DB13006C353F /* Home */, + 03B1A8EA2EC72BFB006C353F /* Player */, + ); + path = Class; + sourceTree = ""; + }; + 03E9A7D42EC4764A000D1067 /* Libs */ = { + isa = PBXGroup; + children = ( + 03B1A83C2EC5C90F006C353F /* Tool */, + 03B1A8372EC5C8BF006C353F /* SRHud */, + 03E9A7ED2EC4A855000D1067 /* SRAccount */, + 03E9A7E82EC49910000D1067 /* SRDeviceID */, + ); + path = Libs; + sourceTree = ""; + }; + 03E9A7D52EC478C7000D1067 /* Networking */ = { + isa = PBXGroup; + children = ( + 03E9A7DA2EC485B3000D1067 /* SRNetwork.swift */, + 03E9A7E12EC494F9000D1067 /* SRTargetType.swift */, + 03E9A7E32EC49584000D1067 /* SRUrlPath.swift */, + 03E9A7DC2EC485DD000D1067 /* SRNetworkModel.swift */, + 03E9A7D62EC479FE000D1067 /* SRResponseCryptor.swift */, + 03E9A7D82EC47B90000D1067 /* SRNetworkReachableManager.swift */, + ); + path = Networking; + sourceTree = ""; + }; + 03E9A7E52EC49804000D1067 /* Define */ = { + isa = PBXGroup; + children = ( + 03E9A7F72EC4AA4F000D1067 /* SRUserDefaultsKey.swift */, + 03E9A7E62EC49813000D1067 /* SRDefine.swift */, + ); + path = Define; + sourceTree = ""; + }; + 03E9A7E82EC49910000D1067 /* SRDeviceID */ = { + isa = PBXGroup; + children = ( + 03E9A7EB2EC499A9000D1067 /* SRDeviceId.swift */, + 03E9A7E92EC4995D000D1067 /* SRKeychain.swift */, + ); + path = SRDeviceID; + sourceTree = ""; + }; + 03E9A7ED2EC4A855000D1067 /* SRAccount */ = { + isa = PBXGroup; + children = ( + 03E9A7EE2EC4A8AF000D1067 /* SRAccountManager.swift */, + 03E9A7F32EC4A94D000D1067 /* SRAccountToken.swift */, + 03E9A7F52EC4A9B1000D1067 /* SRUserInfo.swift */, + ); + path = SRAccount; + sourceTree = ""; + }; + 03E9A7F02EC4A8DD000D1067 /* Extension */ = { + isa = PBXGroup; + children = ( + 03B1A90C2ECAC512006C353F /* NSNumber+SRAdd.swift */, + 03B1A8FA2EC818B6006C353F /* UIStackView+SRAdd.swift */, + 03B1A8542EC5E429006C353F /* UIFont+SRAdd.swift */, + 03B1A8522EC5E129006C353F /* UIScreen+SRAdd.swift */, + 03E9A7F92EC56CFC000D1067 /* String+SRAdd.swift */, + 03E9A7F12EC4A8EC000D1067 /* UserDefaults+SRAdd.swift */, + 03B1A91A2ECAFFCB006C353F /* UIView+SRAdd.swift */, + 03B1A91C2ECB241B006C353F /* UIScrollView+SRAdd.swift */, + ); + path = Extension; + sourceTree = ""; + }; + 03E9A7FB2EC5761A000D1067 /* API */ = { + isa = PBXGroup; + children = ( + 03B1A9292ECC072C006C353F /* SRShortApi.swift */, + 03B1A8E42EC715DA006C353F /* SRHomeApi.swift */, + 03E9A7FC2EC5763B000D1067 /* SRUserApi.swift */, + ); + path = API; + sourceTree = ""; + }; + 8CAFCEC2C631CAE75726D90C /* Pods */ = { + isa = PBXGroup; + children = ( + 59DC746604B26E9FF802D317 /* Pods-SynthReel.debug.xcconfig */, + AA88214030574193B51DE563 /* Pods-SynthReel.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + CF8628F3EB7A59852BCAF359 /* Frameworks */ = { + isa = PBXGroup; + children = ( + F9255BF4D4B1CFDDB5CFFB43 /* Pods_SynthReel.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 03E9A7A62EC4716A000D1067 /* SynthReel */ = { + isa = PBXNativeTarget; + buildConfigurationList = 03E9A7BA2EC4716C000D1067 /* Build configuration list for PBXNativeTarget "SynthReel" */; + buildPhases = ( + B73FFE5658F839BD48F75FE4 /* [CP] Check Pods Manifest.lock */, + 03E9A7A32EC4716A000D1067 /* Sources */, + 03E9A7A42EC4716A000D1067 /* Frameworks */, + 03E9A7A52EC4716A000D1067 /* Resources */, + 92A47CFFAED31670C9452A17 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SynthReel; + productName = SynthReel; + productReference = 03E9A7A72EC4716A000D1067 /* SynthReel.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 03E9A79F2EC4716A000D1067 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + CLASSPREFIX = SR; + LastSwiftUpdateCheck = 2600; + LastUpgradeCheck = 2600; + ORGANIZATIONNAME = SR; + TargetAttributes = { + 03E9A7A62EC4716A000D1067 = { + CreatedOnToolsVersion = 26.0.1; + }; + }; + }; + buildConfigurationList = 03E9A7A22EC4716A000D1067 /* Build configuration list for PBXProject "SynthReel" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 03E9A79E2EC4716A000D1067; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + 03B1A8482EC5CE37006C353F /* XCRemoteSwiftPackageReference "ESTabBarController" */, + 03B1A84B2EC5DA43006C353F /* XCRemoteSwiftPackageReference "SnapKit" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = 03E9A7A82EC4716A000D1067 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 03E9A7A62EC4716A000D1067 /* SynthReel */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 03E9A7A52EC4716A000D1067 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03E9A7CC2EC47177000D1067 /* Assets.xcassets in Resources */, + 03B1A8582EC5E4F1006C353F /* Localizable.strings in Resources */, + 03B1A8DD2EC6D0EB006C353F /* SRHomeChildCell.xib in Resources */, + 03E9A7CE2EC47177000D1067 /* LaunchScreen.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 92A47CFFAED31670C9452A17 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-SynthReel/Pods-SynthReel-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-SynthReel/Pods-SynthReel-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SynthReel/Pods-SynthReel-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + B73FFE5658F839BD48F75FE4 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-SynthReel-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 03E9A7A32EC4716A000D1067 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03B1A9402ECC45BA006C353F /* SRTableViewCell.swift in Sources */, + 03E9A7EA2EC4995D000D1067 /* SRKeychain.swift in Sources */, + 03E9A7D92EC47B90000D1067 /* SRNetworkReachableManager.swift in Sources */, + 03B1A8532EC5E12E006C353F /* UIScreen+SRAdd.swift in Sources */, + 03B1A8FB2EC818BE006C353F /* UIStackView+SRAdd.swift in Sources */, + 03B1A8E12EC6D6D3006C353F /* SRHomeMenuDataSource.swift in Sources */, + 03E9A7F82EC4AA54000D1067 /* SRUserDefaultsKey.swift in Sources */, + 03B1A8452EC5CBBB006C353F /* SRNavigationController.swift in Sources */, + 03B1A9092ECAAF55006C353F /* SRHomeTopChartsView.swift in Sources */, + 03E9A7E42EC49593000D1067 /* SRUrlPath.swift in Sources */, + 03B1A9462ECC5679006C353F /* SRSearchResultCell.swift in Sources */, + 03B1A9502ECEB4E6006C353F /* SRRecommendPlayerViewController.swift in Sources */, + 03B1A83E2EC5C91E006C353F /* SRTool.swift in Sources */, + 03B1A9172ECAF14F006C353F /* SRHomeHotView.swift in Sources */, + 03B1A8FD2EC81C62006C353F /* SRHomeBannerCell.swift in Sources */, + 03E9A7DB2EC485BE000D1067 /* SRNetwork.swift in Sources */, + 03E9A7F42EC4A94D000D1067 /* SRAccountToken.swift in Sources */, + 03B1A8E72EC7175D006C353F /* SRCategoryModel.swift in Sources */, + 03B1A94A2ECC79AB006C353F /* SREpSelectorView.swift in Sources */, + 03B1A94E2ECD604B006C353F /* SREpSelectorCell.swift in Sources */, + 03B1A9262ECBFF31006C353F /* SRShortPlayerViewModel.swift in Sources */, + 03B1A9322ECC1167006C353F /* SRSearchHomeView.swift in Sources */, + 03B1A8F32EC809C5006C353F /* SRHomeHeaderView.swift in Sources */, + 03B1A9482ECC6669006C353F /* SRProgressView.swift in Sources */, + 03B1A91D2ECB2424006C353F /* UIScrollView+SRAdd.swift in Sources */, + 03E9A7C92EC47177000D1067 /* AppDelegate.swift in Sources */, + 03E9A7FA2EC56D03000D1067 /* String+SRAdd.swift in Sources */, + 03B1A9192ECAF2E6006C353F /* SRHomePremiereNowView.swift in Sources */, + 03B1A92C2ECC0A7A006C353F /* SRShortDetailPlayerCell.swift in Sources */, + 03B1A8392EC5C8D6006C353F /* SRHud.swift in Sources */, + 03E9A7EC2EC499A9000D1067 /* SRDeviceId.swift in Sources */, + 03E9A7E22EC494F9000D1067 /* SRTargetType.swift in Sources */, + 03980F532ECEDEAB0006E317 /* SRRecommendPlayerControlView.swift in Sources */, + 03B1A90F2ECAC768006C353F /* SRHomeBingeWorthyView.swift in Sources */, + 03B1A9072EC86656006C353F /* SRGradientView.swift in Sources */, + 03E9A7CA2EC47177000D1067 /* SceneDelegate.swift in Sources */, + 03B1A8402EC5CA37006C353F /* AppDelegate+Config.swift in Sources */, + 03B1A9152ECAEE63006C353F /* SRHomeViralHitsCell.swift in Sources */, + 03B1A8472EC5CBCF006C353F /* SRViewController.swift in Sources */, + 03B1A9032EC8555B006C353F /* SRHomeYouLikeView.swift in Sources */, + 03B1A9362ECC1D1D006C353F /* SRSearchRecordView.swift in Sources */, + 03B1A8D82EC6D051006C353F /* SRCollectionView.swift in Sources */, + 03B1A90D2ECAC51A006C353F /* NSNumber+SRAdd.swift in Sources */, + 03E9A7DD2EC485E1000D1067 /* SRNetworkModel.swift in Sources */, + 03B1A9442ECC4ED9006C353F /* SRSearchResultView.swift in Sources */, + 03B1A8FF2EC81C92006C353F /* SRImageView.swift in Sources */, + 03B1A83B2EC5C8E0006C353F /* SRToast.swift in Sources */, + 03B1A9302ECC10D1006C353F /* SRSearchViewController.swift in Sources */, + 03B1A8432EC5CB99006C353F /* SRTabBarController.swift in Sources */, + 03B1A8EF2EC72C78006C353F /* SRShortModel.swift in Sources */, + 03B1A8ED2EC72C1F006C353F /* SRHomeModuleItem.swift in Sources */, + 03B1A9112ECAC927006C353F /* SRHomeBingeWorthyCell.swift in Sources */, + 03E9A7F22EC4A8F6000D1067 /* UserDefaults+SRAdd.swift in Sources */, + 03B1A93A2ECC3F54006C353F /* SRSearchViewModel.swift in Sources */, + 03E9A7F62EC4A9B1000D1067 /* SRUserInfo.swift in Sources */, + 03B1A8F92EC813BC006C353F /* SRScrollView.swift in Sources */, + 03B1A8E92EC721CD006C353F /* SRLabel.swift in Sources */, + 03B1A9282ECC05B1006C353F /* SRShortDetailModel.swift in Sources */, + 03980F592ECEED190006E317 /* SRMyShortViewController.swift in Sources */, + 03B1A92A2ECC0738006C353F /* SRShortApi.swift in Sources */, + 03B1A90B2ECAB2EA006C353F /* SRHomeTopChartsContentView.swift in Sources */, + 03B1A94C2ECC7A2D006C353F /* SRPanModalContentView.swift in Sources */, + 03B1A9052EC857B3006C353F /* SRHomeYouLikeCell.swift in Sources */, + 03B1A92E2ECC0D7E006C353F /* SRShortDetailControlView.swift in Sources */, + 03B1A9382ECC210D006C353F /* SRSearchRecordCell.swift in Sources */, + 03E9A7D72EC47A23000D1067 /* SRResponseCryptor.swift in Sources */, + 03B1A8502EC5DB2E006C353F /* SRHomeViewController.swift in Sources */, + 03B1A8E52EC715E1006C353F /* SRHomeApi.swift in Sources */, + 03B1A8D52EC6CF37006C353F /* SRHomeChildViewController.swift in Sources */, + 03B1A8F12EC72DD7006C353F /* SRHomeViewModel.swift in Sources */, + 03E9A7FD2EC57658000D1067 /* SRUserApi.swift in Sources */, + 03E9A7E72EC49819000D1067 /* SRDefine.swift in Sources */, + 03B1A9012EC852B2006C353F /* SRHomeModuleView.swift in Sources */, + 03E9A7EF2EC4A8AF000D1067 /* SRAccountManager.swift in Sources */, + 03B1A8F52EC81277006C353F /* SRHomeBannerView.swift in Sources */, + 03B1A9132ECAED04006C353F /* SRHomeViralHitsView.swift in Sources */, + 03B1A8DC2EC6D0EB006C353F /* SRHomeChildCell.swift in Sources */, + 03B1A8552EC5E434006C353F /* UIFont+SRAdd.swift in Sources */, + 03980F512ECEBEE20006E317 /* SRRecommendPlayerCell.swift in Sources */, + 03B1A91B2ECAFFD6006C353F /* UIView+SRAdd.swift in Sources */, + 03B1A9222ECB456C006C353F /* SRDetailPlayerViewController.swift in Sources */, + 03B1A91F2ECB2A0E006C353F /* SRHomeBannerMiniCell.swift in Sources */, + 03B1A93E2ECC4568006C353F /* SRTableView.swift in Sources */, + 03980F4F2ECEB91C0006E317 /* SRRecommendPlayerViewModel.swift in Sources */, + 03B1A9422ECC4632006C353F /* SRHotSearchCell.swift in Sources */, + 03B1A9342ECC12D9006C353F /* SRSearchTextView.swift in Sources */, + 03B1A93C2ECC406E006C353F /* SRHotSearchView.swift in Sources */, + 03B1A8E32EC6F577006C353F /* SRHomeMenuCell.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 03B1A8562EC5E4F1006C353F /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 03B1A8572EC5E4F1006C353F /* en */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + 03E9A7C32EC47177000D1067 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 03E9A7C22EC47177000D1067 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 03E9A7BB2EC4716C000D1067 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 59DC746604B26E9FF802D317 /* Pods-SynthReel.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 6XALB8RSYF; + EXCLUDED_ARCHS = ""; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = SynthReel/Source/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = ""; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.hbqinjiu.SynthReel; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "SynthReel/Source/Bridging-Header.h"; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + 03E9A7BC2EC4716C000D1067 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AA88214030574193B51DE563 /* Pods-SynthReel.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 6XALB8RSYF; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = SynthReel/Source/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = ""; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.hbqinjiu.SynthReel; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "SynthReel/Source/Bridging-Header.h"; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; + 03E9A7BD2EC4716C000D1067 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 6XALB8RSYF; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 03E9A7BE2EC4716C000D1067 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 6XALB8RSYF; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 03E9A7A22EC4716A000D1067 /* Build configuration list for PBXProject "SynthReel" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03E9A7BD2EC4716C000D1067 /* Debug */, + 03E9A7BE2EC4716C000D1067 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 03E9A7BA2EC4716C000D1067 /* Build configuration list for PBXNativeTarget "SynthReel" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03E9A7BB2EC4716C000D1067 /* Debug */, + 03E9A7BC2EC4716C000D1067 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 03B1A8482EC5CE37006C353F /* XCRemoteSwiftPackageReference "ESTabBarController" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/eggswift/ESTabBarController.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.9.0; + }; + }; + 03B1A84B2EC5DA43006C353F /* XCRemoteSwiftPackageReference "SnapKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SnapKit/SnapKit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.7.1; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 03B1A8492EC5CE37006C353F /* ESTabBarController */ = { + isa = XCSwiftPackageProductDependency; + package = 03B1A8482EC5CE37006C353F /* XCRemoteSwiftPackageReference "ESTabBarController" */; + productName = ESTabBarController; + }; + 03B1A84C2EC5DA43006C353F /* SnapKit */ = { + isa = XCSwiftPackageProductDependency; + package = 03B1A84B2EC5DA43006C353F /* XCRemoteSwiftPackageReference "SnapKit" */; + productName = SnapKit; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 03E9A79F2EC4716A000D1067 /* Project object */; +} diff --git a/SynthReel.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/SynthReel.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/SynthReel.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SynthReel.xcodeproj/xcshareddata/xcschemes/SynthReel.xcscheme b/SynthReel.xcodeproj/xcshareddata/xcschemes/SynthReel.xcscheme new file mode 100644 index 0000000..4076887 --- /dev/null +++ b/SynthReel.xcodeproj/xcshareddata/xcschemes/SynthReel.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SynthReel.xcworkspace/contents.xcworkspacedata b/SynthReel.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..0a349e5 --- /dev/null +++ b/SynthReel.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/SynthReel.xcworkspace/xcshareddata/swiftpm/Package.resolved b/SynthReel.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..4145a73 --- /dev/null +++ b/SynthReel.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,24 @@ +{ + "originHash" : "0e62262c59a183f44748a161870cc0f2b76e1b0e46f648559704e4be9de523b9", + "pins" : [ + { + "identity" : "estabbarcontroller", + "kind" : "remoteSourceControl", + "location" : "https://github.com/eggswift/ESTabBarController.git", + "state" : { + "revision" : "93a30b833a05fd916c6d4c5d6e94a270cf3b6636", + "version" : "2.9.0" + } + }, + { + "identity" : "snapkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SnapKit/SnapKit", + "state" : { + "revision" : "2842e6e84e82eb9a8dac0100ca90d9444b0307f4", + "version" : "5.7.1" + } + } + ], + "version" : 3 +} diff --git a/SynthReel/Base/API/SRHomeApi.swift b/SynthReel/Base/API/SRHomeApi.swift new file mode 100644 index 0000000..b70aeff --- /dev/null +++ b/SynthReel/Base/API/SRHomeApi.swift @@ -0,0 +1,92 @@ +// +// SRHomeApi.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/14. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import Alamofire + + +struct SRHomeApi { + + static func requestCategoryList() async -> [SRCategoryModel]? { + + await withCheckedContinuation { continuation in + var param = SRNetwork.Parameters(path: "/getCategories") + param.method = .get + SRNetwork.request(parameters: param) { (response: SRNetwork.Response>) in + continuation.resume(returning: response.data?.list) + } + } + + } + + static func requestHomeModulesData() async -> [SRHomeModuleItem]? { + await withCheckedContinuation { continuation in + var param = SRNetwork.Parameters(path: "/home/all-modules") + param.method = .get + SRNetwork.request(parameters: param) { (response: SRNetwork.Response>) in + continuation.resume(returning: response.data?.list) + } + } + } + + static func requestCategoryVideoData(_ id: String, page: Int) async -> [SRShortModel]? { + await withCheckedContinuation { continuation in + var param = SRNetwork.Parameters(path: "/videoList") + param.method = .get + param.parameters = [ + "category_id" : id, + "current_page" : page, + "page_size" : 20 + ] + SRNetwork.request(parameters: param) { (response: SRNetwork.Response>) in + continuation.resume(returning: response.data?.list) + } + } + } + + static func requestHotSearchData() async -> [SRShortModel]? { + await withCheckedContinuation { continuation in + var param = SRNetwork.Parameters(path: "/search/hots") + param.method = .get + + SRNetwork.request(parameters: param) { (response: SRNetwork.Response>) in + continuation.resume(returning: response.data?.list) + } + } + } + + static func requestSearch(_ text: String) async -> [SRShortModel]? { + await withCheckedContinuation { continuation in + var param = SRNetwork.Parameters(path: "/search") + param.method = .get + param.parameters = [ + "search" : text + ] + + SRNetwork.request(parameters: param) { (response: SRNetwork.Response>) in + continuation.resume(returning: response.data?.list) + } + } + } + + static func requestHomeRecommendData(page: Int) async -> [SRShortModel]? { + await withCheckedContinuation { continuation in + var param = SRNetwork.Parameters(path: "/getRecommands") + param.method = .get + param.parameters = [ + "page_size" : 20, + "current_page" : page + ] + + SRNetwork.request(parameters: param) { (response: SRNetwork.Response>) in + continuation.resume(returning: response.data?.list) + } + } + } + +} diff --git a/SynthReel/Base/API/SRShortApi.swift b/SynthReel/Base/API/SRShortApi.swift new file mode 100644 index 0000000..615e710 --- /dev/null +++ b/SynthReel/Base/API/SRShortApi.swift @@ -0,0 +1,88 @@ +// +// SRShortApi.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import Alamofire + +struct SRShortApi { + + static func requestShortDetail(_ id: String) async -> (SRShortDetailModel?, Int?, String?) { + await withCheckedContinuation { continuation in + var param = SRNetwork.Parameters(path: "/getVideoDetails") + param.method = .get + param.parameters = [ + "short_play_id" : id, + "video_id" : 0, + ] + SRNetwork.request(parameters: param) { (response: SRNetwork.Response) in + if response.isSuccess { + continuation.resume(returning:(response.data, response.code, response.msg)) + } else { + continuation.resume(returning:(nil, response.code, response.msg)) + } + } + } + } + + static func requestShortCollect(shortId: String, videoId: String?, isCollect: Bool) async -> Bool { + await withCheckedContinuation { continuation in + var path = "" + if isCollect { + path = "/collect" + } else { + path = "/cancelCollect" + } + + var param = SRNetwork.Parameters(path: path) + param.isLoding = true + param.parameters = [ + "short_play_id" : shortId, + "video_id" : videoId ?? "0", + ] + + SRNetwork.request(parameters: param) { (response: SRNetwork.Response) in + if response.isSuccess { + continuation.resume(returning: true) + NotificationCenter.default.post(name: SRShortApi.updateShortCollectStateNotification, object: nil, userInfo: [ + "state" : isCollect, + "id" : shortId, + ]) + } else { + continuation.resume(returning: false) + } + } + } + } + + static func requestCreatePlayHistory(shortId: String?, videoId: String?) async { + guard let shortId = shortId else { return } + + await withCheckedContinuation { continuation in + var param = SRNetwork.Parameters(path: "/createHistory") + param.isToast = false + param.parameters = [ + "short_play_id" : shortId, + "video_id" : videoId ?? "0", + ] + SRNetwork.request(parameters: param) { (response: SRNetwork.Response) in + continuation.resume() + } + } + } + + +} + + +extension SRShortApi { + + + ///更新短剧关注状态 [ "state" : isCollect, "id" : shortPlayId,] + static let updateShortCollectStateNotification = NSNotification.Name(rawValue: "SRShortApi.updateShortCollectStateNotification") + +} diff --git a/SynthReel/Base/API/SRUserApi.swift b/SynthReel/Base/API/SRUserApi.swift new file mode 100644 index 0000000..40d6cc6 --- /dev/null +++ b/SynthReel/Base/API/SRUserApi.swift @@ -0,0 +1,25 @@ +// +// SRUserApi.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/13. +// Copyright © 2025 SR. All rights reserved. +// + +import Foundation +import Alamofire + + +struct SRUserApi { + + static func requestUserInfo() async -> SRUserInfo? { + + await withCheckedContinuation { continuation in + var param = SRNetwork.Parameters(path: "/customer/info") + param.method = .get + SRNetwork.request(parameters: param) { (response: SRNetwork.Response) in + continuation.resume(returning: response.data) + } + } + } +} diff --git a/SynthReel/Base/Define/SRDefine.swift b/SynthReel/Base/Define/SRDefine.swift new file mode 100644 index 0000000..356f28d --- /dev/null +++ b/SynthReel/Base/Define/SRDefine.swift @@ -0,0 +1,24 @@ +// +// SRDefine.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// Copyright © 2025 SR. All rights reserved. +// +import UIKit + +///app版本号 +let kSRAPPVersion: String = (Bundle.main.infoDictionary!["CFBundleShortVersionString"] as? String) ?? "0" +let kSRAPPBundleVersion: String = (Bundle.main.infoDictionary!["CFBundleVersion"] as? String) ?? "0" + +let kSRAPPBundleName: String = (Bundle.main.infoDictionary!["CFBundleName"] as? String) ?? "" +let kSRAPPName: String = (Bundle.main.infoDictionary!["CFBundleDisplayName"] as? String) ?? "" + + +#if DEBUG +public func srPrint(message: Any? , file: String = #file, function: String = #function, line: Int = #line) { + print("\n\(Date(timeIntervalSinceNow: 8 * 60 * 60)) \(file.components(separatedBy: "/").last ?? "") \(function) \(line): \(message ?? "")") +} +#else +public func srPrint(message: Any?) { } +#endif diff --git a/SynthReel/Base/Define/SRUserDefaultsKey.swift b/SynthReel/Base/Define/SRUserDefaultsKey.swift new file mode 100644 index 0000000..db5c64d --- /dev/null +++ b/SynthReel/Base/Define/SRUserDefaultsKey.swift @@ -0,0 +1,14 @@ +// +// SRUserDefaultsKey.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// Copyright © 2025 SR. All rights reserved. +// + + +///登录token +let kSRAccountTokenDefaultsKey = "kSRAccountTokenDefaultsKey" + +///用户信息 +let kSRUserInfoDefaultsKey = "kSRUserInfoDefaultsKey" diff --git a/SynthReel/Base/Extension/NSNumber+SRAdd.swift b/SynthReel/Base/Extension/NSNumber+SRAdd.swift new file mode 100644 index 0000000..319ebad --- /dev/null +++ b/SynthReel/Base/Extension/NSNumber+SRAdd.swift @@ -0,0 +1,43 @@ +// +// NSNumber+SRAdd.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/17. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +extension NSNumber { + + func toString(maximumFractionDigits: Int = 10, minimumFractionDigits: Int? = nil, roundingMode: NumberFormatter.RoundingMode? = nil) -> String { + let formatter = NumberFormatter() + formatter.minimumIntegerDigits = 1 + formatter.maximumFractionDigits = maximumFractionDigits + if let minimumFractionDigits = minimumFractionDigits { + formatter.minimumFractionDigits = minimumFractionDigits + } + if let roundingMode = roundingMode { + formatter.roundingMode = roundingMode + } + formatter.numberStyle = .none + return formatter.string(from: self) ?? "0" + } +} + +extension Int { + + func formatTimeGroup() -> (String, String, String) { + let seconds = self + + var s: String = "00" + var m: String = "00" + var h: String = "00" + s = String(format: "%02d", Int(Int(seconds) % 60)) + m = String(format: "%02d", Int(seconds / 60) % 60) + h = String(format: "%02d", Int(seconds / 3600)) + + return (h, m, s) + } + +} diff --git a/SynthReel/Base/Extension/String+SRAdd.swift b/SynthReel/Base/Extension/String+SRAdd.swift new file mode 100644 index 0000000..1079396 --- /dev/null +++ b/SynthReel/Base/Extension/String+SRAdd.swift @@ -0,0 +1,26 @@ +// +// String+SRAdd.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/13. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import YYCategories + +extension String { + + var localized: String { + return String(localized: LocalizationValue(self)) + } + + func localizedReplace(text: String) -> String { + return self.localized.replacingOccurrences(of: "##", with: text) + } + + func size(_ font: UIFont, _ size: CGSize) -> CGSize { + return (self as NSString).size(for: font, size: size, mode: .byWordWrapping) + } + +} diff --git a/SynthReel/Base/Extension/UIFont+SRAdd.swift b/SynthReel/Base/Extension/UIFont+SRAdd.swift new file mode 100644 index 0000000..c77e250 --- /dev/null +++ b/SynthReel/Base/Extension/UIFont+SRAdd.swift @@ -0,0 +1,18 @@ +// +// UIFont+SRAdd.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/13. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + + +extension UIFont { + + static func font(ofSize: CGFloat, weight: Weight) -> UIFont { + return .systemFont(ofSize: ofSize, weight: weight) + } + +} diff --git a/SynthReel/Base/Extension/UIScreen+SRAdd.swift b/SynthReel/Base/Extension/UIScreen+SRAdd.swift new file mode 100644 index 0000000..071dadb --- /dev/null +++ b/SynthReel/Base/Extension/UIScreen+SRAdd.swift @@ -0,0 +1,50 @@ +// +// UIScreen+SRAdd.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/13. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +extension UIScreen { + + static var screen: UIScreen { + return UIScreen.main + } + + static var width: CGFloat { + return UIScreen.main.bounds.width + } + + static var height: CGFloat { + return UIScreen.main.bounds.height + } + + static var safeTop: CGFloat { + return SRTool.keyWindow?.safeAreaInsets.top ?? 20 + } + + static var safeBottom: CGFloat { + return SRTool.keyWindow?.safeAreaInsets.bottom ?? 0 + } + + static var navBarHeight: CGFloat { + return safeTop + 44 + } + + static var tabBarHeight: CGFloat { + return safeBottom + 49 + } + + ///屏幕宽比 + static var widthRatio: CGFloat { + return UIScreen.width / 375 + } + + static func getRatioWidth(size: CGFloat) -> CGFloat { + return self.widthRatio * size + } + +} diff --git a/SynthReel/Base/Extension/UIScrollView+SRAdd.swift b/SynthReel/Base/Extension/UIScrollView+SRAdd.swift new file mode 100644 index 0000000..d95a0ec --- /dev/null +++ b/SynthReel/Base/Extension/UIScrollView+SRAdd.swift @@ -0,0 +1,46 @@ +// +// UIScrollView+SRAdd.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/17. +// Copyright © 2025 SR. All rights reserved. +// + +import MJRefresh + +extension UIScrollView { + + func sr_addRefreshHeader(insetTop: CGFloat = 0, block: (() -> Void)?) { + + self.mj_header = MJRefreshNormalHeader(refreshingBlock: { + block?() + }) + self.mj_header?.ignoredScrollViewContentInsetTop = insetTop + } + + func sr_addRefreshFooter(insetBottom: CGFloat = 0, block: (() -> Void)?) { + self.mj_footer = MJRefreshBackNormalFooter(refreshingBlock: { + block?() + }) + self.mj_footer?.ignoredScrollViewContentInsetBottom = insetBottom + } + + + func sr_endHeaderRefreshing() { + self.mj_header?.endRefreshing() + } + + func sr_endFooterRefreshing() { + if self.mj_footer?.state == .noMoreData { return } + self.mj_footer?.endRefreshing() + } + + ///重置没有更多 + func sr_resetNoMoreData() { + self.mj_footer?.resetNoMoreData() + } + + func sr_endRefreshingWithNoMoreData() { + self.mj_footer?.endRefreshingWithNoMoreData() + } +} diff --git a/SynthReel/Base/Extension/UIStackView+SRAdd.swift b/SynthReel/Base/Extension/UIStackView+SRAdd.swift new file mode 100644 index 0000000..9e251bb --- /dev/null +++ b/SynthReel/Base/Extension/UIStackView+SRAdd.swift @@ -0,0 +1,22 @@ +// +// UIStackView+SRAdd.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/15. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +extension UIStackView { + + func sr_removeAllArrangedSubview() { + let arrangedSubviews = self.arrangedSubviews + + arrangedSubviews.forEach { + self.removeArrangedSubview($0) + $0.removeFromSuperview() + } + } +} + diff --git a/SynthReel/Base/Extension/UIView+SRAdd.swift b/SynthReel/Base/Extension/UIView+SRAdd.swift new file mode 100644 index 0000000..df18b12 --- /dev/null +++ b/SynthReel/Base/Extension/UIView+SRAdd.swift @@ -0,0 +1,92 @@ +// +// UIView+SRAdd.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/17. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +extension UIView { + + ///绘制正六边形 + func applyHexagonMask(_ cornerRadius: CGFloat = 0) { + let width = bounds.width + let height = bounds.height + let center = CGPoint(x: width / 2, y: height / 2) + + let radius = max(width, height) / 2 + + let path = UIBezierPath() + + // 六边形 6 个点(平顶) + var points: [CGPoint] = [] + for i in 0..<6 { + let angle = CGFloat(i) * (.pi / 3) + let x = center.x + radius * cos(angle) + let y = center.y + radius * sin(angle) + points.append(CGPoint(x: x, y: y)) + } + + // 创建带圆角的六边形 + for i in 0..<6 { + let prev = points[(i + 5) % 6] // 上一个点 + let curr = points[i] // 当前点 + let next = points[(i + 1) % 6] // 下一个点 + + // 当前点方向的两条边的单位向量 + let v1 = CGPoint(x: curr.x - prev.x, y: curr.y - prev.y) + let v2 = CGPoint(x: curr.x - next.x, y: curr.y - next.y) + + let l1 = sqrt(v1.x * v1.x + v1.y * v1.y) + let l2 = sqrt(v2.x * v2.x + v2.y * v2.y) + + let u1 = CGPoint(x: v1.x / l1, y: v1.y / l1) + let u2 = CGPoint(x: v2.x / l2, y: v2.y / l2) + + // 两边分别往内缩 cornerRadius + let p1 = CGPoint(x: curr.x - u1.x * cornerRadius, + y: curr.y - u1.y * cornerRadius) + let p2 = CGPoint(x: curr.x - u2.x * cornerRadius, + y: curr.y - u2.y * cornerRadius) + + if i == 0 { + path.move(to: p1) + } else { + path.addLine(to: p1) + } + + // 两个偏移点之间加圆角弧线 + path.addQuadCurve(to: p2, controlPoint: curr) + } + + path.close() + + // 设置 mask + let maskLayer = CAShapeLayer() + maskLayer.path = path.cgPath + layer.mask = maskLayer + } + + + func addHollowHole(holePath: UIBezierPath, dimColor: UIColor = UIColor.black.withAlphaComponent(0.6)) { + // 1. 整个遮罩 + let fullPath = UIBezierPath(rect: self.bounds) + + // 2. 挖洞 + fullPath.append(holePath) + fullPath.usesEvenOddFillRule = true + + // 3. 蒙层 layer + let shapeLayer = CAShapeLayer() + shapeLayer.path = fullPath.cgPath + shapeLayer.fillRule = .evenOdd + shapeLayer.fillColor = dimColor.cgColor // 半透明遮罩 + shapeLayer.opacity = 1.0 + +// self.layer.sublayers?.forEach { $0.removeFromSuperlayer() } // 如需避免重复叠加 +// self.layer.addSublayer(shapeLayer) + self.layer.mask = shapeLayer + } +} diff --git a/SynthReel/Base/Extension/UserDefaults+SRAdd.swift b/SynthReel/Base/Extension/UserDefaults+SRAdd.swift new file mode 100644 index 0000000..da70b84 --- /dev/null +++ b/SynthReel/Base/Extension/UserDefaults+SRAdd.swift @@ -0,0 +1,46 @@ +// +// UserDefaults+SRAdd.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// Copyright © 2025 SR. All rights reserved. +// + + +import Foundation + + +extension UserDefaults { + + static func sr_setObject(_ obj: NSSecureCoding?, forKey key: String) { + let defaults = UserDefaults.standard + + guard let obj = obj else { + defaults.removeObject(forKey: key) + return + } + + do { + let data = try NSKeyedArchiver.archivedData(withRootObject: obj, requiringSecureCoding: true) + defaults.set(data, forKey: key) + } catch { + print("Error archiving object: \(error)") + } + } + + static func sr_object(forKey key: String, as type: T.Type) -> T? { + let defaults = UserDefaults.standard + + guard let data = defaults.data(forKey: key) else { + return nil + } + + do { + let object = try NSKeyedUnarchiver.unarchivedObject(ofClass: type, from: data) + return object + } catch { + print("Error unarchiving object: \(error)") + return nil + } + } +} diff --git a/SynthReel/Base/Networking/SRNetwork.swift b/SynthReel/Base/Networking/SRNetwork.swift new file mode 100644 index 0000000..4bf2e21 --- /dev/null +++ b/SynthReel/Base/Networking/SRNetwork.swift @@ -0,0 +1,193 @@ +// +// SRNetwork.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import Moya +import SmartCodable + +class SRNetwork: NSObject { + + private static let operationQueue = OperationQueue() + private static var tokenOperation: BlockOperation? + + static let provider = MoyaProvider() + + + static func request(parameters: SRNetwork.Parameters, completion: ((_ response: SRNetwork.Response) -> Void)?) { + + if SRAccountManager.manager.token == nil { + self.requestToken(completer: nil) + } + + + if let tokenOperation = self.tokenOperation, parameters.path != "/customer/register" { + + let requestOperation = BlockOperation { + let semaphore = DispatchSemaphore(value: 0) + _request(parameters: parameters) { (response: SRNetwork.Response) in + semaphore.signal() + completion?(response) + } + semaphore.wait() + } + ///设置依赖关系 + requestOperation.addDependency(tokenOperation) + + operationQueue.addOperation(requestOperation) + } else { + _request(parameters: parameters, completion: completion) + } + } + + @discardableResult + static func _request(parameters: SRNetwork.Parameters, completion: ((_ response: SRNetwork.Response) -> Void)?) -> Cancellable { + + if parameters.isLoding { + SRHud.show() + } + return provider.request(.request(parameters: parameters)) { result in + if parameters.isLoding { + SRHud.dismiss() + } + guard let completion = completion else {return} + _resultDispose(parameters: parameters, result: result, completion: completion) + } + } + + private static func _resultDispose(parameters: SRNetwork.Parameters, result: Result, completion: ((_ response: SRNetwork.Response) -> Void)?) { + + switch result { + case .success(let response): + let code = response.statusCode + if code == 401 || code == 402 || code == 403 { + + if parameters.path == "/customer/register" { + var res = SRNetwork.Response() + res.code = -1 + if parameters.isToast { +// BRToast.show(text: "Error".localized) + } + completion?(res) + } else { +// if code == 402, parameters.isToast { +// BRToast.show(text: "beereel_network_error_1".localized) +// } + ///重新获取token + self.requestToken { token in + if token != nil { + _Concurrency.Task { + await SRAccountManager.manager.updateUserInfo() + } + } + } + + ///将请求失败数据重新请求 + if let tokenOperation = self.tokenOperation, parameters.path != "/customer/register" { + + let requestOperation = BlockOperation { + let semaphore = DispatchSemaphore(value: 0) + _request(parameters: parameters) { (response: SRNetwork.Response) in + semaphore.signal() + completion?(response) + } + semaphore.wait() + } + ///设置依赖关系 + requestOperation.addDependency(tokenOperation) + + operationQueue.addOperation(requestOperation) + } + } + + + return + } + + do { + let tempData = try response.mapString() + srPrint(message: parameters.parameters) + srPrint(message: parameters.path) + + let response: SRNetwork.Response = _deserialize(data: tempData) + if !response.isSuccess{ + if parameters.isToast { +// BRToast.show(text: response.msg) + } + } + completion?(response) + + } catch { + var res = SRNetwork.Response() + res.code = -1 + if parameters.isToast { +// BRToast.show(text: "Error".localized) + } + completion?(res) + } + case .failure(let error): + srPrint(message: error) + var res = SRNetwork.Response() + res.code = -1 + if parameters.isToast { +// BRToast.show(text: "beereel_network".localized) + } + completion?(res) + break + } + + } + + ///解析数据 + static private func _deserialize(data: String) -> SRNetwork.Response { + var response: SRNetwork.Response? + + let decrypted = SRResponseCryptor.decrypt(data: data) + srPrint(message: decrypted) + response = SRNetwork.Response.deserialize(from: decrypted) + response?.rawData = decrypted + + if let response = response { + return response + } else { + var response = SRNetwork.Response() + response.code = -1 + response.msg = "Error".localized + return response + } + } +} + +extension SRNetwork { + ///获取token + static func requestToken(completer: ((_ token: SRAccountToken?) -> Void)?) { + guard self.tokenOperation == nil else { + completer?(nil) + return + } + + self.tokenOperation = BlockOperation(block: { + let semaphore = DispatchSemaphore(value: 0) + let param = SRNetwork.Parameters(path: "/customer/register") + + DispatchQueue.main.async { + SRNetwork.request(parameters: param) { (response: SRNetwork.Response) in + if let token = response.data { + SRAccountManager.manager.setAccountToken(token) + } + do { semaphore.signal() } + self.tokenOperation = nil + completer?(response.data) + } + } + semaphore.wait() + }) + operationQueue.addOperation(self.tokenOperation!) + } + + +} diff --git a/SynthReel/Base/Networking/SRNetworkModel.swift b/SynthReel/Base/Networking/SRNetworkModel.swift new file mode 100644 index 0000000..755634d --- /dev/null +++ b/SynthReel/Base/Networking/SRNetworkModel.swift @@ -0,0 +1,62 @@ +// +// SRNetworkModel.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// Copyright © 2025 SR. All rights reserved. +// + + +import UIKit +import SmartCodable +import Moya +import Alamofire + + +extension SRNetwork { + + struct Parameters { + var baseURL: URL? + var parameters: [String : Any]? + var method: Moya.Method = .post + var path: String + var isLoding: Bool = false + var isToast: Bool = true + } + + struct Response: SmartCodable { + + var code: Int? + var data: T? + var msg: String? + + @SmartIgnored + var rawData: Any? + + var isSuccess: Bool { + return code == 200 + } + } + + struct List: SmartCodable { + var list: [T]? + var pagination: Pagination? + } + + struct Pagination: SmartCodable { + var current_page: Int? + var page_size: Int? + var page_total: Int? + var total_size: Int? + } +} + + + +//extension String: SmartCodable { +// +//} +// +//extension Int: SmartCodable { +// +//} diff --git a/SynthReel/Base/Networking/SRNetworkReachableManager.swift b/SynthReel/Base/Networking/SRNetworkReachableManager.swift new file mode 100644 index 0000000..d98f92a --- /dev/null +++ b/SynthReel/Base/Networking/SRNetworkReachableManager.swift @@ -0,0 +1,75 @@ +// +// SRNetworkReachableManager.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import Network + + +class SRNetworkReachableManager: NSObject { + + static let manager = SRNetworkReachableManager() + ///是否有网 + var isReachable: Bool? + + private var connectionType: NWInterface.InterfaceType? + private var status: NWPath.Status? + + private let monitor = NWPathMonitor() + private let queue = DispatchQueue(label: "NetworkMonitorQueue") + + func startMonitoring() { + + monitor.pathUpdateHandler = { [weak self] path in + guard let self = self else { return } + self.status = path.status + + if path.usesInterfaceType(.wifi) { + self.connectionType = .wifi + } else if path.usesInterfaceType(.cellular) { + self.connectionType = .cellular + } else if path.usesInterfaceType(.wiredEthernet) { + self.connectionType = .wiredEthernet + } else { + self.connectionType = nil + } + + + let agoReachable = self.isReachable + + if path.status == .satisfied, self.connectionType != nil { + self.isReachable = true + if agoReachable == false { + DispatchQueue.main.async { + NotificationCenter.default.post(name: SRNetworkReachableManager.networkStatusDidChangeNotification, object: nil) + } + } + + } else { + self.isReachable = false + if agoReachable == true { + DispatchQueue.main.async { + NotificationCenter.default.post(name: SRNetworkReachableManager.networkStatusDidChangeNotification, object: nil) + } + } + } + } + + + monitor.start(queue: queue) + } + + func stopMonitoring() { + monitor.cancel() + } + +} + +extension SRNetworkReachableManager { + ///网络发生变化 + @objc static let networkStatusDidChangeNotification = NSNotification.Name(rawValue: "SRNetworkReachableManager.networkStatusDidChangeNotification") +} diff --git a/SynthReel/Base/Networking/SRResponseCryptor.swift b/SynthReel/Base/Networking/SRResponseCryptor.swift new file mode 100644 index 0000000..303dec7 --- /dev/null +++ b/SynthReel/Base/Networking/SRResponseCryptor.swift @@ -0,0 +1,95 @@ +// +// SRResponseCryptor.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +struct SRResponseCryptor { + + static func decrypt(data: String) -> String { + guard data.hasPrefix("$") else { + return data + } + + let decryptedData = deStrBytes(data: data) + return String(data: decryptedData, encoding: .utf8) ?? "" + } + + static func deStrBytes(data: String) -> Data { + let hexData = String(data.dropFirst()) + var bytes = Data() + + var index = hexData.startIndex + while index < hexData.endIndex { + let nextIndex = hexData.index(index, offsetBy: 2, limitedBy: hexData.endIndex) ?? hexData.endIndex + let byteString = String(hexData[index.. Data { + guard !data.isEmpty else { + return data + } + + let saltLen = Int(data[data.startIndex]) + guard data.count >= 1 + saltLen else { + return data + } + + let salt = data.subdata(in: 1..<1+saltLen) + let encryptedData = data.subdata(in: 1+saltLen.. Data { + let decryptedData = cxEd(data: data) + return removeSalt(data: decryptedData, salt: salt) + } + + // 加密/解密数据(按位取反) + static func cxEd(data: Data) -> Data { + return Data(data.map { $0 ^ 0xFF }) + } + + // 从数据中移除盐值 + static func removeSalt(data: Data, salt: Data) -> Data { + guard !salt.isEmpty else { + return data + } + + var result = Data() + let saltBytes = [UInt8](salt) + let saltCount = saltBytes.count + + for (index, byte) in data.enumerated() { + let saltByte = saltBytes[index % saltCount] + let decryptedByte = calRemoveSalt(v: byte, s: saltByte) + result.append(decryptedByte) + } + return result + } + + // 计算移除盐值后的字节 + static func calRemoveSalt(v: UInt8, s: UInt8) -> UInt8 { + if v >= s { + return v - s + } else { + return UInt8(0xFF) - (s - v) + 1 + } + } +} diff --git a/SynthReel/Base/Networking/SRTargetType.swift b/SynthReel/Base/Networking/SRTargetType.swift new file mode 100644 index 0000000..6a4dd6c --- /dev/null +++ b/SynthReel/Base/Networking/SRTargetType.swift @@ -0,0 +1,91 @@ +// +// SRTargetType.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SmartCodable +import Moya +import Alamofire +import AdSupport +import YYCategories + +enum SRTargetType { + case request(parameters: SRNetwork.Parameters) +} + +extension SRTargetType: TargetType { + var baseURL: URL { + return .init(string: SRBaseURL)! + } + + var path: String { + switch self { + case .request(let param): + return SRUrlPathPrefix + param.path + } + } + + var method: Moya.Method { + switch self { + case .request(let param): + return param.method + } + } + + var task: Moya.Task { + switch self { + case .request(let param): + let parameters = param.parameters ?? [:] + return .requestParameters(parameters: parameters, encoding: getEncoding()) + } + } + + var headers: [String : String]? { + var dic: [String : String] = [ + "system-version" : UIDevice.current.systemVersion, + "lang-key" : "en", + "idfa" : ASIdentifierManager.shared().advertisingIdentifier.uuidString, + "time-zone" : SRTargetType.timeZone(), //时区 + "brand" : "apple", //品牌 + "app-version" : kSRAPPVersion, + "app-name" : "SynthReel", + "device-id" : SRDeviceId.shared.id, //设备id + "system-type" : "ios", + "model" : UIDevice.current.machineModelName ?? "", + "authorization" : SRAccountManager.manager.token?.token ?? "", + "device-gaid" : UIDevice.current.identifierForVendor?.uuidString ?? "", + "product-prefix" : "SynthReel" + ] +#if DEBUG + dic["security"] = "false" +#endif + + return dic + } + +} + + +extension SRTargetType { + + var sampleData: Data { return "".data(using: String.Encoding.utf8)! } + + func getEncoding() -> ParameterEncoding { + switch self.method { + case .get, .delete: + return URLEncoding.default + default: + return JSONEncoding.default + } + } + + static func timeZone() -> String { + let timeZone = NSTimeZone.local as NSTimeZone + let timeZoneSecondsFromGMT = timeZone.secondsFromGMT / 3600 + return String(format: "GMT+0%d:00", timeZoneSecondsFromGMT) + } +} diff --git a/SynthReel/Base/Networking/SRUrlPath.swift b/SynthReel/Base/Networking/SRUrlPath.swift new file mode 100644 index 0000000..f015e90 --- /dev/null +++ b/SynthReel/Base/Networking/SRUrlPath.swift @@ -0,0 +1,27 @@ +// +// SRUrlPath.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// Copyright © 2025 SR. All rights reserved. +// + +let SRBaseURL = "https://api-hbqinjiu.hbqinjiu.com" +let SRUrlPathPrefix = "/eon" +let SRWebBaseURL = "https://www.hbqinjiu.com" +let SRCampaignWebURL = "https://campaign.hbqinjiu.com" + + +/* + SynthReel + admin-api https://admin-api-synthreeltv.synthreeltv.com + api https://api-synthreeltv.synthreeltv.com/th + + + 官网:https://www.synthreeltv.com + 协议页面:https://www.synthreeltv.com/xxxxx + 反馈:https://campaign.synthreeltv.com/pages/leave/index 传{theme:theme_16} + 注销:https://campaign.synthreeltv.com/pages/setting/logout 传参{theme: theme_11, device-id:xxxx} + w2a:https://w2a.synthreeltv.com/ + Api:https://api-synthreeltv.synthreeltv.com/th + */ diff --git a/SynthReel/Base/View/SRCollectionView.swift b/SynthReel/Base/View/SRCollectionView.swift new file mode 100644 index 0000000..e0b13ff --- /dev/null +++ b/SynthReel/Base/View/SRCollectionView.swift @@ -0,0 +1,24 @@ +// +// SRCollectionView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/14. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +class SRCollectionView: UICollectionView { + + override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { + super.init(frame: frame, collectionViewLayout: layout) + self.backgroundColor = .clear + self.contentInsetAdjustmentBehavior = .never + } + + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/SynthReel/Base/View/SRGradientView.swift b/SynthReel/Base/View/SRGradientView.swift new file mode 100644 index 0000000..8a48cf1 --- /dev/null +++ b/SynthReel/Base/View/SRGradientView.swift @@ -0,0 +1,45 @@ +// +// SRGradientView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/15. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +class SRGradientView: UIView { + + override class var layerClass: AnyClass { + return CAGradientLayer.self + } + + var gradientLayer: CAGradientLayer { + return self.layer as! CAGradientLayer + } + + var locations: [NSNumber]? { + didSet { + self.gradientLayer.locations = locations + } + } + + var colors: [CGColor]? { + didSet { + self.gradientLayer.colors = colors + } + } + + var startPoint: CGPoint = .zero { + didSet { + self.gradientLayer.startPoint = startPoint + } + } + + var endPoint: CGPoint = .zero { + didSet { + self.gradientLayer.endPoint = endPoint + } + } + +} diff --git a/SynthReel/Base/View/SRImageView.swift b/SynthReel/Base/View/SRImageView.swift new file mode 100644 index 0000000..5b9bb99 --- /dev/null +++ b/SynthReel/Base/View/SRImageView.swift @@ -0,0 +1,100 @@ +// +// SRImageView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/15. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import Kingfisher + +class SRImageView: UIImageView { + + var placeholderColor = UIColor.gray + var placeholderImage = UIImage(named: "placeholder_image") + + private lazy var placeholderImageView: UIImageView = { + let imageView = UIImageView(image: placeholderImage) + imageView.isHidden = true + imageView.contentMode = .scaleAspectFit + return imageView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + _init() + } + + override init(image: UIImage?) { + super.init(image: image) + _init() + } + + override init(image: UIImage?, highlightedImage: UIImage?) { + super.init(image: image, highlightedImage: highlightedImage) + _init() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + _init() + } + + override func awakeFromNib() { + super.awakeFromNib() + _init() + } + + func _init() { + self.contentMode = .scaleAspectFill + self.layer.masksToBounds = true + if image == nil { + self.backgroundColor = self.placeholderColor + placeholderImageView.isHidden = false + } + addSubview(placeholderImageView) + } + + override var image: UIImage? { + didSet { + if self.backgroundColor == nil && image == nil { + self.backgroundColor = self.placeholderColor + } else if image != nil { + if self.backgroundColor == self.placeholderColor { + self.backgroundColor = nil + } + } + + if image == nil { + placeholderImageView.isHidden = false + } else { + placeholderImageView.isHidden = true + } + } + } + + override func layoutSubviews() { + super.layoutSubviews() + + placeholderImageView.frame = .init(x: 0, y: 0, width: self.bounds.width * (2 / 3), height: self.bounds.height * (2 / 3)) + placeholderImageView.center = .init(x: self.bounds.width / 2, y: self.bounds.height / 2) + } + +} + + +extension UIImageView { + func sr_setImage(_ url: String?, placeholder: UIImage? = nil, completer: ((_ image: UIImage?, _ url: URL?) -> Void)? = nil) { + + self.kf.setImage(with: URL(string: url ?? ""), placeholder: placeholder, options: nil) { result in + switch result { + case .success(let value): + completer?(value.image, value.source.url) + default : + completer?(nil, nil) + break + } + } + } +} diff --git a/SynthReel/Base/View/SRLabel.swift b/SynthReel/Base/View/SRLabel.swift new file mode 100644 index 0000000..41b8fad --- /dev/null +++ b/SynthReel/Base/View/SRLabel.swift @@ -0,0 +1,55 @@ +// +// SRLabel.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/14. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import YYText + +class SRLabel: UILabel { + + var textColors: [CGColor]? + var textStartPoint: CGPoint? + var textEndPoint: CGPoint? + + + override func layoutSubviews() { + super.layoutSubviews() + + let size = self.bounds.size + if let text = self.text, text.count > 0, let colors = self.textColors, let startPoint = self.textStartPoint, let endPoine = self.textEndPoint { + self.textColor = UIColor(patternImage: UIImage.sr_getGradientImage(size: size, colors: colors, startPoint: startPoint, endPoint: endPoine)) + } + } + + +} + +extension UIImage { + + static func sr_getGradientImage(size: CGSize, colors: [CGColor], startPoint: CGPoint, endPoint: CGPoint) -> UIImage{ + + UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale) + guard let context = UIGraphicsGetCurrentContext() else{return UIImage()} + let colorSpace = CGColorSpaceCreateDeviceRGB() + ///设置渐变颜色 + let gradientRef = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: nil)! + let startPoint = CGPoint(x: size.width * startPoint.x, y: size.height * startPoint.y) + let endPoint = CGPoint(x: size.width * endPoint.x, y: size.height * endPoint.y) + context.drawLinearGradient(gradientRef, start: startPoint, end: endPoint, options: CGGradientDrawingOptions(arrayLiteral: .drawsBeforeStartLocation,.drawsAfterEndLocation)) + let gradientImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return gradientImage ?? UIImage() + } + + func sr_resized(to size: CGSize) -> UIImage { + let renderer = UIGraphicsImageRenderer(size: size) + return renderer.image { _ in + self.draw(in: CGRect(origin: .zero, size: size)) + } + } + +} diff --git a/SynthReel/Base/View/SRPanModalContentView.swift b/SynthReel/Base/View/SRPanModalContentView.swift new file mode 100644 index 0000000..07604c3 --- /dev/null +++ b/SynthReel/Base/View/SRPanModalContentView.swift @@ -0,0 +1,84 @@ +// +// SRPanModalContentView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import HWPanModal +import SnapKit + +class SRPanModalContentView: HWPanModalContentView { + + + var contentHeight = UIScreen.height * (2 / 3) + + ///更新UI contentSize发生变化时调用 + func setNeedsLayoutUpdate() { + self.panModalSetNeedsLayoutUpdate() + } + + lazy var bgImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "pan_bg_image_01")) + return imageView + }() + + + override init(frame: CGRect) { + super.init(frame: frame) + addSubview(bgImageView) + + bgImageView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + //MARK: HWPanModalPresentable + override func panScrollable() -> UIScrollView? { + return nil + } + + override func longFormHeight() -> PanModalHeight { + return PanModalHeightMake(.content, contentHeight) + } + + override func showDragIndicator() -> Bool { + return false + } + + override func backgroundConfig() -> HWBackgroundConfig { + let config = HWBackgroundConfig() + config.backgroundAlpha = 0.6 + return config + } + + override func allowsTapBackgroundToDismiss() -> Bool { + return true + } + + override func allowsDragToDismiss() -> Bool { + return false + } + + override func allowsPullDownWhenShortState() -> Bool { + return false + } + + override func showsScrollableVerticalScrollIndicator() -> Bool { + return false + } + + override func springDamping() -> CGFloat { + return 1 + } + + override func cornerRadius() -> CGFloat { + return 0 + } +} diff --git a/SynthReel/Base/View/SRScrollView.swift b/SynthReel/Base/View/SRScrollView.swift new file mode 100644 index 0000000..4d3470b --- /dev/null +++ b/SynthReel/Base/View/SRScrollView.swift @@ -0,0 +1,21 @@ +// +// SRScrollView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/15. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +class SRScrollView: UIScrollView { + + override init(frame: CGRect) { + super.init(frame: frame) + self.contentInsetAdjustmentBehavior = .never + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } +} diff --git a/SynthReel/Base/View/SRTableView.swift b/SynthReel/Base/View/SRTableView.swift new file mode 100644 index 0000000..b1f62ba --- /dev/null +++ b/SynthReel/Base/View/SRTableView.swift @@ -0,0 +1,49 @@ +// +// SRTableView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +class SRTableView: UITableView { + + var insetGroupedMargins: CGFloat = 15 + + override init(frame: CGRect, style: UITableView.Style) { + super.init(frame: frame, style: style) + separatorInset = .init(top: 0, left: 15, bottom: 0, right: 15) + self.backgroundColor = .clear + self.contentInsetAdjustmentBehavior = .never + + if style == .insetGrouped || style == .grouped { + sectionFooterHeight = 14 + sectionHeaderHeight = 0.1 + } else if style == .plain { + if #available(iOS 15.0, *) { + sectionHeaderTopPadding = 0 + } + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override var layoutMargins: UIEdgeInsets { + set { + super.layoutMargins = newValue + } + get { + var margins = super.layoutMargins + if self.style == .insetGrouped { + margins.left = self.safeAreaInsets.left + insetGroupedMargins + margins.right = self.safeAreaInsets.right + insetGroupedMargins + } + return margins + } + } + +} diff --git a/SynthReel/Base/View/SRTableViewCell.swift b/SynthReel/Base/View/SRTableViewCell.swift new file mode 100644 index 0000000..87b0307 --- /dev/null +++ b/SynthReel/Base/View/SRTableViewCell.swift @@ -0,0 +1,48 @@ +// +// SRTableViewCell.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +class SRTableViewCell: UITableViewCell { + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + _init() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + _init() + } + + override func awakeFromNib() { + super.awakeFromNib() + } + + override func setSelected(_ selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } + + + private func _init() { + self.layer.rasterizationScale = UIScreen.main.scale + self.layer.shouldRasterize = true + self.selectionStyle = .none + self.backgroundColor = .clear + } + +} + +extension UITableViewCell { + + var fa_tableView: UITableView? { + return self.value(forKey: "_tableView") as? UITableView + } +} diff --git a/SynthReel/Base/ViewController/SRNavigationController.swift b/SynthReel/Base/ViewController/SRNavigationController.swift new file mode 100644 index 0000000..33453e2 --- /dev/null +++ b/SynthReel/Base/ViewController/SRNavigationController.swift @@ -0,0 +1,36 @@ +// +// SRNavigationController.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/13. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +class SRNavigationController: UINavigationController { + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + + override func pushViewController(_ viewController: UIViewController, animated: Bool) { + if children.count > 0 { + viewController.hidesBottomBarWhenPushed = true + } + super.pushViewController(viewController, animated: animated) + } + + override func setViewControllers(_ viewControllers: [UIViewController], animated: Bool) { + for (index, value) in viewControllers.enumerated() { + if index != 0 { + value.hidesBottomBarWhenPushed = true + } + } + super.setViewControllers(viewControllers, animated: animated) + } + +} diff --git a/SynthReel/Base/ViewController/SRTabBarController.swift b/SynthReel/Base/ViewController/SRTabBarController.swift new file mode 100644 index 0000000..e90e470 --- /dev/null +++ b/SynthReel/Base/ViewController/SRTabBarController.swift @@ -0,0 +1,51 @@ +// +// SRTabBarController.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/13. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import ESTabBarController + +class SRTabBarController: ESTabBarController { + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = ._010101 + + let nav1 = createNavigationView(SRHomeViewController(), image: UIImage(named: "tabbar_icon_01"), selectedImage: UIImage(named: "tabbar_icon_01_selected")) + let nav2 = createNavigationView(SRRecommendPlayerViewController(), image: UIImage(named: "tabbar_icon_02"), selectedImage: UIImage(named: "tabbar_icon_02_selected")) + let nav3 = createNavigationView(SRMyShortViewController(), image: UIImage(named: "tabbar_icon_03"), selectedImage: UIImage(named: "tabbar_icon_03_selected")) + let nav4 = createNavigationView(SRViewController(), image: UIImage(named: "tabbar_icon_04"), selectedImage: UIImage(named: "tabbar_icon_04_selected")) + + + viewControllers = [nav1, nav2, nav3, nav4] + + let appearance = UITabBarAppearance() + appearance.backgroundColor = ._010101 + appearance.shadowColor = .clear + appearance.backgroundImage = UIImage() + appearance.shadowImage = UIImage() + + self.tabBar.standardAppearance = appearance + self.tabBar.scrollEdgeAppearance = appearance + self.tabBar.isTranslucent = false + } + + + func createNavigationView(_ viewController: UIViewController, image: UIImage?, selectedImage: UIImage?) -> UINavigationController { + + let contentView = ESTabBarItemContentView() + contentView.itemContentMode = .alwaysOriginal + contentView.renderingMode = .alwaysOriginal + + let tabBarItem = ESTabBarItem(contentView, image: image, selectedImage: selectedImage) + + let nav = SRNavigationController(rootViewController: viewController) + nav.tabBarItem = tabBarItem + return nav + } + +} diff --git a/SynthReel/Base/ViewController/SRViewController.swift b/SynthReel/Base/ViewController/SRViewController.swift new file mode 100644 index 0000000..17d0c70 --- /dev/null +++ b/SynthReel/Base/ViewController/SRViewController.swift @@ -0,0 +1,82 @@ +// +// SRViewController.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/13. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit +import JXPagingView + +class SRViewController: UIViewController { + + var didScrollCallback: ((_ : UIScrollView) -> Void)? + + lazy var backgroundImageView = UIImageView(image: UIImage(named: "background_image_01")) + + override func viewDidLoad() { + super.viewDidLoad() + self.edgesForExtendedLayout = [.top] + self.view.backgroundColor = ._010101 + + view.addSubview(backgroundImageView) + + backgroundImageView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + } + } + + func handleHeaderRefresh(_ completer: (() -> Void)?) { + completer?() + } + + func handleFooterRefresh(_ completer: (() -> Void)?) { + completer?() + } + +} + +//MARK: JXPagingSmoothViewListViewDelegate +extension SRViewController: JXPagingSmoothViewListViewDelegate, JXPagingViewListViewDelegate { + func listViewDidScrollCallback(callback: @escaping (UIScrollView) -> ()) { + self.didScrollCallback = callback + } + + func listView() -> UIView { + return self.view + } + + func listScrollView() -> UIScrollView { + return UIScrollView() + } +} + +extension UIViewController { +// func configNavigationBack(_ imageName: String = "Frame 3011") { +// let image = UIImage(named: imageName) +// +// let leftBarButtonItem = UIBarButtonItem(image: image, style: .plain ,target: self,action: #selector(handleNavigationBack)) +// navigationItem.leftBarButtonItem = leftBarButtonItem +// } + + @objc func sr_handleNavigationBack() { + self.sr_toLastViewController(animated: true) + } + + func sr_toLastViewController(animated: Bool) { + if self.navigationController != nil + { + if self.navigationController?.viewControllers.count == 1 + { + self.dismiss(animated: animated, completion: nil) + } else { + self.navigationController?.popViewController(animated: animated) + } + } + else if self.presentingViewController != nil { + self.dismiss(animated: animated, completion: nil) + } + } +} diff --git a/SynthReel/Class/Home/M/SRCategoryModel.swift b/SynthReel/Class/Home/M/SRCategoryModel.swift new file mode 100644 index 0000000..5b2c67b --- /dev/null +++ b/SynthReel/Class/Home/M/SRCategoryModel.swift @@ -0,0 +1,16 @@ +// +// SRCategoryModel.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/14. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SmartCodable + +struct SRCategoryModel: SmartCodable { + + var id: String? + var name: String? +} diff --git a/SynthReel/Class/Home/M/SRHomeModuleItem.swift b/SynthReel/Class/Home/M/SRHomeModuleItem.swift new file mode 100644 index 0000000..90f1ad3 --- /dev/null +++ b/SynthReel/Class/Home/M/SRHomeModuleItem.swift @@ -0,0 +1,56 @@ +// +// SRHomeModuleItem.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/14. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SmartCodable + +class SRHomeModuleItem: NSObject, SmartCodable { + + required override init() { } + + enum ModuleKey: String, SmartCaseDefaultable { + case banner = "home_banner" + ///猜你喜欢 + case detailsRecommand = "get_details_recommand" + case popular = "home_v3_recommand" + case updates = "week_ranking" + case bingeWorthy = "week_highest_recommend" + case viralHits = "highest_payment_hot_video" + case premiereNow = "new_recommand" + } + + + var title: String? + var module_key: ModuleKey? + var list: [SRShortModel] = [] + + @SmartAny + var data: Any? + + + func didFinishMapping() { + if let data = data as? [[String : Any]] { + self.list = [SRShortModel].deserialize(from: data) ?? [] + } else if let data = data as? [String : Any] { + var dataList: [[String : Any]]? + if let list = data["list"] as? [[String : Any]] { + self.title = data["title"] as? String + dataList = list + + } else if let list = data["shortPlayList"] as? [[String : Any]] { + self.title = data["category_name"] as? String + dataList = list + } + + if let dataList = dataList { + self.list = [SRShortModel].deserialize(from: dataList) ?? [] + } + } + } +} + diff --git a/SynthReel/Class/Home/V/SRHomeBannerCell.swift b/SynthReel/Class/Home/V/SRHomeBannerCell.swift new file mode 100644 index 0000000..2d8e9f0 --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeBannerCell.swift @@ -0,0 +1,42 @@ +// +// SRHomeBannerCell.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/15. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import FSPagerView +import SnapKit + +class SRHomeBannerCell: FSPagerViewCell { + + var model: SRShortModel? { + didSet { + if let image = model?.horizontally_img, image.count > 0 { + coverImageView.sr_setImage(image) + } else { + coverImageView.sr_setImage(model?.image_url) + } + } + } + + lazy var coverImageView: SRImageView = { + let imageView = SRImageView() + return imageView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + contentView.addSubview(coverImageView) + + coverImageView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + + @MainActor required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/SynthReel/Class/Home/V/SRHomeBannerMiniCell.swift b/SynthReel/Class/Home/V/SRHomeBannerMiniCell.swift new file mode 100644 index 0000000..c8d56d3 --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeBannerMiniCell.swift @@ -0,0 +1,40 @@ +// +// SRHomeBannerMiniCell.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/17. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRHomeBannerMiniCell: UICollectionViewCell { + + var model: SRShortModel? { + didSet { + coverImageView.sr_setImage(model?.image_url) + } + } + + lazy var coverImageView: SRImageView = { + let imageView = SRImageView() + imageView.layer.cornerRadius = 2 + imageView.layer.masksToBounds = true + return imageView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + contentView.addSubview(coverImageView) + + coverImageView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/SynthReel/Class/Home/V/SRHomeBannerView.swift b/SynthReel/Class/Home/V/SRHomeBannerView.swift new file mode 100644 index 0000000..460c680 --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeBannerView.swift @@ -0,0 +1,177 @@ +// +// SRHomeBannerView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/15. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit +import FSPagerView + +class SRHomeBannerView: UIView { + + var dataArr: [SRShortModel]? { + didSet { + self.bannerView.reloadData() + self.miniCollectionView.reloadData() + } + } + + var didSelectedShort: ((_ model: SRShortModel?) -> Void)? + + lazy var bgImageView = UIImageView(image: UIImage(named: "home_banner_bg")) + + lazy var bannerView: FSPagerView = { + let view = FSPagerView() + view.delegate = self + view.dataSource = self + view.isInfinite = true + view.automaticSlidingInterval = 5 + view.register(SRHomeBannerCell.self, forCellWithReuseIdentifier: "cell") + return view + }() + +// lazy var miniBgView: UIView = { +// let view = UIView() +// view.layer.cornerRadius = 4 +// view.layer.masksToBounds = true +// view.backgroundColor = ._051_B_22.withAlphaComponent(0.5) +// return view +// }() + + lazy var miniCollectionViewLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.itemSize = .init(width: 36, height: 46) + layout.minimumLineSpacing = 4 + return layout + }() + + lazy var miniCollectionView: SRCollectionView = { + let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: miniCollectionViewLayout) + collectionView.layer.cornerRadius = 4 + collectionView.layer.masksToBounds = true + collectionView.backgroundColor = ._051_B_22.withAlphaComponent(0.5) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.showsVerticalScrollIndicator = false + collectionView.contentInset = .init(top: 5, left: 0, bottom: 5, right: 0) + collectionView.register(SRHomeBannerMiniCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + sr_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + applyHeteromorphicLayer() + } + + ///添加异形切边 + func applyHeteromorphicLayer() { + let viewSize = bannerView.bounds.size + let path = UIBezierPath() + let layer = CAShapeLayer() + + //右上角边长 + let topRight = UIScreen.getRatioWidth(size: 25) + //左下角边长 + let bottomLeft = UIScreen.getRatioWidth(size: 10) + + path.move(to: .init(x: 0, y: 0)) + path.addLine(to: .init(x: viewSize.width - topRight, y: 0)) + path.addLine(to: .init(x: viewSize.width, y: topRight)) + path.addLine(to: .init(x: viewSize.width, y: viewSize.height)) + path.addLine(to: .init(x: bottomLeft, y: viewSize.height)) + path.addLine(to: .init(x: 0, y: viewSize.height - bottomLeft)) + path.close() + + layer.path = path.cgPath + bannerView.layer.mask = layer + } + +} + +extension SRHomeBannerView { + +func sr_setupUI() { + let bgImage = self.bgImageView.image! + let bgWidth = UIScreen.width - 30 + let bgHeight = bgImage.size.height / bgImage.size.width * bgWidth + + addSubview(bgImageView) + addSubview(bannerView) + addSubview(miniCollectionView) + + bgImageView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.right.equalToSuperview().offset(-15) + make.top.equalToSuperview() + make.bottom.equalToSuperview() + make.height.equalTo(bgHeight) + } + + bannerView.snp.makeConstraints { make in + make.left.equalTo(bgImageView).offset(11) + make.right.equalTo(bgImageView).offset(-11) + make.top.equalTo(bgImageView).offset(18) + make.bottom.equalTo(bgImageView).offset(-12) + } + + miniCollectionView.snp.makeConstraints { make in + make.left.equalTo(bannerView).offset(7) + make.centerY.equalTo(bannerView) + make.top.equalTo(bannerView).offset(7) + make.width.equalTo(46) + } + } +} + +//MARK: FSPagerViewDelegate FSPagerViewDataSource +extension SRHomeBannerView: FSPagerViewDelegate, FSPagerViewDataSource { + func numberOfItems(in pagerView: FSPagerView) -> Int { + return self.dataArr?.count ?? 0 + } + + func pagerView(_ pagerView: FSPagerView, cellForItemAt index: Int) -> FSPagerViewCell { + let cell = pagerView.dequeueReusableCell(withReuseIdentifier: "cell", at: index) as! SRHomeBannerCell + cell.model = self.dataArr?[index] + return cell + } + + + func pagerView(_ pagerView: FSPagerView, didSelectItemAt index: Int) { + self.didSelectedShort?(self.dataArr?[index]) + } + + +} + +//MARK: UICollectionViewDelegate UICollectionViewDataSource +extension SRHomeBannerView: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRHomeBannerMiniCell + cell.model = self.dataArr?[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.dataArr?.count ?? 0 + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + if indexPath.row == self.bannerView.currentIndex { return } + + self.bannerView.selectItem(at: indexPath.row, animated: true) + } + +} diff --git a/SynthReel/Class/Home/V/SRHomeBingeWorthyCell.swift b/SynthReel/Class/Home/V/SRHomeBingeWorthyCell.swift new file mode 100644 index 0000000..5467fd4 --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeBingeWorthyCell.swift @@ -0,0 +1,135 @@ +// +// SRHomeBingeWorthyCell.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/17. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRHomeBingeWorthyCell: UICollectionViewCell { + + var model: SRShortModel? { + didSet { + coverImageView.sr_setImage(model?.image_url) + titleLabel.text = model?.name + desLabel.text = model?.sr_description + if let text = model?.category?.first, text.count > 0 { + categoryLabel.text = "#\(text)" + } else { + categoryLabel.text = nil + } + } + } + + lazy var bgImageView = UIImageView(image: UIImage(named: "home_binge_worthy_cell_image")) + + lazy var coverImageView: SRImageView = { + let imageView = SRImageView() + imageView.layer.cornerRadius = 4 + imageView.layer.masksToBounds = true + return imageView + }() + + lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .medium) + label.textColor = .white + return label + }() + + lazy var categoryLabel: SRLabel = { + let label = SRLabel() + label.font = .font(ofSize: 11, weight: .regular) + label.textColors = [UIColor.srGreen.cgColor, UIColor.srBlue.cgColor] + label.textStartPoint = .init(x: 0.5, y: 0) + label.textEndPoint = .init(x: 0.5, y: 1) + return label + }() + + lazy var desLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 11, weight: .regular) + label.textColor = .A_6_A_6_A_6 + label.numberOfLines = 3 + return label + }() + + lazy var playImageView = UIImageView(image: UIImage(named: "play_icon_01")) + + lazy var playLabel: UILabel = { + let label = SRLabel() + label.font = .font(ofSize: 11, weight: .regular) + label.textColors = [UIColor.srGreen.cgColor, UIColor.srBlue.cgColor] + label.textStartPoint = .init(x: 0.5, y: 0) + label.textEndPoint = .init(x: 0.5, y: 1) + label.text = "Play" + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + sr_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension SRHomeBingeWorthyCell { + + private func sr_setupUI() { + contentView.addSubview(bgImageView) + contentView.addSubview(coverImageView) + contentView.addSubview(titleLabel) + contentView.addSubview(categoryLabel) + contentView.addSubview(desLabel) + contentView.addSubview(playImageView) + playImageView.addSubview(playLabel) + + bgImageView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + coverImageView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(10) + make.top.equalToSuperview().offset(10) + make.width.equalTo(126) + make.height.equalTo(168) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalTo(coverImageView.snp.right).offset(11) + make.top.equalToSuperview().offset(27) + make.right.lessThanOrEqualToSuperview().offset(-15) + } + + categoryLabel.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.top.equalTo(titleLabel.snp.bottom).offset(12) + make.right.lessThanOrEqualToSuperview().offset(-15) + } + + desLabel.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.top.equalTo(titleLabel.snp.bottom).offset(43) + make.right.lessThanOrEqualToSuperview().offset(-15) + } + + playImageView.snp.makeConstraints { make in + make.left.equalTo(coverImageView.snp.right).offset(11) + make.bottom.equalTo(coverImageView).offset(1) + } + + playLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(45) + make.top.equalToSuperview().offset(9) + } + } + +} diff --git a/SynthReel/Class/Home/V/SRHomeBingeWorthyView.swift b/SynthReel/Class/Home/V/SRHomeBingeWorthyView.swift new file mode 100644 index 0000000..2c50343 --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeBingeWorthyView.swift @@ -0,0 +1,75 @@ +// +// SRHomeBingeWorthyView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/17. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRHomeBingeWorthyView: SRHomeModuleView { + + var dataArr: [SRShortModel]? { + didSet { + collectionView.reloadData() + } + } + + lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + layout.minimumLineSpacing = 12 + layout.itemSize = .init(width: 320, height: 192) + return layout + }() + + lazy var collectionView: SRCollectionView = { + let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.showsHorizontalScrollIndicator = false + collectionView.contentInset = .init(top: 0, left: 15, bottom: 0, right: 15) + collectionView.register(SRHomeBingeWorthyCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + self.titleLabel.text = "Binge-Worthy".localized + + addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalTo(button.snp.bottom).offset(6) + make.height.equalTo(collectionViewLayout.itemSize.height) + } + + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + +} + +//MARK: UICollectionViewDataSource UICollectionViewDelegate +extension SRHomeBingeWorthyView: UICollectionViewDataSource, UICollectionViewDelegate { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRHomeBingeWorthyCell + cell.model = self.dataArr?[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.dataArr?.count ?? 0 + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + self.didSelectedShort?(self.dataArr?[indexPath.row]) + } +} diff --git a/SynthReel/Class/Home/V/SRHomeChildCell.swift b/SynthReel/Class/Home/V/SRHomeChildCell.swift new file mode 100644 index 0000000..cf707cb --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeChildCell.swift @@ -0,0 +1,28 @@ +// +// SRHomeChildCell.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/14. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +class SRHomeChildCell: UICollectionViewCell { + + + var model: SRShortModel? { + didSet { + coverImageView.sr_setImage(model?.image_url) + } + } + + @IBOutlet weak var coverImageView: SRImageView! + + override func awakeFromNib() { + super.awakeFromNib() + coverImageView.layer.cornerRadius = 2 + coverImageView.layer.masksToBounds = true + } + +} diff --git a/SynthReel/Class/Home/V/SRHomeChildCell.xib b/SynthReel/Class/Home/V/SRHomeChildCell.xib new file mode 100644 index 0000000..0c42e12 --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeChildCell.xib @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SynthReel/Class/Home/V/SRHomeHeaderView.swift b/SynthReel/Class/Home/V/SRHomeHeaderView.swift new file mode 100644 index 0000000..9b74866 --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeHeaderView.swift @@ -0,0 +1,156 @@ +// +// SRHomeHeaderView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/15. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit +import YYCategories + +class SRHomeHeaderView: UIView { + + var contentHeight: CGFloat { + return scrollView.contentSize.height + 1 + 20 + } + + var heightDidChange: (() -> Void)? + + weak var viewModel: SRHomeViewModel? + + lazy var scrollView: SRScrollView = { + let scrollView = SRScrollView() + scrollView.isScrollEnabled = false + scrollView.addObserver(self, forKeyPath: "contentSize", context: nil) + return scrollView + }() + + lazy var stackView: UIStackView = { + let view = UIStackView() + view.axis = .vertical + view.spacing = 20 + return view + }() + + lazy var bannerView: SRHomeBannerView = { + let view = SRHomeBannerView() + view.didSelectedShort = { [weak self] model in + self?.pushShortDetail(model) + } + return view + }() + + lazy var youLikeView: SRHomeYouLikeView = { + let view = SRHomeYouLikeView() + view.didSelectedShort = { [weak self] model in + self?.pushShortDetail(model) + } + return view + }() + + lazy var topChartsView: SRHomeTopChartsView = { + let view = SRHomeTopChartsView() + view.didSelectedShort = { [weak self] model in + self?.pushShortDetail(model) + } + return view + }() + + lazy var bingeWorthyView: SRHomeBingeWorthyView = { + let view = SRHomeBingeWorthyView() + view.didSelectedShort = { [weak self] model in + self?.pushShortDetail(model) + } + return view + }() + + lazy var viralHitsView: SRHomeViralHitsView = { + let view = SRHomeViralHitsView() + view.didSelectedShort = { [weak self] model in + self?.pushShortDetail(model) + } + return view + }() + + lazy var premiereNowView: SRHomePremiereNowView = { + let view = SRHomePremiereNowView() + view.didSelectedShort = { [weak self] model in + self?.pushShortDetail(model) + } + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + addSubview(scrollView) + scrollView.addSubview(stackView) + + scrollView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + make.bottom.equalToSuperview().offset(-20) + } + + stackView.snp.makeConstraints { make in + make.left.centerX.top.bottom.equalToSuperview() + } + + reloadData() + } + + 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" { + self.heightDidChange?() + } + } + + + func reloadData() { + stackView.sr_removeAllArrangedSubview() + + var popularArr: [SRShortModel]? = nil + var updatesArr: [SRShortModel]? = nil + + self.viewModel?.moduleArr.forEach { + if $0.module_key == .banner { + bannerView.dataArr = $0.list + stackView.addArrangedSubview(bannerView) + } else if $0.module_key == .detailsRecommand { + youLikeView.dataArr = $0.list + stackView.addArrangedSubview(youLikeView) + } else if $0.module_key == .popular { + popularArr = $0.list + stackView.addArrangedSubview(topChartsView) + } else if $0.module_key == .updates { + updatesArr = $0.list + stackView.addArrangedSubview(topChartsView) + } else if $0.module_key == .bingeWorthy { + bingeWorthyView.dataArr = $0.list + stackView.addArrangedSubview(bingeWorthyView) + } else if $0.module_key == .viralHits { + viralHitsView.dataArr = $0.list + stackView.addArrangedSubview(viralHitsView) + } else if $0.module_key == .premiereNow { + premiereNowView.dataArr = $0.list + stackView.addArrangedSubview(premiereNowView) + } + } + + + self.topChartsView.popularArr = popularArr + self.topChartsView.updatesArr = updatesArr + self.topChartsView.reloadData() + } + + func pushShortDetail(_ model: SRShortModel?) { + let vc = SRDetailPlayerViewController() + vc.shortId = model?.short_play_id + self.viewController?.navigationController?.pushViewController(vc, animated: true) + } +} diff --git a/SynthReel/Class/Home/V/SRHomeHotView.swift b/SynthReel/Class/Home/V/SRHomeHotView.swift new file mode 100644 index 0000000..e0e6479 --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeHotView.swift @@ -0,0 +1,61 @@ +// +// SRHomeHotView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/17. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRHomeHotView: UIView { + + var count: Int = 0 { + didSet { + + if count > 1000 { + countLabel.text = NSNumber(value: CGFloat(count) / 1000).toString(maximumFractionDigits: 1) + "k" + + } else { + countLabel.text = "\(count)" + } + + } + } + + lazy var iconImageView = UIImageView(image: UIImage(named: "hot_icon_01")) + + lazy var countLabel: SRLabel = { + let label = SRLabel() + label.font = .font(ofSize: 10, weight: .regular) + label.textColors = [UIColor._4_CFFD_4.cgColor, UIColor._51_D_4_FF.cgColor] + label.textStartPoint = .init(x: 0.5, y: 0) + label.textEndPoint = .init(x: 0.5, y: 1) + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + addSubview(iconImageView) + addSubview(countLabel) + + iconImageView.snp.makeConstraints { make in + make.left.equalToSuperview() + make.top.equalToSuperview().offset(1) + make.centerY.equalToSuperview() + } + + countLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview() + make.left.equalTo(iconImageView.snp.right).offset(3) + } + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/SynthReel/Class/Home/V/SRHomeMenuCell.swift b/SynthReel/Class/Home/V/SRHomeMenuCell.swift new file mode 100644 index 0000000..d052887 --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeMenuCell.swift @@ -0,0 +1,51 @@ +// +// SRHomeMenuCell.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/14. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import JXSegmentedView +import SnapKit + +class SRHomeMenuCell: JXSegmentedTitleGradientCell/*JXSegmentedTitleCell*/ { + + + lazy var bgImageView: UIImageView = { + let imageView = UIImageView() + return imageView + }() + +// lazy var gTitleLabel: SRLabel = { +// let +// }() + + override func commonInit() { + super.commonInit() +// self.titleLabel.removeFromSuperview() +// self.maskTitleLabel.removeFromSuperview() + + self.contentView.addSubview(bgImageView) + + bgImageView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + self.contentView.sendSubviewToBack(bgImageView) + } + + + + override func reloadData(itemModel: JXSegmentedBaseItemModel, selectedType: JXSegmentedViewItemSelectedType) { + super.reloadData(itemModel: itemModel, selectedType: selectedType) + + if itemModel.isSelected { + bgImageView.image = UIImage(named: "home_menu_bg_image_selected") + } else { + bgImageView.image = UIImage(named: "home_menu_bg_image") + } + + } +} diff --git a/SynthReel/Class/Home/V/SRHomeModuleView.swift b/SynthReel/Class/Home/V/SRHomeModuleView.swift new file mode 100644 index 0000000..0bab06d --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeModuleView.swift @@ -0,0 +1,65 @@ +// +// SRHomeModuleView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/15. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRHomeModuleView: UIView { + + var didSelectedShort: ((_ model: SRShortModel?) -> Void)? + + lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 18, weight: .bold) + label.textColor = .white + return label + }() + + lazy var indicatorImageView = UIImageView(image: UIImage(named: "arrow_right_icon_01")) + + lazy var button: UIControl = { + let button = UIControl(frame: .zero, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + + })) + return button + }() + + + override init(frame: CGRect) { + super.init(frame: frame) + + + addSubview(button) + button.addSubview(titleLabel) + button.addSubview(indicatorImageView) + + button.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.right.equalToSuperview().offset(-15) + make.top.equalToSuperview() + make.height.equalTo(40) + } + + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview() + } + + indicatorImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview() + } + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/SynthReel/Class/Home/V/SRHomePremiereNowView.swift b/SynthReel/Class/Home/V/SRHomePremiereNowView.swift new file mode 100644 index 0000000..f6942aa --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomePremiereNowView.swift @@ -0,0 +1,198 @@ +// +// SRHomePremiereNowView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/17. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRHomePremiereNowView: SRHomeModuleView { + + + var dataArr: [SRShortModel]? { + didSet { + let count = dataArr?.count ?? 0 + + self.oneModel = dataArr?.first + + if count >= 2 { + self.twoModel = dataArr?[1] + } else { + self.twoModel = nil + } + + if count >= 3 { + self.threeModel = dataArr?[2] + } else { + self.threeModel = nil + } + + + } + } + + var oneModel: SRShortModel? { + didSet { + oneCoverImageView.sr_setImage(oneModel?.image_url) + + oneTitleLabel.text = oneModel?.name + } + } + + var twoModel: SRShortModel? { + didSet { + if let model = twoModel { + twoCoverImageView.sr_setImage(model.image_url) + twoCoverImageView.isHidden = false + } else { + twoCoverImageView.isHidden = true + } + } + } + + var threeModel: SRShortModel? { + didSet { + if let model = threeModel { + threeCoverImageView.sr_setImage(model.image_url) + threeCoverImageView.isHidden = false + } else { + threeCoverImageView.isHidden = true + } + } + } + + + lazy var bgImageView = UIImageView(image: UIImage(named: "premiere_now_bg_image_01")) + + lazy var oneBgImageView = UIImageView(image: UIImage(named: "premiere_now_bg_image_02")) + + lazy var oneCoverImageView: SRImageView = { + let imageView = SRImageView() + imageView.isUserInteractionEnabled = true + let tap = UITapGestureRecognizer { [weak self] _ in + guard let self = self else { return } + self.didSelectedShort?(self.oneModel) + } + imageView.addGestureRecognizer(tap) + return imageView + }() + + lazy var oneTitleBgView = UIImageView(image: UIImage(named: "premiere_now_title_bg")) + lazy var oneTitleLabel: UILabel = { + let label = SRLabel() + label.font = .font(ofSize: UIScreen.getRatioWidth(size: 12), weight: .medium) + label.numberOfLines = 2 + label.textAlignment = .center + label.textColors = [UIColor.srGreen.cgColor, UIColor.srBlue.cgColor] + label.textStartPoint = .init(x: 0.5, y: 0) + label.textEndPoint = .init(x: 0.5, y: 1) + return label + }() + + lazy var twoCoverImageView: SRImageView = { + let imageView = SRImageView() + imageView.isUserInteractionEnabled = true + let tap = UITapGestureRecognizer { [weak self] _ in + guard let self = self else { return } + self.didSelectedShort?(self.twoModel) + } + imageView.addGestureRecognizer(tap) + return imageView + }() + + lazy var threeCoverImageView: SRImageView = { + let imageView = SRImageView() + imageView.isUserInteractionEnabled = true + let tap = UITapGestureRecognizer { [weak self] _ in + guard let self = self else { return } + self.didSelectedShort?(self.threeModel) + } + imageView.addGestureRecognizer(tap) + return imageView + }() + + + override init(frame: CGRect) { + super.init(frame: frame) + self.titleLabel.text = "Premiere Now".localized + sr_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + oneCoverImageView.applyHexagonMask(2) + twoCoverImageView.applyHexagonMask(2) + threeCoverImageView.applyHexagonMask(2) + } + + +} + +extension SRHomePremiereNowView { + + private func sr_setupUI() { + oneBgImageView.isUserInteractionEnabled = true + + addSubview(bgImageView) + addSubview(oneBgImageView) + oneBgImageView.addSubview(oneCoverImageView) + oneCoverImageView.addSubview(oneTitleBgView) + oneTitleBgView.addSubview(oneTitleLabel) + addSubview(twoCoverImageView) + addSubview(threeCoverImageView) + + bgImageView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.centerY.equalTo(oneBgImageView) + make.height.equalTo(UIScreen.getRatioWidth(size: 155)) + } + + oneBgImageView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.bottom.equalToSuperview() + make.top.equalTo(button.snp.bottom).offset(8) + make.width.equalTo(UIScreen.getRatioWidth(size: 200)) + make.height.equalTo(UIScreen.getRatioWidth(size: 175)) + } + + oneCoverImageView.snp.makeConstraints { make in + make.center.equalToSuperview() + make.width.equalTo(UIScreen.getRatioWidth(size: 190)) + make.height.equalTo(UIScreen.getRatioWidth(size: 166)) + } + + oneTitleBgView.snp.makeConstraints { make in + make.bottom.equalToSuperview() + make.left.equalToSuperview().offset(UIScreen.getRatioWidth(size: 24.5)) + make.right.equalToSuperview().offset(-UIScreen.getRatioWidth(size: 27)) + make.height.equalTo(UIScreen.getRatioWidth(size: 41)) + } + + oneTitleLabel.snp.makeConstraints { make in + make.centerY.equalTo(oneTitleBgView.snp.top).offset(UIScreen.getRatioWidth(size: 23)) + make.centerX.equalToSuperview().offset(UIScreen.getRatioWidth(size: 1.5)) + make.right.lessThanOrEqualToSuperview().offset(-UIScreen.getRatioWidth(size: 15)) + } + + twoCoverImageView.snp.makeConstraints { make in + make.centerY.equalTo(oneBgImageView) + make.left.equalToSuperview().offset(UIScreen.getRatioWidth(size: 20)) + make.width.equalTo(UIScreen.getRatioWidth(size: 62)) + make.height.equalTo(UIScreen.getRatioWidth(size: 54)) + } + + threeCoverImageView.snp.makeConstraints { make in + make.centerY.equalTo(oneBgImageView) + make.right.equalToSuperview().offset(-UIScreen.getRatioWidth(size: 20)) + make.width.height.equalTo(twoCoverImageView) + } + } + +} diff --git a/SynthReel/Class/Home/V/SRHomeTopChartsContentView.swift b/SynthReel/Class/Home/V/SRHomeTopChartsContentView.swift new file mode 100644 index 0000000..229831e --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeTopChartsContentView.swift @@ -0,0 +1,196 @@ +// +// SRHomeTopChartsContentView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/17. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + + +extension SRHomeTopChartsView { + + class ContentView: UIView { + + var didSelectedShort: ((_ model: SRShortModel?) -> Void)? + + var dataArr: [SRShortModel]? { + didSet { + self.collectionView.reloadData() + } + } + + lazy var bgImageView = UIImageView() + + lazy var trophyImageView = UIImageView(image: UIImage(named: "trophy_icon_01")) + + lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 15, weight: .bold) + label.textColor = .white + return label + }() + + lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.itemSize = .init(width: 198, height: 53) + layout.minimumLineSpacing = 9 + return layout + }() + + lazy var collectionView: SRCollectionView = { + let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.isScrollEnabled = false + collectionView.register(ContentCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + + override init(frame: CGRect) { + super.init(frame: frame) + bgImageView.isUserInteractionEnabled = true + + addSubview(bgImageView) + addSubview(trophyImageView) + bgImageView.addSubview(titleLabel) + bgImageView.addSubview(collectionView) + + bgImageView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview().offset(16) + make.width.equalTo(198) + } + + trophyImageView.snp.makeConstraints { make in + make.top.right.equalToSuperview() + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(10) + make.top.equalToSuperview().offset(13) + } + + collectionView.snp.makeConstraints { make in + make.left.equalToSuperview() + make.right.equalToSuperview() + make.top.equalToSuperview().offset(46) + make.bottom.equalToSuperview() + } + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + } + +} + +//MARK: UICollectionViewDelegate UICollectionViewDataSource +extension SRHomeTopChartsView.ContentView: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return min(3, self.dataArr?.count ?? 0) + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRHomeTopChartsView.ContentCell + cell.row = indexPath.row + cell.model = self.dataArr?[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + self.didSelectedShort?(self.dataArr?[indexPath.row]) + } + +} + + +extension SRHomeTopChartsView { + class ContentCell: UICollectionViewCell { + + var model: SRShortModel? { + didSet { + coverImageView.sr_setImage(model?.image_url) + titleLabel.text = model?.name + hotView.count = model?.watch_total ?? 0 + } + } + + var row: Int = 0 { + didSet { + numLabel.text = "\(row + 1)" + } + } + + lazy var numLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .bold) + label.textColor = .white + return label + }() + + lazy var coverImageView: SRImageView = { + let imageView = SRImageView() + imageView.layer.cornerRadius = 2 + imageView.layer.masksToBounds = true + return imageView + }() + + lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .medium) + label.textColor = .white + return label + }() + + lazy var hotView: SRHomeHotView = { + let view = SRHomeHotView() + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + contentView.addSubview(numLabel) + contentView.addSubview(coverImageView) + contentView.addSubview(titleLabel) + contentView.addSubview(hotView) + + numLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.centerX.equalTo(self.contentView.snp.left).offset(14) + } + + coverImageView.snp.makeConstraints { make in + make.top.bottom.equalToSuperview() + make.left.equalToSuperview().offset(24) + make.width.equalTo(40) + } + + titleLabel.snp.makeConstraints { make in + make.top.equalToSuperview().offset(10) + make.left.equalTo(coverImageView.snp.right).offset(6) + make.right.lessThanOrEqualToSuperview().offset(-9) + } + + hotView.snp.makeConstraints { make in + make.left.equalTo(coverImageView.snp.right).offset(6) + make.bottom.equalToSuperview().offset(-7) + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + } +} + diff --git a/SynthReel/Class/Home/V/SRHomeTopChartsView.swift b/SynthReel/Class/Home/V/SRHomeTopChartsView.swift new file mode 100644 index 0000000..5e675de --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeTopChartsView.swift @@ -0,0 +1,106 @@ +// +// SRHomeTopChartsView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/17. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRHomeTopChartsView: SRHomeModuleView { + + var popularArr: [SRShortModel]? { + didSet { + popularView.dataArr = popularArr + } + } + + var updatesArr: [SRShortModel]? { + didSet { + updatesView.dataArr = updatesArr + } + } + + + lazy var scrollView: SRScrollView = { + let scrollView = SRScrollView() + scrollView.showsHorizontalScrollIndicator = false + return scrollView + }() + + lazy var stackView: UIStackView = { + let view = UIStackView() + view.axis = .horizontal + view.spacing = 16 + return view + }() + + lazy var popularView: ContentView = { + let view = ContentView() + view.titleLabel.text = "Popular".localized + view.bgImageView.image = UIImage(named: "popular_bg_image") + view.didSelectedShort = { [weak self] model in + self?.didSelectedShort?(model) + } + return view + }() + + lazy var updatesView: ContentView = { + let view = ContentView() + view.titleLabel.text = "Updates".localized + view.bgImageView.image = UIImage(named: "updates_bg_image") + view.didSelectedShort = { [weak self] model in + self?.didSelectedShort?(model) + } + return view + }() + + + override init(frame: CGRect) { + super.init(frame: frame) + self.titleLabel.text = "Top Charts".localized + + sr_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func reloadData() { + stackView.sr_removeAllArrangedSubview() + + if let arr = self.popularArr, arr.count > 0 { + stackView.addArrangedSubview(popularView) + } + + if let arr = self.updatesArr, arr.count > 0 { + stackView.addArrangedSubview(updatesView) + } + } + +} + +extension SRHomeTopChartsView { + + private func sr_setupUI() { + addSubview(scrollView) + scrollView.addSubview(stackView) + + scrollView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalTo(self.button.snp.bottom) + make.height.equalTo(238 + 16) + make.bottom.equalToSuperview() + } + + stackView.snp.makeConstraints { make in + make.top.equalToSuperview() + make.left.equalToSuperview().offset(15) + make.right.equalToSuperview().offset(-15) + make.height.equalTo(238 + 16) + } + } +} diff --git a/SynthReel/Class/Home/V/SRHomeViralHitsCell.swift b/SynthReel/Class/Home/V/SRHomeViralHitsCell.swift new file mode 100644 index 0000000..ea1ad2e --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeViralHitsCell.swift @@ -0,0 +1,86 @@ +// +// SRHomeViralHitsCell.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/17. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRHomeViralHitsCell: UICollectionViewCell { + + var model: SRShortModel? { + didSet { + coverImageView.sr_setImage(model?.image_url) + titleLabel.text = model?.name + hotView.count = model?.watch_total ?? 0 + } + } + + lazy var bgImageView = UIImageView(image: UIImage(named: "home_viral_hits_cell_image")) + + lazy var coverImageView: SRImageView = { + let imageView = SRImageView() + imageView.layer.cornerRadius = 2 + imageView.layer.masksToBounds = true + return imageView + }() + + lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .medium) + label.textColor = .white + return label + }() + + lazy var hotView: SRHomeHotView = { + let view = SRHomeHotView() + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + sr_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +extension SRHomeViralHitsCell { + + private func sr_setupUI() { + contentView.addSubview(bgImageView) + contentView.addSubview(coverImageView) + coverImageView.addSubview(hotView) + contentView.addSubview(titleLabel) + + bgImageView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + coverImageView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.left.equalToSuperview().offset(13) + make.top.equalToSuperview().offset(21) + make.height.equalTo(180) + } + + hotView.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-5) + make.top.equalToSuperview().offset(4) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(13) + make.right.lessThanOrEqualToSuperview().offset(-13) + make.top.equalTo(coverImageView.snp.bottom).offset(4) + } + + } + +} diff --git a/SynthReel/Class/Home/V/SRHomeViralHitsView.swift b/SynthReel/Class/Home/V/SRHomeViralHitsView.swift new file mode 100644 index 0000000..c6e276c --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeViralHitsView.swift @@ -0,0 +1,74 @@ +// +// SRHomeViralHitsView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/17. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRHomeViralHitsView: SRHomeModuleView { + + var dataArr: [SRShortModel]? { + didSet { + collectionView.reloadData() + } + } + + lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + layout.minimumLineSpacing = 12 + layout.itemSize = .init(width: 161, height: 237) + return layout + }() + + lazy var collectionView: SRCollectionView = { + let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.showsHorizontalScrollIndicator = false + collectionView.contentInset = .init(top: 0, left: 15, bottom: 0, right: 15) + collectionView.register(SRHomeViralHitsCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + self.titleLabel.text = "Viral Hits".localized + + addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalTo(button.snp.bottom).offset(6) + make.height.equalTo(collectionViewLayout.itemSize.height) + } + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + + +//MARK: UICollectionViewDelegate UICollectionViewDataSource +extension SRHomeViralHitsView: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRHomeViralHitsCell + cell.model = dataArr?[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.dataArr?.count ?? 0 + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + self.didSelectedShort?(self.dataArr?[indexPath.row]) + } +} diff --git a/SynthReel/Class/Home/V/SRHomeYouLikeCell.swift b/SynthReel/Class/Home/V/SRHomeYouLikeCell.swift new file mode 100644 index 0000000..2ca2866 --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeYouLikeCell.swift @@ -0,0 +1,120 @@ +// +// SRHomeYouLikeCell.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/15. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRHomeYouLikeCell: UICollectionViewCell { + + var model: SRShortModel? { + didSet { + coverImageView.sr_setImage(model?.image_url) + + titleLabel.text = model?.name + desLabel.text = model?.sr_description + } + } + + lazy var bgView: UIView = UIImageView(image: UIImage(named: "cell_bg_image_01")) + + lazy var coverBgView: UIView = { + let view = UIView() + view.layer.cornerRadius = 34 + view.layer.masksToBounds = true + view.layer.borderWidth = 0.7 + view.layer.borderColor = UIColor._4_CFFD_4.withAlphaComponent(0.25).cgColor + return view + }() + + lazy var coverBorderView: SRGradientView = { + let view = SRGradientView() + view.layer.cornerRadius = 31 + view.layer.masksToBounds = true + view.colors = [UIColor._4_CFFD_4.cgColor, UIColor._51_D_4_FF.cgColor] + view.startPoint = .init(x: 0, y: 0) + view.endPoint = .init(x: 1, y: 1) + return view + }() + + lazy var coverImageView: SRImageView = { + let imageView = SRImageView() + imageView.layer.cornerRadius = 30.25 + imageView.layer.masksToBounds = true + return imageView + }() + + lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .medium) + label.textColor = .white + return label + }() + + lazy var desLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 11, weight: .regular) + label.textColor = .A_6_A_6_A_6 + label.numberOfLines = 2 + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + sr_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +extension SRHomeYouLikeCell { + + private func sr_setupUI() { + contentView.addSubview(bgView) + bgView.addSubview(coverBgView) + coverBgView.addSubview(coverBorderView) + coverBgView.addSubview(coverImageView) + bgView.addSubview(titleLabel) + bgView.addSubview(desLabel) + + bgView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + coverBgView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(8) + make.width.height.equalTo(68) + } + + coverBorderView.snp.makeConstraints { make in + make.center.equalToSuperview() + make.width.height.equalTo(62) + } + + coverImageView.snp.makeConstraints { make in + make.center.equalToSuperview() + make.width.height.equalTo(60.5) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalTo(coverBgView.snp.right).offset(9) + make.top.equalToSuperview().offset(17) + make.right.lessThanOrEqualToSuperview().offset(-12) + } + + desLabel.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.top.equalTo(titleLabel.snp.bottom).offset(11) + make.right.lessThanOrEqualToSuperview().offset(-12) + } + } + +} diff --git a/SynthReel/Class/Home/V/SRHomeYouLikeView.swift b/SynthReel/Class/Home/V/SRHomeYouLikeView.swift new file mode 100644 index 0000000..33d894a --- /dev/null +++ b/SynthReel/Class/Home/V/SRHomeYouLikeView.swift @@ -0,0 +1,81 @@ +// +// SRHomeYouLikeView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/15. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRHomeYouLikeView: UIView { + + var dataArr: [SRShortModel]? { + didSet { + self.collectionView.reloadData() + } + } + + var didSelectedShort: ((_ model: SRShortModel?) -> Void)? + + lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + layout.minimumLineSpacing = 14 + layout.itemSize = .init(width: 220, height: 82) + return layout + }() + + lazy var collectionView: SRCollectionView = { + let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.showsHorizontalScrollIndicator = false + collectionView.contentInset = .init(top: 0, left: 15, bottom: 0, right: 15) + collectionView.register(SRHomeYouLikeCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + sr_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension SRHomeYouLikeView { + + func sr_setupUI() { + + addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.left.right.top.bottom.equalToSuperview() + make.height.equalTo(82) + } + } + +} + +//MARK: UICollectionViewDelegate UICollectionViewDataSource +extension SRHomeYouLikeView: UICollectionViewDelegate, UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRHomeYouLikeCell + cell.model = self.dataArr?[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.dataArr?.count ?? 0 + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + self.didSelectedShort?(self.dataArr?[indexPath.row]) + } +} diff --git a/SynthReel/Class/Home/V/SRHotSearchCell.swift b/SynthReel/Class/Home/V/SRHotSearchCell.swift new file mode 100644 index 0000000..28713bf --- /dev/null +++ b/SynthReel/Class/Home/V/SRHotSearchCell.swift @@ -0,0 +1,114 @@ +// +// SRHotSearchCell.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRHotSearchCell: SRTableViewCell { + + var model: SRShortModel? { + didSet { + coverImageView.sr_setImage(model?.image_url) + titleLabel.text = model?.name + hotView.count = model?.watch_total ?? 0 + desLabel.text = model?.sr_description + } + } + + var row: Int = 0 { + didSet { + numLabel.text = "\(row + 1)" + } + } + + lazy var numLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .bold) + label.textColor = .white + return label + }() + + lazy var coverImageView: SRImageView = { + let imageView = SRImageView() + imageView.layer.cornerRadius = 2 + imageView.layer.masksToBounds = true + return imageView + }() + + lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .medium) + label.textColor = .white + return label + }() + + lazy var hotView: SRHomeHotView = { + let view = SRHomeHotView() + return view + }() + + lazy var desLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 11, weight: .regular) + label.textColor = .A_6_A_6_A_6 + return label + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + sr_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension SRHotSearchCell { + + private func sr_setupUI() { + contentView.addSubview(numLabel) + contentView.addSubview(coverImageView) + contentView.addSubview(titleLabel) + contentView.addSubview(hotView) + contentView.addSubview(desLabel) + + coverImageView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(24) + make.width.equalTo(40) + make.height.equalTo(53) + make.top.equalToSuperview().offset(8) + make.bottom.equalToSuperview().offset(-8) + } + + numLabel.snp.makeConstraints { make in + make.centerX.equalTo(self.contentView.snp.left).offset(14) + make.centerY.equalToSuperview() + } + + titleLabel.snp.makeConstraints { make in + make.left.equalTo(coverImageView.snp.right).offset(6) + make.top.equalTo(coverImageView).offset(8) + } + + hotView.snp.makeConstraints { make in + make.left.equalTo(titleLabel.snp.right).offset(15) + make.right.lessThanOrEqualToSuperview().offset(-30) + make.centerY.equalTo(titleLabel) + } + + desLabel.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.bottom.equalTo(coverImageView).offset(-6) + make.right.equalToSuperview().offset(-27) + } + } + +} diff --git a/SynthReel/Class/Home/V/SRHotSearchView.swift b/SynthReel/Class/Home/V/SRHotSearchView.swift new file mode 100644 index 0000000..127a01c --- /dev/null +++ b/SynthReel/Class/Home/V/SRHotSearchView.swift @@ -0,0 +1,115 @@ +// +// SRHotSearchView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit +import YYCategories + +class SRHotSearchView: UIView { + + override var intrinsicContentSize: CGSize { + return .init(width: UIScreen.width, height: UIScreen.height) + } + + var dataArr: [SRShortModel] = [] { + didSet { + self.tableView.reloadData() + } + } + + lazy var bgImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "search_hot_bg_image")) + imageView.isUserInteractionEnabled = true + return imageView + }() + + lazy var titleLabel: UILabel = { + let label = SRLabel() + label.font = .font(ofSize: 15, weight: .semibold) + label.textColors = [UIColor._7_AF_4_E_0.cgColor, UIColor.white.cgColor, UIColor._7_AF_4_E_0.cgColor] + label.textStartPoint = .init(x: 0, y: 0.5) + label.textEndPoint = .init(x: 1, y: 0.5) + label.text = "Premium Picks".localized + return label + }() + + lazy var tableView: SRTableView = { + let tableView = SRTableView(frame: .zero, style: .plain) + tableView.delegate = self + tableView.dataSource = self + tableView.separatorStyle = .none + tableView.showsVerticalScrollIndicator = false + tableView.keyboardDismissMode = .onDrag + tableView.contentInset = .init(top: 0, left: 0, bottom: 5, right: 0) + tableView.register(SRHotSearchCell.self, forCellReuseIdentifier: "cell") + return tableView + }() + + + override init(frame: CGRect) { + super.init(frame: frame) + + sr_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + +} + +extension SRHotSearchView { + + private func sr_setupUI() { + addSubview(bgImageView) + bgImageView.addSubview(titleLabel) + bgImageView.addSubview(tableView) + + bgImageView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.centerX.equalToSuperview() + make.top.equalToSuperview() + make.bottom.equalToSuperview().offset(-(UIScreen.safeBottom + 10)) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(10) + make.top.equalToSuperview().offset(13) + } + + tableView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalToSuperview().offset(38) + make.bottom.equalToSuperview() + } + } + +} + +//MARK: UITableViewDelegate UITableViewDataSource +extension SRHotSearchView: UITableViewDelegate, UITableViewDataSource { + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SRHotSearchCell + cell.row = indexPath.row + cell.model = dataArr[indexPath.row] + return cell + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return self.dataArr.count + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let model = dataArr[indexPath.row] + let vc = SRDetailPlayerViewController() + vc.shortId = model.short_play_id + self.viewController?.navigationController?.pushViewController(vc, animated: true) + } +} diff --git a/SynthReel/Class/Home/V/SRSearchHomeView.swift b/SynthReel/Class/Home/V/SRSearchHomeView.swift new file mode 100644 index 0000000..3223680 --- /dev/null +++ b/SynthReel/Class/Home/V/SRSearchHomeView.swift @@ -0,0 +1,93 @@ +// +// SRSearchHomeView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRSearchHomeView: UIView { + + var didSearch: ((_ text: String) -> Void)? + + weak var viewModel: SRSearchViewModel? { + didSet { + viewModel?.addObserver(self, forKeyPath: "hotDataArr", context: nil) + viewModel?.addObserver(self, forKeyPath: "recordList", context: nil) + + self.hotView.dataArr = self.viewModel?.hotDataArr ?? [] + self.recordView.dataArr = self.viewModel?.recordList ?? [] + updateLayout() + } + } + + lazy var stackView: UIStackView = { + let view = UIStackView() + view.spacing = 20 + view.axis = .vertical + return view + }() + + lazy var recordView: SRSearchRecordView = { + let view = SRSearchRecordView() + view.didSearch = { [weak self] text in + self?.didSearch?(text) + } + view.didDeleteAll = { [weak self] in + self?.viewModel?.clearSearchRecord() + } + return view + }() + + lazy var hotView: SRHotSearchView = { + let view = SRHotSearchView() + return view + }() + + deinit { + viewModel?.removeObserver(self, forKeyPath: "hotDataArr") + viewModel?.removeObserver(self, forKeyPath: "recordList") + } + + override init(frame: CGRect) { + super.init(frame: frame) + + addSubview(stackView) + + stackView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalToSuperview().offset(14) + make.bottom.lessThanOrEqualToSuperview() + } + + } + + 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 == "hotDataArr" { + self.hotView.dataArr = self.viewModel?.hotDataArr ?? [] + } else if keyPath == "recordList" { + self.recordView.dataArr = self.viewModel?.recordList ?? [] + } + updateLayout() + } + + func updateLayout() { + stackView.sr_removeAllArrangedSubview() + + if self.recordView.dataArr.count > 0 { + stackView.addArrangedSubview(recordView) + } + if self.hotView.dataArr.count > 0 { + stackView.addArrangedSubview(self.hotView) + } + + } + +} diff --git a/SynthReel/Class/Home/V/SRSearchRecordCell.swift b/SynthReel/Class/Home/V/SRSearchRecordCell.swift new file mode 100644 index 0000000..2da3169 --- /dev/null +++ b/SynthReel/Class/Home/V/SRSearchRecordCell.swift @@ -0,0 +1,42 @@ +// +// SRSearchRecordCell.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRSearchRecordCell: UICollectionViewCell { + + static let TextFont: UIFont = .font(ofSize: 12, weight: .regular) + + lazy var textLabel: UILabel = { + let label = UILabel() + label.font = Self.TextFont + label.textColor = .A_6_A_6_A_6 + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + contentView.backgroundColor = ._010101.withAlphaComponent(0.2) + contentView.layer.cornerRadius = 14 + contentView.layer.masksToBounds = true + contentView.layer.borderWidth = 1 + contentView.layer.borderColor = UIColor._4_CFFD_4.withAlphaComponent(0.25).cgColor + + contentView.addSubview(textLabel) + + textLabel.snp.makeConstraints { make in + make.center.equalToSuperview() + } + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/SynthReel/Class/Home/V/SRSearchRecordView.swift b/SynthReel/Class/Home/V/SRSearchRecordView.swift new file mode 100644 index 0000000..b48cd4b --- /dev/null +++ b/SynthReel/Class/Home/V/SRSearchRecordView.swift @@ -0,0 +1,135 @@ +// +// SRSearchRecordView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit +import collection_view_layouts + +class SRSearchRecordView: UIView { + + var didSearch: ((_ text: String) -> Void)? + var didDeleteAll: (() -> Void)? + + var dataArr: [String] = [] { + didSet { + self.collectionView.reloadData() + } + } + + lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .medium) + label.textColor = .CCCCCC + label.text = "Search History".localized + return label + }() + + lazy var deleteButton: UIButton = { + let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + self.didDeleteAll?() + })) + button.setImage(UIImage(named: "delete_icon_01"), for: .normal) + return button + }() + + private lazy var collectionViewLayout: TagsLayout = { + let layout = TagsLayout() + layout.delegate = self + layout.contentPadding = ItemsPadding(horizontal: 15, vertical: 0) + layout.cellsPadding = ItemsPadding(horizontal: 9, vertical: 10) + return layout + }() + + private lazy var collectionView: SRCollectionView = { + let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.addObserver(self, forKeyPath: "contentSize", context: nil) + collectionView.register(SRSearchRecordCell.self, forCellWithReuseIdentifier: "tagCell") + return collectionView + }() + + deinit { + self.collectionView.removeObserver(self, forKeyPath: "contentSize") + } + + override init(frame: CGRect) { + super.init(frame: frame) + sr_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 SRSearchRecordView { + + private func sr_setupUI() { + addSubview(titleLabel) + addSubview(deleteButton) + addSubview(collectionView) + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(14) + make.centerY.equalTo(deleteButton) + } + + deleteButton.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-5) + make.top.equalToSuperview().offset(-10) + make.width.equalTo(36) + make.height.equalTo(36) + } + + collectionView.snp.makeConstraints { make in + make.top.equalToSuperview().offset(24) + make.left.equalToSuperview() + make.right.equalToSuperview() + make.bottom.equalToSuperview() + make.height.equalTo(1) + } + } + +} + +//MARK: UICollectionViewDelegate UICollectionViewDataSource +extension SRSearchRecordView: UICollectionViewDelegate, UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return dataArr.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "tagCell", for: indexPath) as! SRSearchRecordCell + cell.textLabel.text = dataArr[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + self.didSearch?(dataArr[indexPath.row]) + } + +} + +extension SRSearchRecordView: LayoutDelegate { + func cellSize(indexPath: IndexPath) -> CGSize { + let text = dataArr[indexPath.row] + let size = text.size(SRSearchRecordCell.TextFont, .init(width: UIScreen.width, height: 24)) + return .init(width: size.width + 20, height: 28) + } +} diff --git a/SynthReel/Class/Home/V/SRSearchResultCell.swift b/SynthReel/Class/Home/V/SRSearchResultCell.swift new file mode 100644 index 0000000..6278326 --- /dev/null +++ b/SynthReel/Class/Home/V/SRSearchResultCell.swift @@ -0,0 +1,139 @@ +// +// SRSearchResultCell.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRSearchResultCell: UICollectionViewCell { + + + var model: SRShortModel? { + didSet { + coverImageView.sr_setImage(model?.image_url) + titleLabel.text = model?.name + desLabel.text = model?.sr_description + if let category = model?.categoryList?.first?.name, category.count > 0 { + categoryLabel.text = "#\(category)" + } else { + categoryLabel.text = "" + } + } + } + + lazy var bgImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "search_result_cell_bg_image")) + return imageView + }() + + lazy var coverImageView: SRImageView = { + let imageView = SRImageView() + imageView.layer.cornerRadius = 4 + imageView.layer.masksToBounds = true + return imageView + }() + + lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .medium) + label.textColor = .white + return label + }() + + lazy var categoryLabel: SRLabel = { + let label = SRLabel() + label.font = .font(ofSize: 11, weight: .regular) + label.textColors = [UIColor.srGreen.cgColor, UIColor.srBlue.cgColor] + label.textStartPoint = .init(x: 0.5, y: 0) + label.textEndPoint = .init(x: 0.5, y: 1) + return label + }() + + lazy var desLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 11, weight: .regular) + label.textColor = .A_6_A_6_A_6 + label.numberOfLines = 3 + return label + }() + + lazy var playImageView = UIImageView(image: UIImage(named: "play_icon_01")) + + lazy var playLabel: UILabel = { + let label = SRLabel() + label.font = .font(ofSize: 11, weight: .regular) + label.textColors = [UIColor.srGreen.cgColor, UIColor.srBlue.cgColor] + label.textStartPoint = .init(x: 0.5, y: 0) + label.textEndPoint = .init(x: 0.5, y: 1) + label.text = "Play" + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + sr_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + +} + +extension SRSearchResultCell { + + private func sr_setupUI() { + contentView.addSubview(bgImageView) + contentView.addSubview(coverImageView) + contentView.addSubview(titleLabel) + contentView.addSubview(categoryLabel) + contentView.addSubview(desLabel) + contentView.addSubview(playImageView) + playImageView.addSubview(playLabel) + + bgImageView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + coverImageView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(10) + make.centerY.equalToSuperview() + make.width.equalTo(126) + make.height.equalTo(168) + } + + titleLabel.snp.makeConstraints { make in + make.top.equalTo(coverImageView).offset(17) + make.left.equalTo(coverImageView.snp.right).offset(10) + make.right.lessThanOrEqualToSuperview().offset(-13) + } + + categoryLabel.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.top.equalTo(titleLabel.snp.bottom).offset(12) + } + + desLabel.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.top.equalTo(titleLabel.snp.bottom).offset(43) + make.right.lessThanOrEqualToSuperview().offset(-12) + } + + playImageView.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.bottom.equalTo(coverImageView) + } + + playLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(45) + make.top.equalToSuperview().offset(9) + } + } + +} diff --git a/SynthReel/Class/Home/V/SRSearchResultView.swift b/SynthReel/Class/Home/V/SRSearchResultView.swift new file mode 100644 index 0000000..b53cbf7 --- /dev/null +++ b/SynthReel/Class/Home/V/SRSearchResultView.swift @@ -0,0 +1,111 @@ +// +// SRSearchResultView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit +import YYCategories + +class SRSearchResultView: UIView { + + weak var viewModel: SRSearchViewModel? + + lazy var dataArr: [SRShortModel] = [] + + lazy var titleLabel: UILabel = { + let label = SRLabel() + label.font = .font(ofSize: 15, weight: .semibold) + label.textColors = [UIColor._7_AF_4_E_0.cgColor, UIColor.white.cgColor, UIColor._7_AF_4_E_0.cgColor] + label.textStartPoint = .init(x: 0, y: 0.5) + label.textEndPoint = .init(x: 1, y: 0.5) + label.text = "Search Results".localized + return label + }() + + lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.itemSize = .init(width: UIScreen.width - 30, height: 192) + layout.minimumLineSpacing = 10 + return layout + }() + + lazy var collectionView: SRCollectionView = { + let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.keyboardDismissMode = .onDrag + collectionView.showsVerticalScrollIndicator = false + collectionView.contentInset = .init(top: 0, left: 0, bottom: UIScreen.safeBottom + 10, right: 0) + collectionView.register(SRSearchResultCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + sr_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func search(_ text: String) { + self.dataArr.removeAll() + self.collectionView.reloadData() + + Task { + if let arr = await SRHomeApi.requestSearch(text) { + self.dataArr = arr + self.collectionView.reloadData() + } + } + + } +} + +extension SRSearchResultView { + + private func sr_setupUI() { + addSubview(titleLabel) + addSubview(collectionView) + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.top.equalToSuperview().offset(30) + } + + collectionView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.bottom.equalToSuperview() + make.top.equalToSuperview().offset(59) + } + } + +} + +//MARK: UICollectionViewDelegate UICollectionViewDataSource +extension SRSearchResultView: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRSearchResultCell + cell.model = dataArr[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return dataArr.count + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let model = dataArr[indexPath.row] + let vc = SRDetailPlayerViewController() + vc.shortId = model.short_play_id + self.viewController?.navigationController?.pushViewController(vc, animated: true) + } + +} diff --git a/SynthReel/Class/Home/V/SRSearchTextView.swift b/SynthReel/Class/Home/V/SRSearchTextView.swift new file mode 100644 index 0000000..c85262d --- /dev/null +++ b/SynthReel/Class/Home/V/SRSearchTextView.swift @@ -0,0 +1,125 @@ +// +// SRSearchTextView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRSearchTextView: UIView { + + var didSearch: ((_ text: String) -> Void)? + + var text: String? { + get { + return textField.text + } + set { + textField.text = newValue + } + } + + lazy var bgImageView = UIImageView(image: UIImage(named: "search_text_bg_image")) + + lazy var button: UIButton = { + var configuration = UIButton.Configuration.plain() + configuration.background.cornerRadius = 0 + configuration.background.image = UIImage(named: "search_text_button") + configuration.contentInsets = .init(top: 0, leading: 10, bottom: 0, trailing: 10) + configuration.attributedTitle = AttributedString("Search".localized, attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 14, weight: .medium), + .foregroundColor : UIColor._010101 + ])) + let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + self.handleSearch() + })) + button.setContentHuggingPriority(.required, for: .horizontal) + button.setContentCompressionResistancePriority(.required, for: .horizontal) + return button + }() + + lazy var textField: UITextField = { + let textField = UITextField(frame: .zero) + textField.tintColor = UIColor.white.withAlphaComponent(0.5) + textField.delegate = self + textField.returnKeyType = .search + textField.font = .font(ofSize: 12, weight: .medium) + textField.textColor = .white + textField.attributedPlaceholder = NSAttributedString(string: "search_placeholder_text".localized, attributes: [ + .font : UIFont.font(ofSize: 12, weight: .medium), + .foregroundColor : UIColor.white.withAlphaComponent(0.3) + ]) + return textField + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + sr_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @discardableResult + override func becomeFirstResponder() -> Bool { + super.becomeFirstResponder() + return self.textField.becomeFirstResponder() + } + + @discardableResult + override func resignFirstResponder() -> Bool { + super.resignFirstResponder() + return self.textField.resignFirstResponder() + } + + private func handleSearch() { + if let text = textField.text { + self.didSearch?(text) + } + } + +} + + +extension SRSearchTextView { + + private func sr_setupUI() { + addSubview(bgImageView) + addSubview(button) + addSubview(textField) + + bgImageView.snp.makeConstraints { make in + make.top.bottom.left.equalToSuperview() + make.right.equalToSuperview().offset(-8) + make.height.equalTo(40) + } + + button.snp.makeConstraints { make in + make.top.bottom.equalToSuperview() + make.right.equalToSuperview() + } + + textField.snp.makeConstraints { make in + make.top.bottom.equalToSuperview() + make.left.equalToSuperview().offset(14) + make.right.equalTo(button.snp.left).offset(-15) + } + } + +} + +//MARK: UITextFieldDelegate +extension SRSearchTextView: UITextFieldDelegate { + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + handleSearch() + return true + } + +} diff --git a/SynthReel/Class/Home/VC/SRHomeChildViewController.swift b/SynthReel/Class/Home/VC/SRHomeChildViewController.swift new file mode 100644 index 0000000..6641a92 --- /dev/null +++ b/SynthReel/Class/Home/VC/SRHomeChildViewController.swift @@ -0,0 +1,143 @@ +// +// SRHomeChildViewController.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/14. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRHomeChildViewController: SRViewController { + + var categoryId: String? + + var dataArr: [SRShortModel] = [] + var page = 1 + + lazy var bgImageView = UIImageView(image: UIImage(named: "home_list_bg_image")) + + lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let itemWidth = floor((UIScreen.width - 30 - 26 - 10) / 2) + let itemHeight = 206.0 / 155.0 * itemWidth + + let layout = UICollectionViewFlowLayout() + layout.minimumInteritemSpacing = 10 + layout.minimumLineSpacing = 10 + layout.itemSize = .init(width: itemWidth, height: itemHeight) + layout.sectionInset = .init(top: 0, left: 13 + 15, bottom: 0, right: 13 + 15) + return layout + }() + + lazy var collectionView: SRCollectionView = { + let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.showsVerticalScrollIndicator = false + collectionView.showsHorizontalScrollIndicator = false + collectionView.register(UINib(nibName: "SRHomeChildCell", bundle: nil), forCellWithReuseIdentifier: "cell") + collectionView.sr_addRefreshFooter { [weak self] in + self?.handleFooterRefresh(nil) + } + return collectionView + }() + + override func viewDidLoad() { + super.viewDidLoad() + self.view.backgroundColor = .clear + self.backgroundImageView.isHidden = true + + sr_setupUI() + + Task { + await requestDataArr(page: self.page) + } + } + + + override func listScrollView() -> UIScrollView { + return collectionView + } + + + override func handleHeaderRefresh(_ completer: (() -> Void)?) { + Task { + await self.requestDataArr(page: 1) + completer?() + } + } + + override func handleFooterRefresh(_ completer: (() -> Void)?) { + Task { + await self.requestDataArr(page: self.page + 1) + self.collectionView.sr_endFooterRefreshing() + completer?() + } + } + +} + +extension SRHomeChildViewController { + + private func sr_setupUI() { + view.addSubview(bgImageView) + view.addSubview(collectionView) + + bgImageView.snp.makeConstraints { make in + make.top.equalToSuperview().offset(20) + make.left.equalToSuperview().offset(15) + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().offset(-15) + } + + collectionView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalTo(bgImageView).offset(30) + make.bottom.equalTo(bgImageView).offset(-27) + } + } + +} + +//MARK: UICollectionViewDelegate UICollectionViewDataSource +extension SRHomeChildViewController: UICollectionViewDelegate, UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRHomeChildCell + cell.model = self.dataArr[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.dataArr.count + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + self.didScrollCallback?(scrollView) + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let model = dataArr[indexPath.row] + + let vc = SRDetailPlayerViewController() + vc.shortId = model.short_play_id + self.navigationController?.pushViewController(vc, animated: true) + } +} + +extension SRHomeChildViewController { + + private func requestDataArr(page: Int) async { + guard let id = categoryId else { return } + + if let dataArr = await SRHomeApi.requestCategoryVideoData(id, page: page) { + if page == 1 { + self.dataArr.removeAll() + } + self.dataArr += dataArr + self.page = page + self.collectionView.reloadData() + } + } + +} diff --git a/SynthReel/Class/Home/VC/SRHomeViewController.swift b/SynthReel/Class/Home/VC/SRHomeViewController.swift new file mode 100644 index 0000000..a14df19 --- /dev/null +++ b/SynthReel/Class/Home/VC/SRHomeViewController.swift @@ -0,0 +1,279 @@ +// +// SRHomeViewController.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/13. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit +import YYCategories +import JXPagingView +import JXSegmentedView + + +extension JXPagingListContainerView: @retroactive JXSegmentedViewListContainer { } + +class SRHomeViewController: SRViewController { + + var viewModel = SRHomeViewModel() + + + lazy var searchButton: UIButton = { + var configuration = UIButton.Configuration.plain() + configuration.image = UIImage(named: "search_icon_01") + configuration.contentInsets = .init(top: 0, leading: 15, bottom: 0, trailing: 15) + + let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + let vc = SRSearchViewController() + self.navigationController?.pushViewController(vc, animated: true) + })) + + return button + }() + + lazy var titleImageView = UIImageView(image: UIImage(named: "home_title_image")) + + lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: UIScreen.getRatioWidth(size: 20), weight: .bold).withBoldItalic() + label.textColor = .white + label.text = "Drama Center".localized + return label + }() + + lazy var menuContentView: UIView = { + let view = UIView() + return view + }() + + lazy var moreButton: UIControl = { + let view = UIControl() + return view + }() + + lazy var moreTitleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 18, weight: .bold) + label.textColor = .white + label.text = "Categories".localized + return label + }() + + lazy var moreIconImageView = UIImageView(image: UIImage(named: "arrow_right_icon_01")) + + lazy var menuDataSource: JXSegmentedTitleGradientDataSource = { + let dataSource = SRHomeMenuDataSource() + dataSource.titleNormalGradientColors = [UIColor.white.cgColor, UIColor._96_E_5_FF.cgColor, UIColor.white.cgColor] + dataSource.titleSelectedGradientColors = [UIColor.white.cgColor, UIColor._96_E_5_FF.cgColor, UIColor.white.cgColor] + dataSource.titleGradientStartPoint = .init(x: 0.5, y: 0) + dataSource.titleGradientEndPoint = .init(x: 0.5, y: 1) + dataSource.titleNormalFont = .font(ofSize: 13, weight: .medium) + dataSource.titleSelectedFont = .font(ofSize: 13, weight: .medium) + dataSource.itemWidth = 100 + dataSource.itemWidthIncrement = 5 + dataSource.titleNumberOfLines = 2 + dataSource.itemSpacing = 5 + return dataSource + }() + + lazy var menuView: JXSegmentedView = { + let view = JXSegmentedView() + view.dataSource = menuDataSource + view.contentEdgeInsetLeft = 15 + view.contentEdgeInsetRight = 15 + return view + }() + + lazy var pageView: JXPagingView = { + let view = JXPagingView(delegate: self) + view.layer.masksToBounds = true + view.mainTableView.backgroundColor = .clear + view.listContainerView.listCellBackgroundColor = .clear + view.mainTableView.gestureDelegate = self + view.automaticallyDisplayListVerticalScrollIndicator = false + view.mainTableView.sr_addRefreshHeader { [weak self] in + self?.handleHeaderRefresh(nil) + } + return view + }() + + lazy var headerView: SRHomeHeaderView = { + let view = SRHomeHeaderView() + view.viewModel = self.viewModel + view.heightDidChange = { [weak self] in + guard let self = self else { return } + self.pageView.resizeTableHeaderViewHeight(animatable: false) + } + return view + }() + + @MainActor + deinit { + srPrint(message: "销毁") + } + + override func viewDidLoad() { + super.viewDidLoad() + sr_setupUI() + + Task { + await requestModuleList() + await requestCategoryList() + } + } + + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.navigationController?.setNavigationBarHidden(true, animated: true) + } + + override func handleHeaderRefresh(_ completer: (() -> Void)?) { + Task { + await requestModuleList() + self.pageView.mainTableView.sr_endHeaderRefreshing() + } + if let vc = self.pageView.listContainerView.validListDict[self.pageView.listContainerView.currentIndex] as? SRViewController { + vc.handleHeaderRefresh(nil) + } + } + +} + +extension SRHomeViewController { + + func sr_setupUI() { + view.addSubview(searchButton) + view.addSubview(titleImageView) + titleImageView.addSubview(titleLabel) + view.addSubview(pageView) + menuContentView.addSubview(menuView) + menuContentView.addSubview(moreButton) + moreButton.addSubview(moreTitleLabel) + moreButton.addSubview(moreIconImageView) + + menuView.listContainer = pageView.listContainerView + + searchButton.snp.makeConstraints { make in + make.height.equalTo(44) + make.right.equalToSuperview() + make.top.equalToSuperview().offset(UIScreen.safeTop) + } + + titleImageView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.top.equalToSuperview().offset(UIScreen.safeTop + 12) + make.width.equalTo(UIScreen.getRatioWidth(size: titleImageView.image?.size.width ?? 0)) + make.height.equalTo(UIScreen.getRatioWidth(size: titleImageView.image?.size.height ?? 0)) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(UIScreen.getRatioWidth(size: 15)) + make.centerY.equalTo(titleImageView.snp.top).offset(UIScreen.getRatioWidth(size: 21)) + } + + pageView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalTo(titleImageView.snp.bottom).offset(0) + } + + menuView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.height.equalTo(40) + } + + moreButton.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.right.equalToSuperview().offset(-15) + make.height.equalTo(40) + make.top.equalToSuperview() + } + + moreTitleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview() + } + + moreIconImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview() + } + } + +} + +//MARK: JXPagingSmoothViewDataSource +extension SRHomeViewController: JXPagingViewDelegate { + func tableHeaderViewHeight(in pagingView: JXPagingView) -> Int { + return Int(ceil(self.headerView.contentHeight)) + } + + func tableHeaderView(in pagingView: JXPagingView) -> UIView { + return self.headerView + } + + func heightForPinSectionHeader(in pagingView: JXPagingView) -> Int { + return 90 + } + + func viewForPinSectionHeader(in pagingView: JXPagingView) -> UIView { + return self.menuContentView + } + + func numberOfLists(in pagingView: JXPagingView) -> Int { + return self.menuDataSource.titles.count + } + + func pagingView(_ pagingView: JXPagingView, initListAtIndex index: Int) -> any JXPagingViewListViewDelegate { + let vc = SRHomeChildViewController() + vc.categoryId = self.viewModel.categoryArr[index].id + return vc + } + +} + +//MARK: JXPagingMainTableViewGestureDelegate +extension SRHomeViewController: JXPagingMainTableViewGestureDelegate { + func mainTableViewGestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + if otherGestureRecognizer == menuView.collectionView.panGestureRecognizer { + return false + } + + if let view = otherGestureRecognizer.view { + var superview: UIView? = view.superview + while superview != nil { + if superview?.isKind(of: SRHomeHeaderView.self) == true { + return false + } + superview = superview?.superview + } + } + + return gestureRecognizer.isKind(of: UIPanGestureRecognizer.self) && otherGestureRecognizer.isKind(of: UIPanGestureRecognizer.self) + } + +} + +extension SRHomeViewController { + + func requestCategoryList() async { + await self.viewModel.requestCategoryList() + + self.menuDataSource.titles = self.viewModel.categoryTitleArr + + self.pageView.reloadData() + self.menuView.reloadData() + } + + func requestModuleList() async { + await self.viewModel.requestModuleList() + + self.headerView.reloadData() + } + + +} + diff --git a/SynthReel/Class/Home/VC/SRSearchViewController.swift b/SynthReel/Class/Home/VC/SRSearchViewController.swift new file mode 100644 index 0000000..cf877cc --- /dev/null +++ b/SynthReel/Class/Home/VC/SRSearchViewController.swift @@ -0,0 +1,113 @@ +// +// SRSearchViewController.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRSearchViewController: SRViewController { + + lazy var viewModel = SRSearchViewModel() + + lazy var returnButton: UIButton = { + let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + self.sr_handleNavigationBack() + })) + button.setImage(UIImage(named: "arrow_left_icon_01"), for: .normal) + return button + }() + + lazy var textView: SRSearchTextView = { + let view = SRSearchTextView() + view.didSearch = { [weak self] text in + self?.search(text) + } + return view + }() + + lazy var homeView: SRSearchHomeView = { + let view = SRSearchHomeView() + view.viewModel = self.viewModel + view.didSearch = { [weak self] text in + self?.textView.text = text + self?.search(text) + } + return view + }() + + lazy var resultView: SRSearchResultView = { + let view = SRSearchResultView() + view.viewModel = self.viewModel + return view + }() + + override func viewDidLoad() { + super.viewDidLoad() + + sr_setupUI() + self.resultView.isHidden = true + + Task { + await self.viewModel.requestHotSearchData() + } + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.navigationController?.setNavigationBarHidden(true, animated: true) + } + + private func search(_ text: String) { + if text.isEmpty { + homeView.isHidden = false + resultView.isHidden = true + } else { + textView.resignFirstResponder() + homeView.isHidden = true + resultView.isHidden = false + resultView.search(text) + self.viewModel.addSearchRecord(text) + } + + } + +} + +extension SRSearchViewController { + + private func sr_setupUI() { + view.addSubview(returnButton) + view.addSubview(textView) + view.addSubview(homeView) + view.addSubview(resultView) + + + returnButton.snp.makeConstraints { make in + make.width.height.equalTo(44) + make.left.equalToSuperview().offset(10) + make.top.equalToSuperview().offset(UIScreen.safeTop) + } + + textView.snp.makeConstraints { make in + make.left.equalTo(returnButton.snp.right).offset(10) + make.right.equalToSuperview().offset(-15) + make.centerY.equalTo(returnButton) + } + + homeView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.bottom.equalToSuperview() + make.top.equalTo(returnButton.snp.bottom) + } + + resultView.snp.makeConstraints { make in + make.edges.equalTo(homeView) + } + } + +} diff --git a/SynthReel/Class/Home/VM/SRHomeMenuDataSource.swift b/SynthReel/Class/Home/VM/SRHomeMenuDataSource.swift new file mode 100644 index 0000000..51bb545 --- /dev/null +++ b/SynthReel/Class/Home/VM/SRHomeMenuDataSource.swift @@ -0,0 +1,36 @@ +// +// SRHomeMenuDataSource.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/14. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import JXSegmentedView + + +class SRHomeMenuDataSource: JXSegmentedTitleGradientDataSource { + + nonisolated override init() { + super.init() + } + + + + nonisolated override func registerCellClass(in segmentedView: JXSegmentedView) { + MainActor.assumeIsolated { + segmentedView.collectionView.register(SRHomeMenuCell.self, forCellWithReuseIdentifier: "SRHomeMenuCell") + } + } + + nonisolated override func segmentedView(_ segmentedView: JXSegmentedView, cellForItemAt index: Int) -> JXSegmentedBaseCell { + return MainActor.assumeIsolated { + let cell = segmentedView.dequeueReusableCell(withReuseIdentifier: "SRHomeMenuCell", at: index) + return cell + } + } + + + +} diff --git a/SynthReel/Class/Home/VM/SRHomeViewModel.swift b/SynthReel/Class/Home/VM/SRHomeViewModel.swift new file mode 100644 index 0000000..d96cc6a --- /dev/null +++ b/SynthReel/Class/Home/VM/SRHomeViewModel.swift @@ -0,0 +1,107 @@ +// +// SRHomeViewModel.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/14. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +class SRHomeViewModel: NSObject { + + lazy var categoryArr: [SRCategoryModel] = [] + lazy var categoryTitleArr: [String] = [] + + lazy var moduleArr: [SRHomeModuleItem] = [] + + func requestCategoryList() async { + if let list = await SRHomeApi.requestCategoryList() { + self.categoryArr = list + var titles: [String] = [] + self.categoryArr.forEach { + titles.append($0.name ?? "") + } + self.categoryTitleArr = titles + } + } + + func requestModuleList() async { + guard let list = await SRHomeApi.requestHomeModulesData() else { return } + + /* + 第一项 get_details_recommand + popular = home_v3_recommand + updates = week_ranking + Binge-Worthy = week_highest_recommend + Viral Hits = highest_payment_hot_video + Premiere Now = new_recommand + */ + + var bannerItem: SRHomeModuleItem? = nil + var detailsRecommandItem: SRHomeModuleItem? = nil + var popularItem: SRHomeModuleItem? = nil + var updatesItem: SRHomeModuleItem? = nil + var bingeWorthyItem: SRHomeModuleItem? = nil + var viralHitsItem: SRHomeModuleItem? = nil + var premiereNowItem: SRHomeModuleItem? = nil + + + list.forEach { + switch $0.module_key { + case .banner: + bannerItem = $0 + + case .detailsRecommand: + detailsRecommandItem = $0 + + case .popular: + popularItem = $0 + + case .updates: + updatesItem = $0 + + case .bingeWorthy: + bingeWorthyItem = $0 + + case .viralHits: + viralHitsItem = $0 + + case .premiereNow: + premiereNowItem = $0 + + default: + break + } + } + + moduleArr.removeAll() + + if let item = bannerItem { + moduleArr.append(item) + } + if let item = detailsRecommandItem { + moduleArr.append(item) + } + if let item = popularItem { + moduleArr.append(item) + } + if let item = updatesItem { + moduleArr.append(item) + } + if let item = bingeWorthyItem { + moduleArr.append(item) + } + if let item = viralHitsItem { + moduleArr.append(item) + } + if let item = premiereNowItem { + moduleArr.append(item) + } + + + + } + + +} diff --git a/SynthReel/Class/Home/VM/SRSearchViewModel.swift b/SynthReel/Class/Home/VM/SRSearchViewModel.swift new file mode 100644 index 0000000..68b837a --- /dev/null +++ b/SynthReel/Class/Home/VM/SRSearchViewModel.swift @@ -0,0 +1,53 @@ +// +// SRSearchViewModel.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +class SRSearchViewModel: NSObject { + + static let searchRecordUserDefaultKey = "SRSearchViewModel.searchRecordUserDefaultKey" + + @objc dynamic private(set) var recordList: [String] = (UserDefaults.standard.object(forKey: SRSearchViewModel.searchRecordUserDefaultKey) as? [String]) ?? [] + + @objc dynamic private(set) lazy var hotDataArr: [SRShortModel] = [] + + 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: SRSearchViewModel.searchRecordUserDefaultKey) + } + + func clearSearchRecord() { + recordList.removeAll() + UserDefaults.standard.set(recordList, forKey: SRSearchViewModel.searchRecordUserDefaultKey) + } + + ///获取热门搜索 + func requestHotSearchData() async { + if let list = await SRHomeApi.requestHotSearchData(), list.count > 0 { + self.hotDataArr = list + } + } + + +} diff --git a/SynthReel/Class/MyShort/VC/SRMyShortViewController.swift b/SynthReel/Class/MyShort/VC/SRMyShortViewController.swift new file mode 100644 index 0000000..dc560bb --- /dev/null +++ b/SynthReel/Class/MyShort/VC/SRMyShortViewController.swift @@ -0,0 +1,37 @@ +// +// SRMyShortViewController.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/20. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SRMyShortViewController: SRViewController { + + override func viewDidLoad() { + super.viewDidLoad() + self.backgroundImageView.image = UIImage(named: "my_short_bg_image") + self.backgroundImageView.contentMode = .scaleAspectFit + + sr_setupUI() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.navigationController?.setNavigationBarHidden(true, animated: true) + } + +} + +extension SRMyShortViewController { + private func sr_setupUI() { + + self.backgroundImageView.snp.remakeConstraints { make in + make.edges.equalToSuperview() + } + } + +} diff --git a/SynthReel/Class/Player/M/SRShortDetailModel.swift b/SynthReel/Class/Player/M/SRShortDetailModel.swift new file mode 100644 index 0000000..25a3606 --- /dev/null +++ b/SynthReel/Class/Player/M/SRShortDetailModel.swift @@ -0,0 +1,20 @@ +// +// SRShortDetailModel.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SmartCodable + +class SRShortDetailModel: NSObject, SmartCodable { + required override init() { } + + var episodeList: [SRVideoInfoModel]? + var video_info: SRVideoInfoModel? + var shortPlayInfo: SRShortModel? + var is_collect: Bool? + var share_coin: Int? +} diff --git a/SynthReel/Class/Player/M/SRShortModel.swift b/SynthReel/Class/Player/M/SRShortModel.swift new file mode 100644 index 0000000..cc752f3 --- /dev/null +++ b/SynthReel/Class/Player/M/SRShortModel.swift @@ -0,0 +1,58 @@ +// +// SRShortModel.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/14. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SmartCodable + +class SRShortModel: NSObject, SmartCodable { + + required override init() { } + + var id: String? + var sr_description: String? + var name: String? + var watch_total: Int? + var collect_total: Int? + var current_episode: String? + var short_play_video_id: String? + var image_url: String? + var is_collect: Bool? + var episode_total: Int? + var horizontally_img: String? + var category: [String]? + var video_url: String? + var categoryList: [SRCategoryModel]? + var short_play_id: String? + var video_info: SRVideoInfoModel? + + @SmartIgnored + var cellHeight: CGFloat = 0 + + + static func mappingForKey() -> [SmartKeyTransformer]? { + return [ + CodingKeys.sr_description <--- ["description", "short_video_description"], + CodingKeys.name <--- ["short_video_title", "name"] + ] + } +} + +class SRVideoInfoModel: NSObject, SmartCodable { + required override init() { } + + var episode: String? + var short_play_id: String? + var coins: Int? + var video_url: String? + ///是否锁定,购买后解锁 + var is_lock: Bool? + var short_play_video_id: String? + ///播放进度,毫秒 + var play_seconds: Int? + var image_url: String? +} diff --git a/SynthReel/Class/Player/V/SREpSelectorCell.swift b/SynthReel/Class/Player/V/SREpSelectorCell.swift new file mode 100644 index 0000000..cfbec62 --- /dev/null +++ b/SynthReel/Class/Player/V/SREpSelectorCell.swift @@ -0,0 +1,91 @@ +// +// SREpSelectorCell.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/19. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SnapKit + +class SREpSelectorCell: UICollectionViewCell { + + + var model: SRVideoInfoModel? { + didSet { + numLabel.text = model?.episode + } + } + + var sr_isSelected: Bool = false { + didSet { + if sr_isSelected { + numLabel.textColors = [UIColor.srGreen.cgColor, UIColor.srBlue.cgColor] + boderView.isHidden = false + } else { + numLabel.textColors = [UIColor.white.cgColor, UIColor.white.cgColor] + boderView.isHidden = true + } + } + } + + lazy var numLabel: SRLabel = { + let label = SRLabel() + label.font = .font(ofSize: 14, weight: .regular) + label.textStartPoint = .init(x: 0.5, y: 0) + label.textEndPoint = .init(x: 0.5, y: 1) + return label + }() + + lazy var boderView: SRGradientView = { + let view = SRGradientView() + view.colors = [UIColor.srGreen.cgColor, UIColor.srBlue.cgColor] + view.startPoint = .init(x: 0.5, y: 0) + view.endPoint = .init(x: 0.5, y: 1) + view.layer.cornerRadius = 10 + view.layer.masksToBounds = true + return view + }() + + lazy var boderLayer: CAShapeLayer = { + let layer = CAShapeLayer() + return layer + }() + + override init(frame: CGRect) { + super.init(frame: frame) + contentView.layer.cornerRadius = 10 + contentView.layer.masksToBounds = true + contentView.backgroundColor = ._1_B_1_B_1_B + boderLayer.fillColor = contentView.backgroundColor?.cgColor // 设置为透明填充,实现镂空效果 + + + contentView.addSubview(boderView) + boderView.layer.addSublayer(boderLayer) + contentView.addSubview(numLabel) + + numLabel.snp.makeConstraints { make in + make.center.equalToSuperview() + } + + boderView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + } + + + override func layoutSubviews() { + super.layoutSubviews() + let size = self.bounds.size + let boderWidth: CGFloat = 1 + + boderLayer.path = UIBezierPath(roundedRect: .init(x: boderWidth, y: boderWidth, width: size.width - boderWidth * 2, height: size.height - boderWidth * 2), cornerRadius: 9.5).cgPath + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/SynthReel/Class/Player/V/SREpSelectorView.swift b/SynthReel/Class/Player/V/SREpSelectorView.swift new file mode 100644 index 0000000..64aab68 --- /dev/null +++ b/SynthReel/Class/Player/V/SREpSelectorView.swift @@ -0,0 +1,197 @@ +// +// SREpSelectorView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import HWPanModal +import SnapKit + +class SREpSelectorView: SRPanModalContentView { + + var didSelected: ((_ index: Int) -> Void)? + + var model: SRShortDetailModel? { + didSet { + coverImageView.sr_setImage(model?.shortPlayInfo?.image_url) + shortNameLabel.text = model?.shortPlayInfo?.name + desLabel.text = model?.shortPlayInfo?.sr_description + + subtitleLabel.text = "all_episodes_text".localizedReplace(text: "\(model?.shortPlayInfo?.episode_total ?? 0)") + + if let text = model?.shortPlayInfo?.category?.first, text.count > 0 { + cagetoryLabel.text = "#" + text + } else { + cagetoryLabel.text = "" + } + self.collectionView.reloadData() + } + } + var selectedIndex: Int = 0 { + didSet { + self.collectionView.reloadData() + } + } + + lazy var coverBgView = UIImageView(image: UIImage(named: "ep_cover_bg_image")) + lazy var coverImageView: UIImageView = { + let imageView = SRImageView() + imageView.layer.cornerRadius = 2 + return imageView + }() + + lazy var shortNameLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 15, weight: .semibold) + label.textColor = .srBlue + return label + }() + + lazy var cagetoryLabel: SRLabel = { + let label = SRLabel() + label.font = .font(ofSize: 12, weight: .regular) + label.textColors = [UIColor.srGreen.cgColor, UIColor.srBlue.cgColor] + label.textStartPoint = .init(x: 0.5, y: 0) + label.textEndPoint = .init(x: 0.5, y: 1) + return label + }() + + lazy var desLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .regular) + label.textColor = .A_6_A_6_A_6 + label.numberOfLines = 3 + return label + }() + + lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 15, weight: .medium) + label.textColor = .white + label.text = "Select Episode".localized + return label + }() + + lazy var subtitleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .regular) + label.textColor = .CCCCCC + return label + }() + + lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let itemWidth = (UIScreen.width - 30 - 40) / 5 + + let layout = UICollectionViewFlowLayout() + layout.minimumLineSpacing = 10 + layout.minimumInteritemSpacing = 10 + layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15) + layout.itemSize = .init(width: floor(itemWidth), height: 50) + return layout + }() + + lazy var collectionView: SRCollectionView = { + let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.showsVerticalScrollIndicator = false + collectionView.contentInset = .init(top: 0, left: 0, bottom: UIScreen.safeBottom + 10, right: 0) + collectionView.register(SREpSelectorCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + sr_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + + +extension SREpSelectorView { + + private func sr_setupUI() { + addSubview(coverBgView) + addSubview(coverImageView) + addSubview(shortNameLabel) + addSubview(cagetoryLabel) + addSubview(desLabel) + addSubview(titleLabel) + addSubview(subtitleLabel) + addSubview(collectionView) + + coverBgView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.top.equalToSuperview().offset(18) + } + + coverImageView.snp.makeConstraints { make in + make.center.equalTo(coverBgView) + make.width.equalTo(63) + make.height.equalTo(84) + } + + shortNameLabel.snp.makeConstraints { make in + make.left.equalTo(coverImageView.snp.right).offset(10) + make.top.equalToSuperview().offset(24) + make.right.lessThanOrEqualToSuperview().offset(-15) + } + + cagetoryLabel.snp.makeConstraints { make in + make.left.equalTo(shortNameLabel) + make.top.equalTo(shortNameLabel.snp.bottom).offset(8) + } + + desLabel.snp.makeConstraints { make in + make.left.equalTo(shortNameLabel) + make.right.lessThanOrEqualToSuperview().offset(-15) + make.top.equalTo(shortNameLabel.snp.bottom).offset(32) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.top.equalTo(coverBgView.snp.bottom).offset(16) + } + + subtitleLabel.snp.makeConstraints { make in + make.centerY.equalTo(titleLabel) + make.left.equalTo(titleLabel.snp.right).offset(3) + } + + collectionView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalToSuperview().offset(166) + make.bottom.equalToSuperview() + } + } + +} + +//MARK: UICollectionViewDelegate UICollectionViewDataSource +extension SREpSelectorView: UICollectionViewDelegate, UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SREpSelectorCell + cell.model = self.model?.episodeList?[indexPath.row] + cell.sr_isSelected = indexPath.row == self.selectedIndex + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.model?.episodeList?.count ?? 0 + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + self.didSelected?(indexPath.row) + Task { + await self.dismiss(animated: true) + } + } +} diff --git a/SynthReel/Class/Player/V/SRProgressView.swift b/SynthReel/Class/Player/V/SRProgressView.swift new file mode 100644 index 0000000..6e9976b --- /dev/null +++ b/SynthReel/Class/Player/V/SRProgressView.swift @@ -0,0 +1,216 @@ +// +// SRProgressView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import YYText +import YYCategories + +class SRProgressView: UIView { + + ///滑动开始 + var panStart: (() -> Void)? + + ///滑动中 + var panChange: ((_ progress: CGFloat) -> Void)? + + ///滑动完成回调 + var panFinish: ((_ progress: CGFloat) -> Void)? + + var progress: CGFloat = 0 { + didSet { + if !isPaning { + setNeedsDisplay() + } + } + } + + ///用来记录滑动时的当前进度 + private var tempProgress: CGFloat = 0 + + ///滑动进度 + private var panProgress: CGFloat = 0 + + var progressColor = UIColor.white.withAlphaComponent(0.2) + var currentProgress = UIColor.white + + var lineWidth: CGFloat = 3 + + ///加载中状态 + var isLoading = false { + didSet { + if isLoading { + if gradientTimer == nil { + gradientTimer = Timer.scheduledTimer(timeInterval: 0.05, target: YYTextWeakProxy(target: self), selector: #selector(handleGradientTimer), userInfo: nil, repeats: true) + } + } else { + gradientTimer?.invalidate() + gradientTimer = nil + } + } + } + + var insets: UIEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: 0) { + didSet { + self.invalidateIntrinsicContentSize() + setNeedsDisplay() + } + } + + private(set) lazy var panGesture: UIPanGestureRecognizer = { + let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(sender:))) + return pan + }() + + private(set) lazy var tagGesture: UITapGestureRecognizer = { + let tap = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(sender:))) + return tap + }() + + ///是否在滑动中 + private var isPaning: Bool = false + + private var gradientTimer: Timer? + + private var gradientValue: CGFloat = 0 + + override var intrinsicContentSize: CGSize { + return .init(width: UIScreen.width, height: lineWidth + insets.top + insets.bottom) + } + + override init(frame: CGRect) { + super.init(frame: frame) +// self.backgroundColor = progressColor + self.backgroundColor = .clear + + self.addGestureRecognizer(panGesture) + self.addGestureRecognizer(tagGesture) + + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + setNeedsDisplay() + } + + @objc private func handleGradientTimer() { + gradientValue += 0.1 + if gradientValue > 1 { + gradientValue = 0 + } + setNeedsDisplay() + } + + override func draw(_ rect: CGRect) { + super.draw(rect) + guard let context = UIGraphicsGetCurrentContext() else { return } + let width = rect.width + + let progressX = insets.left + let progressY = insets.top + let progressWidth = width - insets.left - insets.right + + if isLoading, !isPaning { + // 定义颜色空间 + let colorSpace = CGColorSpaceCreateDeviceRGB() + let colors: [CGColor] = [ + UIColor.clear.cgColor, + UIColor.white.cgColor, + UIColor.clear.cgColor + ] + let locations: [CGFloat] = [0.0, gradientValue, 1.0] + + guard let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: locations) else { + return + } + + let gradientRect = CGRect(x: progressX, + y: progressY, + width: progressWidth, + height: lineWidth) + + // 定义渐变的起点和终点 + let startPoint = CGPoint(x: rect.minX, y: rect.minY) + let endPoint = CGPoint(x: rect.maxX, y: rect.maxY) + + // 裁剪到渐变区域 + context.saveGState() + context.clip(to: gradientRect) + + // 绘制渐变 + context.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: []) + } else { + var progress = self.progress + if self.isPaning { + progress = self.panProgress + } + let currentProgressWidth = progressWidth * progress + + ///绘制进度 + let progressPath = UIBezierPath(roundedRect: CGRect(x: progressX, y: progressY, width: progressWidth, height: lineWidth), cornerRadius: lineWidth / 2) + context.addPath(progressPath.cgPath) + context.setFillColor(progressColor.cgColor) + context.fillPath() + + ///绘制当前进度 + let currentPath = UIBezierPath(roundedRect: CGRect(x: progressX, y: progressY, width: progressWidth * progress, height: lineWidth), cornerRadius: lineWidth / 2) + context.addPath(currentPath.cgPath) + context.setFillColor(currentProgress.cgColor) + context.fillPath() + + ///绘制一个点 + let path = UIBezierPath(arcCenter: .init(x: currentProgressWidth + progressX, y: progressY + lineWidth / 2), radius: 3, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true) + context.addPath(path.cgPath) + context.setFillColor(currentProgress.cgColor) + context.fillPath() + } + + + } + +} + +extension SRProgressView { + @objc func handlePanGesture(sender: UIPanGestureRecognizer) { + + switch sender.state { + case .began: + self.isPaning = true + self.tempProgress = self.progress + sender.setTranslation(CGPoint(x: 0, y: 0), in: self) + self.panStart?() + + case .changed: + let point = sender.translation(in: self) + let offsetX = point.x / (self.width - self.insets.left - self.insets.right) + self.panProgress = self.tempProgress + offsetX + if self.panProgress < 0 { + self.panProgress = 0 + } + self.panChange?(self.panProgress) + setNeedsDisplay() + + default: + self.isPaning = false + self.panFinish?(self.panProgress) + + self.panProgress = 0 + } + } + + @objc func handleTapGesture(sender: UITapGestureRecognizer) { + let point = sender.location(in: self) + let offsetX = (point.x - self.insets.left) / (self.width - self.insets.left - self.insets.right) + self.panFinish?(offsetX) + } +} diff --git a/SynthReel/Class/Player/V/SRRecommendPlayerCell.swift b/SynthReel/Class/Player/V/SRRecommendPlayerCell.swift new file mode 100644 index 0000000..3b77847 --- /dev/null +++ b/SynthReel/Class/Player/V/SRRecommendPlayerCell.swift @@ -0,0 +1,26 @@ +// +// SRRecommendPlayerCell.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/20. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import JXPlayer + +class SRRecommendPlayerCell: JXPlayerListCell { + + override var ControlViewClass: JXPlayerListControlView.Type { + return SRRecommendPlayerControlView.self + } + + override var model: Any? { + didSet { + let model = self.model as? SRShortModel + let videoInfo = model?.video_info + self.player.setPlayUrl(url: videoInfo?.video_url ?? "") + self.player.coverImageView?.sr_setImage(model?.image_url) + } + } +} diff --git a/SynthReel/Class/Player/V/SRRecommendPlayerControlView.swift b/SynthReel/Class/Player/V/SRRecommendPlayerControlView.swift new file mode 100644 index 0000000..3eec91e --- /dev/null +++ b/SynthReel/Class/Player/V/SRRecommendPlayerControlView.swift @@ -0,0 +1,226 @@ +// +// SRRecommendPlayerControlView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/20. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import JXPlayer +import SnapKit +import YYCategories + +class SRRecommendPlayerControlView: JXPlayerListControlView { + + override var viewModel: JXPlayerListViewModel? { + didSet { + self.viewModel?.addObserver(self, forKeyPath: "isPlaying", context: nil) + } + } + + override var model: Any? { + didSet { + let model = model as! SRShortModel + + shortNameLabel.text = model.name + + stackView.sr_removeAllArrangedSubview() + if let text = model.category?.first, text.count > 0 { + categoryLabel.text = "#" + text + stackView.addArrangedSubview(categoryLabel) + } + + if let text = model.sr_description, text.count > 0 { + desLabel.text = text + stackView.addArrangedSubview(desLabel) + } + + } + } + + override var isCurrent: Bool { + didSet { + updatePlayerViewStatus() + } + } + + lazy var controlerView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "short_progress_bg_image")) + imageView.isUserInteractionEnabled = true + return imageView + }() + + lazy var shortNameLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .semibold) + label.textColor = .srBlue + return label + }() + + lazy var stackView: UIStackView = { + let view = UIStackView() + view.axis = .vertical + view.spacing = 8 + return view + }() + + lazy var categoryLabel: SRLabel = { + let label = SRLabel() + label.font = .font(ofSize: 11, weight: .regular) + label.textColors = [UIColor.srGreen.cgColor, UIColor.srBlue.cgColor] + label.textStartPoint = .init(x: 0.5, y: 0) + label.textEndPoint = .init(x: 0.5, y: 1) + return label + }() + + lazy var desLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 11, weight: .regular) + label.textColor = .A_6_A_6_A_6 + label.numberOfLines = 2 + return label + }() + + lazy var epBgView: UIView = { + let view = SRGradientView() + view.colors = [UIColor._51_D_4_FF.withAlphaComponent(0.5).cgColor, UIColor._4_CFFD_4.withAlphaComponent(0.1).cgColor] + view.startPoint = .init(x: 0, y: 0.5) + view.endPoint = .init(x: 1, y: 0.5) + view.layer.cornerRadius = 2 + view.layer.masksToBounds = true + let tap = UITapGestureRecognizer { [weak self] _ in + guard let self = self else { return } + let vc = SRDetailPlayerViewController() + vc.shortId = (self.model as? SRShortModel)?.short_play_id + self.viewController?.navigationController?.pushViewController(vc, animated: true) + } + view.addGestureRecognizer(tap) + return view + }() + + lazy var epIconImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "ep_icon_02")) + imageView.setContentHuggingPriority(.required, for: .horizontal) + imageView.setContentCompressionResistancePriority(.required, for: .horizontal) + return imageView + }() + + lazy var epTextLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .regular) + label.textColor = .white + label.text = "recommend_ep_text".localized + return label + }() + + lazy var indicatorImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "arrow_right_icon_02")) + imageView.setContentHuggingPriority(.required, for: .horizontal) + imageView.setContentCompressionResistancePriority(.required, for: .horizontal) + return imageView + }() + + lazy var playerImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "play_icon_02")) + imageView.isHidden = true + return imageView + }() + + deinit { + self.viewModel?.removeObserver(self, forKeyPath: "isPlaying") + } + + override init(frame: CGRect) { + super.init(frame: frame) + sr_setupUI() + + let tap = UITapGestureRecognizer { [weak self] _ in + guard let self = self else { return } + self.viewModel?.userSwitchPlayAndPause() + } + self.addGestureRecognizer(tap) + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "isPlaying" { + updatePlayerViewStatus() + } + } + + + func updatePlayerViewStatus() { + if self.viewModel?.isPlaying == true || !isCurrent { + playerImageView.isHidden = true + } else { + playerImageView.isHidden = false + } + + } + +} + +extension SRRecommendPlayerControlView { + + private func sr_setupUI() { + addSubview(controlerView) + controlerView.addSubview(shortNameLabel) + controlerView.addSubview(stackView) + controlerView.addSubview(epBgView) + epBgView.addSubview(epIconImageView) + epBgView.addSubview(epTextLabel) + epBgView.addSubview(indicatorImageView) + addSubview(playerImageView) + + controlerView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().offset(-10) + } + + shortNameLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.right.lessThanOrEqualToSuperview().offset(-12) + make.top.equalToSuperview().offset(13) + } + + stackView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.right.lessThanOrEqualToSuperview().offset(-12) + make.top.equalTo(shortNameLabel.snp.bottom).offset(8) + make.bottom.equalToSuperview().offset(-52) + } + + epBgView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.right.equalToSuperview().offset(-12) + make.bottom.equalToSuperview().offset(-18) + make.height.equalTo(26) + } + + epIconImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(8) + } + + epTextLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalTo(epIconImageView.snp.right).offset(4) + make.right.lessThanOrEqualTo(self.indicatorImageView.snp.left).offset(-5) + } + + indicatorImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().offset(-12) + } + + playerImageView.snp.makeConstraints { make in + make.center.equalToSuperview() + } + } + +} diff --git a/SynthReel/Class/Player/V/SRShortDetailControlView.swift b/SynthReel/Class/Player/V/SRShortDetailControlView.swift new file mode 100644 index 0000000..febdfb7 --- /dev/null +++ b/SynthReel/Class/Player/V/SRShortDetailControlView.swift @@ -0,0 +1,239 @@ +// +// SRShortDetailControlView.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import JXPlayer +import SnapKit + +class SRShortDetailControlView: JXPlayerListControlView { + + var sr_viewModel: SRShortPlayerViewModel? { + return self.viewModel as? SRShortPlayerViewModel + } + + override var viewModel: JXPlayerListViewModel? { + didSet { + self.viewModel?.addObserver(self, forKeyPath: "isPlaying", context: nil) + } + } + + var shortModel: SRShortModel? { + didSet { + titleLabel.text = shortModel?.name + collectButton.isSelected = shortModel?.is_collect == true + } + } + + override var durationTime: TimeInterval { + didSet { + updateProgress() + let (_, m, s) = Int(durationTime).formatTimeGroup() + totalTimeLabel.text = "\(m):\(s)" + } + } + + override var currentTime: TimeInterval { + didSet { + updateProgress() + let (_, m, s) = Int(currentTime).formatTimeGroup() + currentTimeLabel.text = "\(m):\(s)" + } + } + + override var isLoading: Bool { + didSet { + progressView.isLoading = isLoading + } + } + + lazy var progressBgView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "short_progress_bg_image")) + imageView.isUserInteractionEnabled = true + return imageView + }() + + lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .medium) + label.textColor = .srBlue + return label + }() + + lazy var progressView: SRProgressView = { + let view = SRProgressView() + view.insets = .init(top: 10, left: 5, bottom: 10, right: 5) + view.panFinish = { [weak self] progress in + guard let self = self else { return } + self.viewModel?.seekTo(Float(progress)) + } + return view + }() + + lazy var totalTimeLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 10, weight: .regular) + label.textColor = .DFDFDF + label.text = "00:00" + return label + }() + + lazy var currentTimeLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 10, weight: .regular) + label.textColor = .DFDFDF + label.text = "00:00" + return label + }() + + lazy var epButton: UIButton = { + let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + self.sr_viewModel?.onEpSelectorView() + })) + button.setImage(UIImage(named: "ep_icon_01"), for: .normal) + return button + }() + + lazy var collectButton: UIButton = { + let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + guard let shortId = self.shortModel?.short_play_id else { return } + let videoId = (self.model as? SRVideoInfoModel)?.short_play_video_id + let isCollect = !(self.shortModel?.is_collect ?? false) + + Task { + await SRShortApi.requestShortCollect(shortId: shortId, videoId: videoId, isCollect: isCollect) + } + })) + button.setImage(UIImage(named: "collect_icon_01"), for: .normal) + button.setImage(UIImage(named: "collect_icon_01_selected"), for: .selected) + button.setImage(UIImage(named: "collect_icon_01_selected"), for: [.selected, .highlighted]) + return button + }() + + lazy var playerImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "play_icon_02")) + imageView.isHidden = true + return imageView + }() + + deinit { + self.viewModel?.removeObserver(self, forKeyPath: "isPlaying") + NotificationCenter.default.removeObserver(self) + } + + override init(frame: CGRect) { + super.init(frame: frame) + NotificationCenter.default.addObserver(self, selector: #selector(updateShortCollectStateNotification), name: SRShortApi.updateShortCollectStateNotification, object: nil) + + let tap = UITapGestureRecognizer { [weak self] _ in + guard let self = self else { return } + self.viewModel?.userSwitchPlayAndPause() + } + self.addGestureRecognizer(tap) + + sr_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func updateShortCollectStateNotification(sender: Notification) { + guard let userInfo = sender.userInfo else { return } + guard let shortId = userInfo["id"] as? String else { return } + guard let state = userInfo["state"] as? Bool else { return } + guard shortId == self.shortModel?.short_play_id else { return } + self.shortModel?.is_collect = state + + collectButton.isSelected = state + } + + private func updateProgress() { + guard durationTime > 0 else { + progressView.progress = 0 + return + } + progressView.progress = currentTime / durationTime + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "isPlaying" { + updatePlayerViewStatus() + } + } + + + func updatePlayerViewStatus() { + if self.viewModel?.isPlaying == true || !isCurrent { + playerImageView.isHidden = true + } else { + playerImageView.isHidden = false + } + + } + +} + +extension SRShortDetailControlView { + + private func sr_setupUI() { + addSubview(progressBgView) + progressBgView.addSubview(titleLabel) + progressBgView.addSubview(progressView) + progressBgView.addSubview(totalTimeLabel) + progressBgView.addSubview(currentTimeLabel) + addSubview(epButton) + addSubview(collectButton) + addSubview(playerImageView) + + progressBgView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().offset(-(UIScreen.safeBottom + 5)) + make.height.equalTo(88) + } + + titleLabel.snp.makeConstraints { make in + make.centerY.equalTo(progressBgView.snp.top).offset(23) + make.left.equalToSuperview().offset(9) + make.right.lessThanOrEqualToSuperview().offset(-9) + } + + progressView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(4) + make.right.equalToSuperview().offset(-6) + make.bottom.equalToSuperview().offset(-30) + } + + totalTimeLabel.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-11) + make.bottom.equalToSuperview().offset(-24) + } + + currentTimeLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(9) + make.bottom.equalToSuperview().offset(-24) + } + + epButton.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-15) + make.bottom.equalTo(progressBgView.snp.top).offset(-44) + } + + collectButton.snp.makeConstraints { make in + make.centerX.equalTo(epButton) + make.bottom.equalTo(epButton.snp.top).offset(-25) + } + + playerImageView.snp.makeConstraints { make in + make.center.equalToSuperview() + } + } + +} diff --git a/SynthReel/Class/Player/V/SRShortDetailPlayerCell.swift b/SynthReel/Class/Player/V/SRShortDetailPlayerCell.swift new file mode 100644 index 0000000..60e93f7 --- /dev/null +++ b/SynthReel/Class/Player/V/SRShortDetailPlayerCell.swift @@ -0,0 +1,43 @@ +// +// SRShortDetailPlayerCell.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import JXPlayer + +class SRShortDetailPlayerCell: JXPlayerListCell { + + override var ControlViewClass: JXPlayerListControlView.Type { + return SRShortDetailControlView.self + } + + var sr_controlView: SRShortDetailControlView { + return self.controlView as! SRShortDetailControlView + } + + var sr_viewModel: SRShortPlayerViewModel? { + return self.viewModel as? SRShortPlayerViewModel + } + + override var model: Any? { + didSet { + let model = self.model as? SRVideoInfoModel + self.player.setPlayUrl(url: model?.video_url ?? "") + +// self.lockView.isHidden = !(model?.is_lock ?? true) +// lockView.videoInfo = model + } + } + + var shortModel: SRShortModel? { + didSet { + self.sr_controlView.shortModel = shortModel + self.player.coverImageView?.sr_setImage(shortModel?.image_url) + } + } + +} diff --git a/SynthReel/Class/Player/VC/SRDetailPlayerViewController.swift b/SynthReel/Class/Player/VC/SRDetailPlayerViewController.swift new file mode 100644 index 0000000..73a3a20 --- /dev/null +++ b/SynthReel/Class/Player/VC/SRDetailPlayerViewController.swift @@ -0,0 +1,107 @@ +// +// SRDetailPlayerViewController.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/17. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import JXPlayer +import SnapKit + +class SRDetailPlayerViewController: JXPlayerListViewController { + + var shortId: String? { + set { + sr_viewModel.shortId = newValue ?? "0" + } + get { + return sr_viewModel.shortId + } + } + + + override var ViewModelClass: JXPlayerListViewModel.Type { + return SRShortPlayerViewModel.self + } + + var sr_viewModel: SRShortPlayerViewModel { + return self.viewModel as! SRShortPlayerViewModel + } + + lazy var returnButton: UIButton = { + let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + self.sr_handleNavigationBack() + })) + button.setImage(UIImage(named: "arrow_left_icon_01"), for: .normal) + return button + }() + + deinit { + srPrint(message: "销毁") + } + + override func viewDidLoad() { + super.viewDidLoad() + self.view.backgroundColor = .black + self.register(SRShortDetailPlayerCell.self, forCellWithReuseIdentifier: "SRShortDetailPlayerCell") + self.delegate = self + self.dataSource = self + + sr_setupUI() + + Task { + await self.sr_viewModel.requestShortDetail() + } + } + + override func play() { + let videoInfo = self.viewModel.currentCell?.model as? SRVideoInfoModel + super.play() + + Task { + await SRShortApi.requestCreatePlayHistory(shortId: videoInfo?.short_play_id, videoId: videoInfo?.short_play_video_id) + } + } + +} + +extension SRDetailPlayerViewController { + + private func sr_setupUI() { + view.addSubview(returnButton) + + returnButton.snp.makeConstraints { make in + make.height.equalTo(44) + make.width.equalTo(44) + make.left.equalToSuperview().offset(10) + make.top.equalToSuperview().offset(UIScreen.safeTop) + } + } +} + +//MARK: JXPlayerListViewControllerDelegate JXPlayerListViewControllerDataSource +extension SRDetailPlayerViewController: JXPlayerListViewControllerDelegate, JXPlayerListViewControllerDataSource { + func jx_playerListViewController(_ viewController: JXPlayerListViewController, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = self.dequeueReusableCell(withReuseIdentifier: "SRShortDetailPlayerCell", for: indexPath) as! SRShortDetailPlayerCell + cell.model = self.sr_viewModel.dataArr[indexPath.section].episodeList?[indexPath.row] + cell.shortModel = self.sr_viewModel.dataArr[indexPath.section].shortPlayInfo + return cell + } + + func jx_playerListViewController(_ viewController: JXPlayerListViewController, numberOfItemsInSection section: Int) -> Int { + return self.sr_viewModel.dataArr[section].episodeList?.count ?? 0 + } + + func jx_numberOfSections(in viewController: JXPlayerListViewController) -> Int { + return self.sr_viewModel.dataArr.count + } + + func jx_playerListViewController(_ viewController: JXPlayerListViewController, didChangeIndexPathForVisible indexPath: IndexPath) { + if let view = self.sr_viewModel.popView as? SREpSelectorView { + view.selectedIndex = indexPath.row + } + } +} diff --git a/SynthReel/Class/Player/VC/SRRecommendPlayerViewController.swift b/SynthReel/Class/Player/VC/SRRecommendPlayerViewController.swift new file mode 100644 index 0000000..475e8fe --- /dev/null +++ b/SynthReel/Class/Player/VC/SRRecommendPlayerViewController.swift @@ -0,0 +1,61 @@ +// +// SRRecommendPlayerViewController.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/20. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import JXPlayer + +class SRRecommendPlayerViewController: JXPlayerListViewController { + + + override var ViewModelClass: JXPlayerListViewModel.Type { + return SRRecommendPlayerViewModel.self + } + + override var contentSize: CGSize { + return .init(width: UIScreen.width, height: UIScreen.height - UIScreen.tabBarHeight) + } + + var sr_viewModel: SRRecommendPlayerViewModel { + return self.viewModel as! SRRecommendPlayerViewModel + } + + override func viewDidLoad() { + super.viewDidLoad() + self.register(SRRecommendPlayerCell.self, forCellWithReuseIdentifier: "SRRecommendPlayerCell") + + self.delegate = self + self.dataSource = self + + Task { + await self.sr_viewModel.requestRecommendList(page: 1) + } + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.navigationController?.setNavigationBarHidden(true, animated: true) + } + + +} + +//MARK: JXPlayerListViewControllerDelegate JXPlayerListViewControllerDataSource +extension SRRecommendPlayerViewController: JXPlayerListViewControllerDelegate, JXPlayerListViewControllerDataSource { + func jx_playerListViewController(_ viewController: JXPlayerListViewController, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = self.dequeueReusableCell(withReuseIdentifier: "SRRecommendPlayerCell", for: indexPath) as! SRRecommendPlayerCell + cell.model = self.sr_viewModel.dataArr[indexPath.row] + return cell + } + + func jx_playerListViewController(_ viewController: JXPlayerListViewController, numberOfItemsInSection section: Int) -> Int { + return self.sr_viewModel.dataArr.count + } + + + +} diff --git a/SynthReel/Class/Player/VM/SRRecommendPlayerViewModel.swift b/SynthReel/Class/Player/VM/SRRecommendPlayerViewModel.swift new file mode 100644 index 0000000..3a1f449 --- /dev/null +++ b/SynthReel/Class/Player/VM/SRRecommendPlayerViewModel.swift @@ -0,0 +1,54 @@ +// +// SRRecommendPlayerViewModel.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/20. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import JXPlayer + +class SRRecommendPlayerViewModel: JXPlayerListViewModel { + + lazy var dataArr: [SRShortModel] = [] + + nonisolated required init() { + super.init() + } + + + private func addDataArr(dataArr: [SRShortModel]) { + guard dataArr.count > 0 else { return } + + var indexPaths: [IndexPath] = [] + var startRow = self.dataArr.count + + dataArr.forEach { _ in + indexPaths.append(IndexPath(row: startRow, section: 0)) + startRow += 1 + } + self.dataArr += dataArr + + CATransaction.setCompletionBlock(nil) + CATransaction.begin() + self.playerListVC?.collectionView.insertItems(at: indexPaths) + CATransaction.commit() + } + + func requestRecommendList(page: Int) async { + guard let dataArr = await SRHomeApi.requestHomeRecommendData(page: page) else { return } + + if page == 1 { + self.playerListVC?.clearData() + self.dataArr = dataArr + self.playerListVC?.reloadData { [weak self] in + + self?.playerListVC?.scrollToItem(indexPath: .init(row: 0, section: 0), animated: false) + } + } else { + self.addDataArr(dataArr: dataArr) + } + + } +} diff --git a/SynthReel/Class/Player/VM/SRShortPlayerViewModel.swift b/SynthReel/Class/Player/VM/SRShortPlayerViewModel.swift new file mode 100644 index 0000000..ff7c3bd --- /dev/null +++ b/SynthReel/Class/Player/VM/SRShortPlayerViewModel.swift @@ -0,0 +1,72 @@ +// +// SRShortPlayerViewModel.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/18. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import JXPlayer +import HWPanModal + +class SRShortPlayerViewModel: JXPlayerListViewModel { + + var shortId: String = "0" + + var dataArr: [SRShortDetailModel] = [] + + weak var popView: UIView? + + nonisolated required init() { + super.init() + } + + + func requestShortDetail(indexPath: IndexPath? = nil) async -> Int? { + let (model, code, _) = await SRShortApi.requestShortDetail(shortId) + guard let model = model else { return code } + + self.dataArr.removeAll() + self.dataArr.append(model) + + self.playerListVC?.reloadData { [weak self] in + guard let self = self else { return } + var targetIndexPath = IndexPath(row: 0, section: 0) + + if let indexPath = indexPath, indexPath.row < (model.episodeList?.count ?? 0) { + targetIndexPath = indexPath + } else if let videoInfo = model.video_info { + var row: Int? + model.episodeList?.enumerated().forEach { + if $1.short_play_video_id == videoInfo.short_play_video_id { + row = $0 + } + } + if let row = row { + targetIndexPath = .init(row: row, section: 0) + } + } + + self.playerListVC?.scrollToItem(indexPath: targetIndexPath, animated: false) + } + return code + } +} + + +extension SRShortPlayerViewModel { + + func onEpSelectorView() { + let view = SREpSelectorView() + view.model = self.dataArr[currentIndexPath.section] + view.selectedIndex = self.currentIndexPath.row + view.didSelected = { [weak self] index in + guard let self = self else { return } + self.playerListVC?.scrollToItem(indexPath: IndexPath(row: index, section: currentIndexPath.section), animated: false) + } + view.present(in: nil) + self.popView = view + } + +} diff --git a/SynthReel/Delegate/AppDelegate+Config.swift b/SynthReel/Delegate/AppDelegate+Config.swift new file mode 100644 index 0000000..e85f72c --- /dev/null +++ b/SynthReel/Delegate/AppDelegate+Config.swift @@ -0,0 +1,18 @@ +// +// AppDelegate+Config.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/13. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +extension AppDelegate { + + func setConfig() { + SRToast.config() + + } + +} diff --git a/SynthReel/Delegate/AppDelegate.swift b/SynthReel/Delegate/AppDelegate.swift new file mode 100644 index 0000000..17fcdfc --- /dev/null +++ b/SynthReel/Delegate/AppDelegate.swift @@ -0,0 +1,46 @@ +// +// AppDelegate.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + SRTool.appDelegate = self + + SRNetworkReachableManager.manager.startMonitoring() + + self.setConfig() + + + Task { + await SRAccountManager.manager.updateUserInfo() + } + + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/SynthReel/Delegate/SceneDelegate.swift b/SynthReel/Delegate/SceneDelegate.swift new file mode 100644 index 0000000..e40e33e --- /dev/null +++ b/SynthReel/Delegate/SceneDelegate.swift @@ -0,0 +1,65 @@ +// +// SceneDelegate.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + SRTool.sceneDelegate = self + guard let windowScene = (scene as? UIWindowScene) else { return } + SRTool.windowScene = windowScene + + window = UIWindow(windowScene: windowScene) + + window?.rootViewController = SRTabBarController() + window?.makeKeyAndVisible() + + + + NotificationCenter.default.addObserver(self, selector: #selector(abcd), name: NSNotification.Name(rawValue: "abcd"), object: nil) + } + + @objc private func abcd() { + window?.rootViewController = SRTabBarController() + window?.makeKeyAndVisible() + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/SynthReel/Libs/SRAccount/SRAccountManager.swift b/SynthReel/Libs/SRAccount/SRAccountManager.swift new file mode 100644 index 0000000..65cea8d --- /dev/null +++ b/SynthReel/Libs/SRAccount/SRAccountManager.swift @@ -0,0 +1,49 @@ +// +// SRAccountManager.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +class SRAccountManager: NSObject { + + + static let manager = SRAccountManager() + + private(set) var token = UserDefaults.sr_object(forKey: kSRAccountTokenDefaultsKey, as: SRAccountToken.self) + private(set) var userInfo = UserDefaults.sr_object(forKey: kSRUserInfoDefaultsKey, as: SRUserInfo.self) + + + func setAccountToken(_ token: SRAccountToken?) { + self.token = token + UserDefaults.sr_setObject(token, forKey: kSRAccountTokenDefaultsKey) + } + + + ///更新用户信息 + func updateUserInfo() async { +// Task { +// completer?() +// } + if let userInfo = await SRUserApi.requestUserInfo() { + self.userInfo = userInfo + UserDefaults.sr_setObject(userInfo, forKey: kSRUserInfoDefaultsKey) + NotificationCenter.default.post(name: SRAccountManager.userInfoUpdateNotification, object: nil) + } + + + } + +} + + +extension SRAccountManager { + + ///用户信息更新 + @objc static let userInfoUpdateNotification = NSNotification.Name(rawValue: "SRAccountManager.userInfoUpdateNotification") + ///登录状态发生变化 + @objc static let loginStatusChangeNotification = NSNotification.Name(rawValue: "SRAccountManager.loginStatusChangeNotification") +} diff --git a/SynthReel/Libs/SRAccount/SRAccountToken.swift b/SynthReel/Libs/SRAccount/SRAccountToken.swift new file mode 100644 index 0000000..fb2fd0b --- /dev/null +++ b/SynthReel/Libs/SRAccount/SRAccountToken.swift @@ -0,0 +1,42 @@ +// +// SRAccountToken.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SmartCodable + + +class SRAccountToken: NSObject, SmartCodable, NSSecureCoding { + + required override init() { } + + var auto_login: Int? + var token: String? + var customer_id: String? + + + static var supportsSecureCoding: Bool { + get { + return true + } + } + + func encode(with coder: NSCoder) { + coder.encode(token, forKey: "token") + coder.encode(customer_id, forKey: "customer_id") + coder.encode(auto_login, forKey: "auto_login") + } + + required init?(coder: NSCoder) { + super.init() + + token = coder.decodeObject(of: NSString.self, forKey: "token") as? String + customer_id = coder.decodeObject(of: NSString.self, forKey: "customer_id") as? String + auto_login = coder.decodeObject(of: NSNumber.self, forKey: "auto_login")?.intValue + } + +} diff --git a/SynthReel/Libs/SRAccount/SRUserInfo.swift b/SynthReel/Libs/SRAccount/SRUserInfo.swift new file mode 100644 index 0000000..c14c694 --- /dev/null +++ b/SynthReel/Libs/SRAccount/SRUserInfo.swift @@ -0,0 +1,66 @@ +// +// SRUserInfo.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit +import SmartCodable + +class SRUserInfo: NSObject, SmartCodable, NSSecureCoding { + + required override init() { } + + var id: String? + var avator: String? + var coin_left_total: Int? + var family_name: String? + var send_coin_left_total: Int? + var vip_end_time: TimeInterval? + var is_vip: Bool? + var customer_id: String? + var is_tourist: Bool? + + func getNickName() -> String { + if let name = family_name, !name.isEmpty { + return name + } else { + return "Visitor" + } + } + + var totalCoins: Int { + return (coin_left_total ?? 0) + (send_coin_left_total ?? 0) + } + + static var supportsSecureCoding: Bool { + return true + } + + func encode(with coder: NSCoder) { + coder.encode(id, forKey: "id") + coder.encode(customer_id, forKey: "customer_id") + coder.encode(is_tourist, forKey: "is_tourist") + coder.encode(avator, forKey: "avator") + coder.encode(family_name, forKey: "family_name") + coder.encode(coin_left_total, forKey: "coin_left_total") + coder.encode(send_coin_left_total, forKey: "send_coin_left_total") + coder.encode(is_vip, forKey: "is_vip") + coder.encode(vip_end_time, forKey: "vip_end_time") + } + + required init?(coder: NSCoder) { + super.init() + id = coder.decodeObject(of: NSString.self, forKey: "id") as? String + customer_id = coder.decodeObject(of: NSString.self, forKey: "customer_id") as? String + is_tourist = coder.decodeObject(of: NSNumber.self, forKey: "is_tourist")?.boolValue + avator = coder.decodeObject(of: NSString.self, forKey: "avator") as? String + family_name = coder.decodeObject(of: NSString.self, forKey: "family_name") as? String + coin_left_total = coder.decodeObject(of: NSNumber.self, forKey: "coin_left_total")?.intValue + send_coin_left_total = coder.decodeObject(of: NSNumber.self, forKey: "send_coin_left_total")?.intValue + is_vip = coder.decodeObject(of: NSNumber.self, forKey: "is_vip")?.boolValue + vip_end_time = coder.decodeObject(of: NSNumber.self, forKey: "vip_end_time")?.doubleValue + } +} diff --git a/SynthReel/Libs/SRDeviceID/SRDeviceId.swift b/SynthReel/Libs/SRDeviceID/SRDeviceId.swift new file mode 100644 index 0000000..76ccd84 --- /dev/null +++ b/SynthReel/Libs/SRDeviceID/SRDeviceId.swift @@ -0,0 +1,27 @@ +// +// SRDeviceId.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +class SRDeviceId { + + static let shared = SRDeviceId() + private let key = "com.synthreel.deviceid" + +// private init() {} + + lazy var id: String = { + if let savedID = SRKeychain.shared.read(key: key) { + return savedID + } else { + let newID = UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString + SRKeychain.shared.save(key: key, value: newID) + return newID + } + }() +} diff --git a/SynthReel/Libs/SRDeviceID/SRKeychain.swift b/SynthReel/Libs/SRDeviceID/SRKeychain.swift new file mode 100644 index 0000000..5b95e09 --- /dev/null +++ b/SynthReel/Libs/SRDeviceID/SRKeychain.swift @@ -0,0 +1,59 @@ +// +// SRKeychain.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/12. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +class SRKeychain { + static let shared = SRKeychain() + + func save(key: String, value: String) { + if let data = value.data(using: .utf8) { + // 先删除旧的 + let query = [ + kSecClass: kSecClassGenericPassword, + kSecAttrAccount: key + ] as CFDictionary + SecItemDelete(query) + + // 再保存新的 + let attributes = [ + kSecClass: kSecClassGenericPassword, + kSecAttrAccount: key, + kSecValueData: data, + kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock + ] as CFDictionary + + SecItemAdd(attributes, nil) + } + } + + func read(key: String) -> String? { + let query = [ + kSecClass: kSecClassGenericPassword, + kSecAttrAccount: key, + kSecReturnData: kCFBooleanTrue!, + kSecMatchLimit: kSecMatchLimitOne + ] as CFDictionary + + var dataTypeRef: AnyObject? + let status = SecItemCopyMatching(query, &dataTypeRef) + + if status == errSecSuccess, let data = dataTypeRef as? Data { + return String(data: data, encoding: .utf8) + } + return nil + } + + func delete(key: String) { + let query = [ + kSecClass: kSecClassGenericPassword, + kSecAttrAccount: key + ] as CFDictionary + SecItemDelete(query) + } +} diff --git a/SynthReel/Libs/SRHud/SRHud.swift b/SynthReel/Libs/SRHud/SRHud.swift new file mode 100644 index 0000000..cd0212c --- /dev/null +++ b/SynthReel/Libs/SRHud/SRHud.swift @@ -0,0 +1,22 @@ +// +// SRHud.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/13. +// Copyright © 2025 SR. All rights reserved. +// + +import SVProgressHUD + +struct SRHud { + + static func show(containerView: UIView? = nil, type: SVProgressHUDMaskType = .clear) { + SVProgressHUD.setContainerView(containerView) + SVProgressHUD.setDefaultMaskType(type) + SVProgressHUD.show() + } + + static func dismiss() { + SVProgressHUD.dismiss() + } +} diff --git a/SynthReel/Libs/SRHud/SRToast.swift b/SynthReel/Libs/SRHud/SRToast.swift new file mode 100644 index 0000000..0eaba30 --- /dev/null +++ b/SynthReel/Libs/SRHud/SRToast.swift @@ -0,0 +1,24 @@ +// +// SRToast.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/13. +// Copyright © 2025 SR. All rights reserved. +// + + +import Toast + +struct SRToast { + + static func config() { + CSToastManager.setTapToDismissEnabled(false) + CSToastManager.setDefaultDuration(2) + CSToastManager.setDefaultPosition(CSToastPositionCenter) + } + + static func show(text: String?) { + guard let text = text else { return } + SRTool.keyWindow?.makeToast(text) + } +} diff --git a/SynthReel/Libs/Tool/SRTool.swift b/SynthReel/Libs/Tool/SRTool.swift new file mode 100644 index 0000000..fa663ba --- /dev/null +++ b/SynthReel/Libs/Tool/SRTool.swift @@ -0,0 +1,56 @@ +// +// SRTool.swift +// SynthReel +// +// Created by 湖北秦九 on 2025/11/13. +// Copyright © 2025 SR. All rights reserved. +// + +import UIKit + +class SRTool { + + static var appDelegate: AppDelegate? + static var sceneDelegate: SceneDelegate? + + static var windowScene: UIWindowScene? + + + static var keyWindow: UIWindow? { + return windowScene?.keyWindow + } + + static var rootViewController: UIViewController? { + return keyWindow?.rootViewController + } + + ///获得启动图 + static var lanuchViewController: UIViewController? { + let storyboard = UIStoryboard(name: "LaunchScreen", bundle: nil) + let vc = storyboard.instantiateInitialViewController() + return vc + } + + static var topViewController: UIViewController? { + var resultVC: UIViewController? = self.rootViewController + if let rootNav = resultVC as? UINavigationController { + resultVC = rootNav.topViewController + } + + resultVC = self._topViewController(resultVC) + while resultVC?.presentedViewController != nil { + resultVC = self._topViewController(resultVC?.presentedViewController) + } + return resultVC + } + + private static func _topViewController(_ vc: UIViewController?) -> UIViewController? { + if vc is UINavigationController { + return _topViewController((vc as? UINavigationController)?.topViewController) + } else if vc is UITabBarController { + return _topViewController((vc as? UITabBarController)?.selectedViewController) + } else { + return vc + } + } +} diff --git a/SynthReel/Source/Assets.xcassets/AccentColor.colorset/Contents.json b/SynthReel/Source/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/AppIcon.appiconset/Contents.json b/SynthReel/Source/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..2305880 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Color/#010101.colorset/Contents.json b/SynthReel/Source/Assets.xcassets/Color/#010101.colorset/Contents.json new file mode 100644 index 0000000..525bf3d --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Color/#010101.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x01", + "green" : "0x01", + "red" : "0x01" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Color/#051B22.colorset/Contents.json b/SynthReel/Source/Assets.xcassets/Color/#051B22.colorset/Contents.json new file mode 100644 index 0000000..4764841 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Color/#051B22.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x22", + "green" : "0x1B", + "red" : "0x05" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Color/#1B1B1B.colorset/Contents.json b/SynthReel/Source/Assets.xcassets/Color/#1B1B1B.colorset/Contents.json new file mode 100644 index 0000000..a79bd7e --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Color/#1B1B1B.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x1B", + "green" : "0x1B", + "red" : "0x1B" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Color/#4CFFD4.colorset/Contents.json b/SynthReel/Source/Assets.xcassets/Color/#4CFFD4.colorset/Contents.json new file mode 100644 index 0000000..315f36b --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Color/#4CFFD4.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xD4", + "green" : "0xFF", + "red" : "0x4C" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Color/#51D4FF.colorset/Contents.json b/SynthReel/Source/Assets.xcassets/Color/#51D4FF.colorset/Contents.json new file mode 100644 index 0000000..60347a2 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Color/#51D4FF.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xD4", + "red" : "0x51" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Color/#7AF4E0.colorset/Contents.json b/SynthReel/Source/Assets.xcassets/Color/#7AF4E0.colorset/Contents.json new file mode 100644 index 0000000..cd89fb0 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Color/#7AF4E0.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xE0", + "green" : "0xF4", + "red" : "0x7A" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Color/#96E5FF.colorset/Contents.json b/SynthReel/Source/Assets.xcassets/Color/#96E5FF.colorset/Contents.json new file mode 100644 index 0000000..49dbcc5 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Color/#96E5FF.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xE5", + "red" : "0x96" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Color/#A6A6A6.colorset/Contents.json b/SynthReel/Source/Assets.xcassets/Color/#A6A6A6.colorset/Contents.json new file mode 100644 index 0000000..fc9f464 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Color/#A6A6A6.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xA6", + "green" : "0xA6", + "red" : "0xA6" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Color/#CCCCCC.colorset/Contents.json b/SynthReel/Source/Assets.xcassets/Color/#CCCCCC.colorset/Contents.json new file mode 100644 index 0000000..a200dfb --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Color/#CCCCCC.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xCC", + "green" : "0xCC", + "red" : "0xCC" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Color/#DFDFDF.colorset/Contents.json b/SynthReel/Source/Assets.xcassets/Color/#DFDFDF.colorset/Contents.json new file mode 100644 index 0000000..16fcfee --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Color/#DFDFDF.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xDF", + "green" : "0xDF", + "red" : "0xDF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Color/Contents.json b/SynthReel/Source/Assets.xcassets/Color/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Color/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Color/sr_blue.colorset/Contents.json b/SynthReel/Source/Assets.xcassets/Color/sr_blue.colorset/Contents.json new file mode 100644 index 0000000..60347a2 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Color/sr_blue.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xD4", + "red" : "0x51" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Color/sr_green.colorset/Contents.json b/SynthReel/Source/Assets.xcassets/Color/sr_green.colorset/Contents.json new file mode 100644 index 0000000..315f36b --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Color/sr_green.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xD4", + "green" : "0xFF", + "red" : "0x4C" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Contents.json b/SynthReel/Source/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/Contents.json b/SynthReel/Source/Assets.xcassets/Image/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/arrow_left_icon_01.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/arrow_left_icon_01.imageset/Contents.json new file mode 100644 index 0000000..50c3586 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/arrow_left_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "返回@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "返回@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/arrow_left_icon_01.imageset/返回@2x.png b/SynthReel/Source/Assets.xcassets/Image/arrow_left_icon_01.imageset/返回@2x.png new file mode 100644 index 0000000..e1a9ae9 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/arrow_left_icon_01.imageset/返回@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/arrow_left_icon_01.imageset/返回@3x.png b/SynthReel/Source/Assets.xcassets/Image/arrow_left_icon_01.imageset/返回@3x.png new file mode 100644 index 0000000..8b21f0a Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/arrow_left_icon_01.imageset/返回@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_01.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_01.imageset/Contents.json new file mode 100644 index 0000000..5c4d3b1 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_01.imageset/Frame@2x.png b/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_01.imageset/Frame@2x.png new file mode 100644 index 0000000..d714c46 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_01.imageset/Frame@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_01.imageset/Frame@3x.png b/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_01.imageset/Frame@3x.png new file mode 100644 index 0000000..0ccf3b2 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_01.imageset/Frame@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_02.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_02.imageset/Contents.json new file mode 100644 index 0000000..5c4d3b1 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_02.imageset/Frame@2x.png b/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_02.imageset/Frame@2x.png new file mode 100644 index 0000000..a388ab6 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_02.imageset/Frame@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_02.imageset/Frame@3x.png b/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_02.imageset/Frame@3x.png new file mode 100644 index 0000000..9c97adc Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/arrow_right_icon_02.imageset/Frame@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/background_image_01.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/background_image_01.imageset/Contents.json new file mode 100644 index 0000000..36577cc --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/background_image_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "bg@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "bg@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/background_image_01.imageset/bg@2x.png b/SynthReel/Source/Assets.xcassets/Image/background_image_01.imageset/bg@2x.png new file mode 100644 index 0000000..c577f57 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/background_image_01.imageset/bg@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/background_image_01.imageset/bg@3x.png b/SynthReel/Source/Assets.xcassets/Image/background_image_01.imageset/bg@3x.png new file mode 100644 index 0000000..8715df2 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/background_image_01.imageset/bg@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/cell_bg_image_01.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/cell_bg_image_01.imageset/Contents.json new file mode 100644 index 0000000..77dbf9f --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/cell_bg_image_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 3@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Rectangle 3@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/cell_bg_image_01.imageset/Rectangle 3@2x.png b/SynthReel/Source/Assets.xcassets/Image/cell_bg_image_01.imageset/Rectangle 3@2x.png new file mode 100644 index 0000000..e6440c5 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/cell_bg_image_01.imageset/Rectangle 3@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/cell_bg_image_01.imageset/Rectangle 3@3x.png b/SynthReel/Source/Assets.xcassets/Image/cell_bg_image_01.imageset/Rectangle 3@3x.png new file mode 100644 index 0000000..f7500a4 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/cell_bg_image_01.imageset/Rectangle 3@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/collect_icon_01.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/collect_icon_01.imageset/Contents.json new file mode 100644 index 0000000..2fcc7b2 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/collect_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "收藏@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "收藏@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/collect_icon_01.imageset/收藏@2x.png b/SynthReel/Source/Assets.xcassets/Image/collect_icon_01.imageset/收藏@2x.png new file mode 100644 index 0000000..1773eac Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/collect_icon_01.imageset/收藏@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/collect_icon_01.imageset/收藏@3x.png b/SynthReel/Source/Assets.xcassets/Image/collect_icon_01.imageset/收藏@3x.png new file mode 100644 index 0000000..ed871d2 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/collect_icon_01.imageset/收藏@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/collect_icon_01_selected.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/collect_icon_01_selected.imageset/Contents.json new file mode 100644 index 0000000..f34cd3c --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/collect_icon_01_selected.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "已收藏@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "已收藏@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/collect_icon_01_selected.imageset/已收藏@2x.png b/SynthReel/Source/Assets.xcassets/Image/collect_icon_01_selected.imageset/已收藏@2x.png new file mode 100644 index 0000000..1689e33 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/collect_icon_01_selected.imageset/已收藏@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/collect_icon_01_selected.imageset/已收藏@3x.png b/SynthReel/Source/Assets.xcassets/Image/collect_icon_01_selected.imageset/已收藏@3x.png new file mode 100644 index 0000000..4135cb5 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/collect_icon_01_selected.imageset/已收藏@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/delete_icon_01.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/delete_icon_01.imageset/Contents.json new file mode 100644 index 0000000..5c4d3b1 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/delete_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/delete_icon_01.imageset/Frame@2x.png b/SynthReel/Source/Assets.xcassets/Image/delete_icon_01.imageset/Frame@2x.png new file mode 100644 index 0000000..6933db6 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/delete_icon_01.imageset/Frame@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/delete_icon_01.imageset/Frame@3x.png b/SynthReel/Source/Assets.xcassets/Image/delete_icon_01.imageset/Frame@3x.png new file mode 100644 index 0000000..f0c2ce3 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/delete_icon_01.imageset/Frame@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/ep_cover_bg_image.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/ep_cover_bg_image.imageset/Contents.json new file mode 100644 index 0000000..4fb92bc --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/ep_cover_bg_image.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "bg border@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "bg border@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/ep_cover_bg_image.imageset/bg border@2x.png b/SynthReel/Source/Assets.xcassets/Image/ep_cover_bg_image.imageset/bg border@2x.png new file mode 100644 index 0000000..5271fba Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/ep_cover_bg_image.imageset/bg border@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/ep_cover_bg_image.imageset/bg border@3x.png b/SynthReel/Source/Assets.xcassets/Image/ep_cover_bg_image.imageset/bg border@3x.png new file mode 100644 index 0000000..58e8c10 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/ep_cover_bg_image.imageset/bg border@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/ep_icon_01.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/ep_icon_01.imageset/Contents.json new file mode 100644 index 0000000..6f5f509 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/ep_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "选集@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "选集@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/ep_icon_01.imageset/选集@2x.png b/SynthReel/Source/Assets.xcassets/Image/ep_icon_01.imageset/选集@2x.png new file mode 100644 index 0000000..60aa89f Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/ep_icon_01.imageset/选集@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/ep_icon_01.imageset/选集@3x.png b/SynthReel/Source/Assets.xcassets/Image/ep_icon_01.imageset/选集@3x.png new file mode 100644 index 0000000..59d2e30 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/ep_icon_01.imageset/选集@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/ep_icon_02.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/ep_icon_02.imageset/Contents.json new file mode 100644 index 0000000..d32a4ee --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/ep_icon_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "剧集@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "剧集@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/ep_icon_02.imageset/剧集@2x.png b/SynthReel/Source/Assets.xcassets/Image/ep_icon_02.imageset/剧集@2x.png new file mode 100644 index 0000000..7fb5894 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/ep_icon_02.imageset/剧集@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/ep_icon_02.imageset/剧集@3x.png b/SynthReel/Source/Assets.xcassets/Image/ep_icon_02.imageset/剧集@3x.png new file mode 100644 index 0000000..d182ba2 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/ep_icon_02.imageset/剧集@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/home_banner_bg.imageset/Border@2x.png b/SynthReel/Source/Assets.xcassets/Image/home_banner_bg.imageset/Border@2x.png new file mode 100644 index 0000000..b0a1dc6 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/home_banner_bg.imageset/Border@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/home_banner_bg.imageset/Border@3x.png b/SynthReel/Source/Assets.xcassets/Image/home_banner_bg.imageset/Border@3x.png new file mode 100644 index 0000000..3e01d9d Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/home_banner_bg.imageset/Border@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/home_banner_bg.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/home_banner_bg.imageset/Contents.json new file mode 100644 index 0000000..8c69c2d --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/home_banner_bg.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Border@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Border@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/home_binge_worthy_cell_image.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/home_binge_worthy_cell_image.imageset/Contents.json new file mode 100644 index 0000000..4fb92bc --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/home_binge_worthy_cell_image.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "bg border@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "bg border@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/home_binge_worthy_cell_image.imageset/bg border@2x.png b/SynthReel/Source/Assets.xcassets/Image/home_binge_worthy_cell_image.imageset/bg border@2x.png new file mode 100644 index 0000000..03bb69f Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/home_binge_worthy_cell_image.imageset/bg border@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/home_binge_worthy_cell_image.imageset/bg border@3x.png b/SynthReel/Source/Assets.xcassets/Image/home_binge_worthy_cell_image.imageset/bg border@3x.png new file mode 100644 index 0000000..4d072dc Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/home_binge_worthy_cell_image.imageset/bg border@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/home_list_bg_image.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/home_list_bg_image.imageset/Contents.json new file mode 100644 index 0000000..d293436 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/home_list_bg_image.imageset/Contents.json @@ -0,0 +1,50 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "bg border@2x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "bottom" : 208, + "left" : 246, + "right" : 266, + "top" : 371 + }, + "center" : { + "height" : 1, + "mode" : "tile", + "width" : 15 + }, + "mode" : "9-part" + }, + "scale" : "2x" + }, + { + "filename" : "bg border@3x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "bottom" : 201, + "left" : 369, + "right" : 376, + "top" : 464 + }, + "center" : { + "height" : 1, + "mode" : "tile", + "width" : 24 + }, + "mode" : "9-part" + }, + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/home_list_bg_image.imageset/bg border@2x.png b/SynthReel/Source/Assets.xcassets/Image/home_list_bg_image.imageset/bg border@2x.png new file mode 100644 index 0000000..794aa28 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/home_list_bg_image.imageset/bg border@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/home_list_bg_image.imageset/bg border@3x.png b/SynthReel/Source/Assets.xcassets/Image/home_list_bg_image.imageset/bg border@3x.png new file mode 100644 index 0000000..f43b648 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/home_list_bg_image.imageset/bg border@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image.imageset/Contents.json new file mode 100644 index 0000000..4fb92bc --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "bg border@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "bg border@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image.imageset/bg border@2x.png b/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image.imageset/bg border@2x.png new file mode 100644 index 0000000..5bbe863 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image.imageset/bg border@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image.imageset/bg border@3x.png b/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image.imageset/bg border@3x.png new file mode 100644 index 0000000..6615a06 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image.imageset/bg border@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image_selected.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image_selected.imageset/Contents.json new file mode 100644 index 0000000..4fb92bc --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image_selected.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "bg border@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "bg border@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image_selected.imageset/bg border@2x.png b/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image_selected.imageset/bg border@2x.png new file mode 100644 index 0000000..ba17a31 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image_selected.imageset/bg border@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image_selected.imageset/bg border@3x.png b/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image_selected.imageset/bg border@3x.png new file mode 100644 index 0000000..9be8894 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/home_menu_bg_image_selected.imageset/bg border@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/home_title_image.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/home_title_image.imageset/Contents.json new file mode 100644 index 0000000..db526de --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/home_title_image.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Title Border@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Title Border@3x 1.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/home_title_image.imageset/Title Border@2x.png b/SynthReel/Source/Assets.xcassets/Image/home_title_image.imageset/Title Border@2x.png new file mode 100644 index 0000000..96c7097 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/home_title_image.imageset/Title Border@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/home_title_image.imageset/Title Border@3x 1.png b/SynthReel/Source/Assets.xcassets/Image/home_title_image.imageset/Title Border@3x 1.png new file mode 100644 index 0000000..2053dc4 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/home_title_image.imageset/Title Border@3x 1.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/home_viral_hits_cell_image.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/home_viral_hits_cell_image.imageset/Contents.json new file mode 100644 index 0000000..4fb92bc --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/home_viral_hits_cell_image.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "bg border@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "bg border@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/home_viral_hits_cell_image.imageset/bg border@2x.png b/SynthReel/Source/Assets.xcassets/Image/home_viral_hits_cell_image.imageset/bg border@2x.png new file mode 100644 index 0000000..aeb9d30 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/home_viral_hits_cell_image.imageset/bg border@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/home_viral_hits_cell_image.imageset/bg border@3x.png b/SynthReel/Source/Assets.xcassets/Image/home_viral_hits_cell_image.imageset/bg border@3x.png new file mode 100644 index 0000000..0ebebfe Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/home_viral_hits_cell_image.imageset/bg border@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/hot_icon_01.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/hot_icon_01.imageset/Contents.json new file mode 100644 index 0000000..5c4d3b1 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/hot_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/hot_icon_01.imageset/Frame@2x.png b/SynthReel/Source/Assets.xcassets/Image/hot_icon_01.imageset/Frame@2x.png new file mode 100644 index 0000000..9b95067 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/hot_icon_01.imageset/Frame@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/hot_icon_01.imageset/Frame@3x.png b/SynthReel/Source/Assets.xcassets/Image/hot_icon_01.imageset/Frame@3x.png new file mode 100644 index 0000000..73ecab6 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/hot_icon_01.imageset/Frame@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/my_short_bg_image.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/my_short_bg_image.imageset/Contents.json new file mode 100644 index 0000000..7b5bfb6 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/my_short_bg_image.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "科技背景 2@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "科技背景 2@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/my_short_bg_image.imageset/科技背景 2@2x.png b/SynthReel/Source/Assets.xcassets/Image/my_short_bg_image.imageset/科技背景 2@2x.png new file mode 100644 index 0000000..fc5b3ac Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/my_short_bg_image.imageset/科技背景 2@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/my_short_bg_image.imageset/科技背景 2@3x.png b/SynthReel/Source/Assets.xcassets/Image/my_short_bg_image.imageset/科技背景 2@3x.png new file mode 100644 index 0000000..cd825af Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/my_short_bg_image.imageset/科技背景 2@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/pan_bg_image_01.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/pan_bg_image_01.imageset/Contents.json new file mode 100644 index 0000000..8ca8b13 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/pan_bg_image_01.imageset/Contents.json @@ -0,0 +1,50 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 70@2x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "bottom" : 65, + "left" : 48, + "right" : 60, + "top" : 63 + }, + "center" : { + "height" : 1, + "mode" : "tile", + "width" : 1 + }, + "mode" : "9-part" + }, + "scale" : "2x" + }, + { + "filename" : "Rectangle 70@3x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "bottom" : 120, + "left" : 76, + "right" : 77, + "top" : 125 + }, + "center" : { + "height" : 1, + "mode" : "tile", + "width" : 1 + }, + "mode" : "9-part" + }, + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/pan_bg_image_01.imageset/Rectangle 70@2x.png b/SynthReel/Source/Assets.xcassets/Image/pan_bg_image_01.imageset/Rectangle 70@2x.png new file mode 100644 index 0000000..7f7fd53 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/pan_bg_image_01.imageset/Rectangle 70@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/pan_bg_image_01.imageset/Rectangle 70@3x.png b/SynthReel/Source/Assets.xcassets/Image/pan_bg_image_01.imageset/Rectangle 70@3x.png new file mode 100644 index 0000000..b640731 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/pan_bg_image_01.imageset/Rectangle 70@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/play_icon_01.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/play_icon_01.imageset/Contents.json new file mode 100644 index 0000000..c9f3912 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/play_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "play@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "play@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/play_icon_01.imageset/play@2x.png b/SynthReel/Source/Assets.xcassets/Image/play_icon_01.imageset/play@2x.png new file mode 100644 index 0000000..1e6205a Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/play_icon_01.imageset/play@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/play_icon_01.imageset/play@3x.png b/SynthReel/Source/Assets.xcassets/Image/play_icon_01.imageset/play@3x.png new file mode 100644 index 0000000..45f20e8 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/play_icon_01.imageset/play@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/play_icon_02.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/play_icon_02.imageset/Contents.json new file mode 100644 index 0000000..c9f3912 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/play_icon_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "play@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "play@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/play_icon_02.imageset/play@2x.png b/SynthReel/Source/Assets.xcassets/Image/play_icon_02.imageset/play@2x.png new file mode 100644 index 0000000..d15dcbe Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/play_icon_02.imageset/play@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/play_icon_02.imageset/play@3x.png b/SynthReel/Source/Assets.xcassets/Image/play_icon_02.imageset/play@3x.png new file mode 100644 index 0000000..6199087 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/play_icon_02.imageset/play@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/popular_bg_image.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/popular_bg_image.imageset/Contents.json new file mode 100644 index 0000000..05ebf46 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/popular_bg_image.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 4@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Rectangle 4@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/popular_bg_image.imageset/Rectangle 4@2x.png b/SynthReel/Source/Assets.xcassets/Image/popular_bg_image.imageset/Rectangle 4@2x.png new file mode 100644 index 0000000..795c369 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/popular_bg_image.imageset/Rectangle 4@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/popular_bg_image.imageset/Rectangle 4@3x.png b/SynthReel/Source/Assets.xcassets/Image/popular_bg_image.imageset/Rectangle 4@3x.png new file mode 100644 index 0000000..688a498 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/popular_bg_image.imageset/Rectangle 4@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_01.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_01.imageset/Contents.json new file mode 100644 index 0000000..4fb92bc --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "bg border@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "bg border@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_01.imageset/bg border@2x.png b/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_01.imageset/bg border@2x.png new file mode 100644 index 0000000..09e61ca Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_01.imageset/bg border@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_01.imageset/bg border@3x.png b/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_01.imageset/bg border@3x.png new file mode 100644 index 0000000..aa4729e Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_01.imageset/bg border@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_02.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_02.imageset/Contents.json new file mode 100644 index 0000000..467c9c7 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "bg border-center@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "bg border-center@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_02.imageset/bg border-center@2x.png b/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_02.imageset/bg border-center@2x.png new file mode 100644 index 0000000..c12174d Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_02.imageset/bg border-center@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_02.imageset/bg border-center@3x.png b/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_02.imageset/bg border-center@3x.png new file mode 100644 index 0000000..16c7fe6 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/premiere_now_bg_image_02.imageset/bg border-center@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/premiere_now_title_bg.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/premiere_now_title_bg.imageset/Contents.json new file mode 100644 index 0000000..9d8569a --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/premiere_now_title_bg.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Vector 7@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Vector 7@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/premiere_now_title_bg.imageset/Vector 7@2x.png b/SynthReel/Source/Assets.xcassets/Image/premiere_now_title_bg.imageset/Vector 7@2x.png new file mode 100644 index 0000000..d32bc89 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/premiere_now_title_bg.imageset/Vector 7@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/premiere_now_title_bg.imageset/Vector 7@3x.png b/SynthReel/Source/Assets.xcassets/Image/premiere_now_title_bg.imageset/Vector 7@3x.png new file mode 100644 index 0000000..15a6f98 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/premiere_now_title_bg.imageset/Vector 7@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/search_hot_bg_image.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/search_hot_bg_image.imageset/Contents.json new file mode 100644 index 0000000..05ebf46 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/search_hot_bg_image.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 4@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Rectangle 4@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/search_hot_bg_image.imageset/Rectangle 4@2x.png b/SynthReel/Source/Assets.xcassets/Image/search_hot_bg_image.imageset/Rectangle 4@2x.png new file mode 100644 index 0000000..0e58f62 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/search_hot_bg_image.imageset/Rectangle 4@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/search_hot_bg_image.imageset/Rectangle 4@3x.png b/SynthReel/Source/Assets.xcassets/Image/search_hot_bg_image.imageset/Rectangle 4@3x.png new file mode 100644 index 0000000..4e8b388 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/search_hot_bg_image.imageset/Rectangle 4@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/search_icon_01.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/search_icon_01.imageset/Contents.json new file mode 100644 index 0000000..15e4379 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/search_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "搜索@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "搜索@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/search_icon_01.imageset/搜索@2x.png b/SynthReel/Source/Assets.xcassets/Image/search_icon_01.imageset/搜索@2x.png new file mode 100644 index 0000000..85298c8 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/search_icon_01.imageset/搜索@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/search_icon_01.imageset/搜索@3x.png b/SynthReel/Source/Assets.xcassets/Image/search_icon_01.imageset/搜索@3x.png new file mode 100644 index 0000000..1bf0581 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/search_icon_01.imageset/搜索@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/search_result_cell_bg_image.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/search_result_cell_bg_image.imageset/Contents.json new file mode 100644 index 0000000..bdf7337 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/search_result_cell_bg_image.imageset/Contents.json @@ -0,0 +1,44 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "bg@2x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 159, + "right" : 185 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "2x" + }, + { + "filename" : "bg@3x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 189, + "right" : 279 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/search_result_cell_bg_image.imageset/bg@2x.png b/SynthReel/Source/Assets.xcassets/Image/search_result_cell_bg_image.imageset/bg@2x.png new file mode 100644 index 0000000..791ce94 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/search_result_cell_bg_image.imageset/bg@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/search_result_cell_bg_image.imageset/bg@3x.png b/SynthReel/Source/Assets.xcassets/Image/search_result_cell_bg_image.imageset/bg@3x.png new file mode 100644 index 0000000..a29536e Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/search_result_cell_bg_image.imageset/bg@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/search_text_bg_image.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/search_text_bg_image.imageset/Contents.json new file mode 100644 index 0000000..6442394 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/search_text_bg_image.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 48@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Rectangle 48@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/search_text_bg_image.imageset/Rectangle 48@2x.png b/SynthReel/Source/Assets.xcassets/Image/search_text_bg_image.imageset/Rectangle 48@2x.png new file mode 100644 index 0000000..a0df6fa Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/search_text_bg_image.imageset/Rectangle 48@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/search_text_bg_image.imageset/Rectangle 48@3x.png b/SynthReel/Source/Assets.xcassets/Image/search_text_bg_image.imageset/Rectangle 48@3x.png new file mode 100644 index 0000000..98436d0 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/search_text_bg_image.imageset/Rectangle 48@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/search_text_button.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/search_text_button.imageset/Contents.json new file mode 100644 index 0000000..c0a5a83 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/search_text_button.imageset/Contents.json @@ -0,0 +1,44 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Subtract@2x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 41, + "right" : 27 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "2x" + }, + { + "filename" : "Subtract@3x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 62, + "right" : 39 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/search_text_button.imageset/Subtract@2x.png b/SynthReel/Source/Assets.xcassets/Image/search_text_button.imageset/Subtract@2x.png new file mode 100644 index 0000000..8bbd504 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/search_text_button.imageset/Subtract@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/search_text_button.imageset/Subtract@3x.png b/SynthReel/Source/Assets.xcassets/Image/search_text_button.imageset/Subtract@3x.png new file mode 100644 index 0000000..341f112 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/search_text_button.imageset/Subtract@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/short_progress_bg_image.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/short_progress_bg_image.imageset/Contents.json new file mode 100644 index 0000000..e98bc84 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/short_progress_bg_image.imageset/Contents.json @@ -0,0 +1,50 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "bg border@2x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "bottom" : 76, + "left" : 472, + "right" : 49, + "top" : 74 + }, + "center" : { + "height" : 1, + "mode" : "tile", + "width" : 1 + }, + "mode" : "9-part" + }, + "scale" : "2x" + }, + { + "filename" : "bg border@3x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "bottom" : 133, + "left" : 700, + "right" : 99, + "top" : 113 + }, + "center" : { + "height" : 1, + "mode" : "tile", + "width" : 1 + }, + "mode" : "9-part" + }, + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/short_progress_bg_image.imageset/bg border@2x.png b/SynthReel/Source/Assets.xcassets/Image/short_progress_bg_image.imageset/bg border@2x.png new file mode 100644 index 0000000..fda64d1 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/short_progress_bg_image.imageset/bg border@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/short_progress_bg_image.imageset/bg border@3x.png b/SynthReel/Source/Assets.xcassets/Image/short_progress_bg_image.imageset/bg border@3x.png new file mode 100644 index 0000000..5d39dae Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/short_progress_bg_image.imageset/bg border@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/trophy_icon_01.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/trophy_icon_01.imageset/Contents.json new file mode 100644 index 0000000..8bb64fe --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/trophy_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "奖杯 1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "奖杯 1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/trophy_icon_01.imageset/奖杯 1@2x.png b/SynthReel/Source/Assets.xcassets/Image/trophy_icon_01.imageset/奖杯 1@2x.png new file mode 100644 index 0000000..2d95458 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/trophy_icon_01.imageset/奖杯 1@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/trophy_icon_01.imageset/奖杯 1@3x.png b/SynthReel/Source/Assets.xcassets/Image/trophy_icon_01.imageset/奖杯 1@3x.png new file mode 100644 index 0000000..58711ac Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/trophy_icon_01.imageset/奖杯 1@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/updates_bg_image.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/Image/updates_bg_image.imageset/Contents.json new file mode 100644 index 0000000..05ebf46 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/Image/updates_bg_image.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 4@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Rectangle 4@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/Image/updates_bg_image.imageset/Rectangle 4@2x.png b/SynthReel/Source/Assets.xcassets/Image/updates_bg_image.imageset/Rectangle 4@2x.png new file mode 100644 index 0000000..b6d9589 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/updates_bg_image.imageset/Rectangle 4@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/Image/updates_bg_image.imageset/Rectangle 4@3x.png b/SynthReel/Source/Assets.xcassets/Image/updates_bg_image.imageset/Rectangle 4@3x.png new file mode 100644 index 0000000..6f3995a Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/Image/updates_bg_image.imageset/Rectangle 4@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/Contents.json b/SynthReel/Source/Assets.xcassets/TabBar/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/TabBar/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01.imageset/Contents.json new file mode 100644 index 0000000..92bde5c --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "home-Not selected@3x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "home-Not selected@2x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "original" + } +} diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01.imageset/home-Not selected@2x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01.imageset/home-Not selected@2x.png new file mode 100644 index 0000000..e29acd7 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01.imageset/home-Not selected@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01.imageset/home-Not selected@3x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01.imageset/home-Not selected@3x.png new file mode 100644 index 0000000..0bbbc63 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01.imageset/home-Not selected@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01_selected.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01_selected.imageset/Contents.json new file mode 100644 index 0000000..b60f645 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01_selected.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "home@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "home@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "original" + } +} diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01_selected.imageset/home@2x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01_selected.imageset/home@2x.png new file mode 100644 index 0000000..e88613b Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01_selected.imageset/home@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01_selected.imageset/home@3x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01_selected.imageset/home@3x.png new file mode 100644 index 0000000..d1eda75 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_01_selected.imageset/home@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02.imageset/Contents.json new file mode 100644 index 0000000..7c99708 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Discover@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Discover@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "original" + } +} diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02.imageset/Discover@2x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02.imageset/Discover@2x.png new file mode 100644 index 0000000..016a9e1 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02.imageset/Discover@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02.imageset/Discover@3x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02.imageset/Discover@3x.png new file mode 100644 index 0000000..f6770a0 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02.imageset/Discover@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02_selected.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02_selected.imageset/Contents.json new file mode 100644 index 0000000..7c99708 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02_selected.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Discover@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Discover@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "original" + } +} diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02_selected.imageset/Discover@2x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02_selected.imageset/Discover@2x.png new file mode 100644 index 0000000..1b0fbaf Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02_selected.imageset/Discover@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02_selected.imageset/Discover@3x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02_selected.imageset/Discover@3x.png new file mode 100644 index 0000000..bf7dff6 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_02_selected.imageset/Discover@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03.imageset/Contents.json new file mode 100644 index 0000000..f1b73a6 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Favorite@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Favorite@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "original" + } +} diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03.imageset/Favorite@2x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03.imageset/Favorite@2x.png new file mode 100644 index 0000000..af9971f Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03.imageset/Favorite@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03.imageset/Favorite@3x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03.imageset/Favorite@3x.png new file mode 100644 index 0000000..26d9f1b Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03.imageset/Favorite@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03_selected.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03_selected.imageset/Contents.json new file mode 100644 index 0000000..f1b73a6 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03_selected.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Favorite@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Favorite@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "original" + } +} diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03_selected.imageset/Favorite@2x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03_selected.imageset/Favorite@2x.png new file mode 100644 index 0000000..77b4c69 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03_selected.imageset/Favorite@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03_selected.imageset/Favorite@3x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03_selected.imageset/Favorite@3x.png new file mode 100644 index 0000000..a5ffa26 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_03_selected.imageset/Favorite@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Contents.json new file mode 100644 index 0000000..c2dfb99 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Profile@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Profile@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "original" + } +} diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Profile@2x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Profile@2x.png new file mode 100644 index 0000000..a7b4fe6 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Profile@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Profile@3x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Profile@3x.png new file mode 100644 index 0000000..6b8a6cb Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Profile@3x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/Contents.json b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/Contents.json new file mode 100644 index 0000000..c2dfb99 --- /dev/null +++ b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Profile@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Profile@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "original" + } +} diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/Profile@2x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/Profile@2x.png new file mode 100644 index 0000000..5fc7741 Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/Profile@2x.png differ diff --git a/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/Profile@3x.png b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/Profile@3x.png new file mode 100644 index 0000000..c52dfdd Binary files /dev/null and b/SynthReel/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/Profile@3x.png differ diff --git a/SynthReel/Source/Base.lproj/LaunchScreen.storyboard b/SynthReel/Source/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/SynthReel/Source/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SynthReel/Source/Bridging-Header.h b/SynthReel/Source/Bridging-Header.h new file mode 100644 index 0000000..817be0f --- /dev/null +++ b/SynthReel/Source/Bridging-Header.h @@ -0,0 +1,8 @@ +// +// Bridging-Header.h +// SynthReel +// +// Created by 湖北秦九 on 2025/11/14. +// Copyright © 2025 SR. All rights reserved. +// + diff --git a/SynthReel/Source/Info.plist b/SynthReel/Source/Info.plist new file mode 100644 index 0000000..e666e0f --- /dev/null +++ b/SynthReel/Source/Info.plist @@ -0,0 +1,25 @@ + + + + + UIDesignRequiresCompatibility + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + + diff --git a/SynthReel/Source/en.lproj/Localizable.strings b/SynthReel/Source/en.lproj/Localizable.strings new file mode 100644 index 0000000..d8cfd06 --- /dev/null +++ b/SynthReel/Source/en.lproj/Localizable.strings @@ -0,0 +1,25 @@ +/* + Localizable.strings + SynthReel + + Created by 湖北秦九 on 2025/11/13. + Copyright © 2025 SR. All rights reserved. +*/ + + +"Drama Center" = "Drama Center"; +"Top Charts" = "Top Charts"; +"Popular" = "Popular"; +"Updates" = "Updates"; +"Binge-Worthy" = "Binge-Worthy"; +"Viral Hits" = "Viral Hits"; +"Premiere Now" = "Premiere Now"; +"Categories" = "Categories"; +"Search" = "Search"; +"search_placeholder_text" = "What to watch next?"; +"Search History" = "Search History"; +"Premium Picks" = "Premium Picks"; +"Search Results" = "Search Results"; +"Select Episode" = "Select Episode"; +"all_episodes_text" = "(All ## episodes)"; +"recommend_ep_text" = "Watch the complete series";