登录页面开发
@ -162,8 +162,23 @@
|
||||
F3F389062F6939EE001B0E15 /* XSRewardCoinsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389052F6939EE001B0E15 /* XSRewardCoinsViewController.swift */; };
|
||||
F3F389082F6940BB001B0E15 /* XSRewardCoinsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389072F6940BB001B0E15 /* XSRewardCoinsCell.swift */; };
|
||||
F3F3890A2F694109001B0E15 /* XSSendCoinRecordModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389092F694109001B0E15 /* XSSendCoinRecordModel.swift */; };
|
||||
F3F3890C2F695055001B0E15 /* FACoinsRecordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3890B2F695055001B0E15 /* FACoinsRecordViewController.swift */; };
|
||||
F3F3890E2F695068001B0E15 /* FAVipRecordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3890D2F695068001B0E15 /* FAVipRecordViewController.swift */; };
|
||||
F3F3890C2F695055001B0E15 /* XSCoinsOrderRecordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3890B2F695055001B0E15 /* XSCoinsOrderRecordViewController.swift */; };
|
||||
F3F3890E2F695068001B0E15 /* XSVipOrderRecordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3890D2F695068001B0E15 /* XSVipOrderRecordViewController.swift */; };
|
||||
F3F389102F695C5A001B0E15 /* XSSegmentedGradientIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3890F2F695C5A001B0E15 /* XSSegmentedGradientIndicatorView.swift */; };
|
||||
F3F389122F6A37E3001B0E15 /* XSCoinsOrderRecordCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389112F6A37E3001B0E15 /* XSCoinsOrderRecordCell.swift */; };
|
||||
F3F389142F6A3812001B0E15 /* XSOrderRecordModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389132F6A3812001B0E15 /* XSOrderRecordModel.swift */; };
|
||||
F3F389162F6A3AD3001B0E15 /* XSVipOrderRecordCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389152F6A3AD3001B0E15 /* XSVipOrderRecordCell.swift */; };
|
||||
F3F389182F6A4407001B0E15 /* XSStoreCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389172F6A4407001B0E15 /* XSStoreCell.swift */; };
|
||||
F3F3891A2F6A444D001B0E15 /* XSStoreCoinsBigCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389192F6A444D001B0E15 /* XSStoreCoinsBigCell.swift */; };
|
||||
F3F3891C2F6A4465001B0E15 /* XSStoreCoinsSmallCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3891B2F6A4465001B0E15 /* XSStoreCoinsSmallCell.swift */; };
|
||||
F3F3891E2F6A4484001B0E15 /* XSStoreCoinsSpreadCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3891D2F6A4484001B0E15 /* XSStoreCoinsSpreadCell.swift */; };
|
||||
F3F389202F6A487B001B0E15 /* XSStoreVipCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3891F2F6A487B001B0E15 /* XSStoreVipCell.swift */; };
|
||||
F3F389222F6A4B8F001B0E15 /* XSCoinsPackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389212F6A4B8F001B0E15 /* XSCoinsPackViewController.swift */; };
|
||||
F3F389242F6A8B89001B0E15 /* XSHomeCoinsPackButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389232F6A8B89001B0E15 /* XSHomeCoinsPackButton.swift */; };
|
||||
F3F389272F6A9597001B0E15 /* XSBaseAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389262F6A9597001B0E15 /* XSBaseAlert.swift */; };
|
||||
F3F389292F6A98C9001B0E15 /* XSAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389282F6A98C9001B0E15 /* XSAlert.swift */; };
|
||||
F3F3892B2F6AAC25001B0E15 /* XSVersionUpdateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3892A2F6AAC25001B0E15 /* XSVersionUpdateModel.swift */; };
|
||||
F3F3892D2F6B86F7001B0E15 /* XSLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3892C2F6B86F7001B0E15 /* XSLoginView.swift */; };
|
||||
F3F683ED2F56C380008AF250 /* XSHomeHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F683EC2F56C380008AF250 /* XSHomeHistoryView.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@ -327,8 +342,23 @@
|
||||
F3F389052F6939EE001B0E15 /* XSRewardCoinsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSRewardCoinsViewController.swift; sourceTree = "<group>"; };
|
||||
F3F389072F6940BB001B0E15 /* XSRewardCoinsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSRewardCoinsCell.swift; sourceTree = "<group>"; };
|
||||
F3F389092F694109001B0E15 /* XSSendCoinRecordModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSSendCoinRecordModel.swift; sourceTree = "<group>"; };
|
||||
F3F3890B2F695055001B0E15 /* FACoinsRecordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinsRecordViewController.swift; sourceTree = "<group>"; };
|
||||
F3F3890D2F695068001B0E15 /* FAVipRecordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAVipRecordViewController.swift; sourceTree = "<group>"; };
|
||||
F3F3890B2F695055001B0E15 /* XSCoinsOrderRecordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSCoinsOrderRecordViewController.swift; sourceTree = "<group>"; };
|
||||
F3F3890D2F695068001B0E15 /* XSVipOrderRecordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSVipOrderRecordViewController.swift; sourceTree = "<group>"; };
|
||||
F3F3890F2F695C5A001B0E15 /* XSSegmentedGradientIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSSegmentedGradientIndicatorView.swift; sourceTree = "<group>"; };
|
||||
F3F389112F6A37E3001B0E15 /* XSCoinsOrderRecordCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSCoinsOrderRecordCell.swift; sourceTree = "<group>"; };
|
||||
F3F389132F6A3812001B0E15 /* XSOrderRecordModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSOrderRecordModel.swift; sourceTree = "<group>"; };
|
||||
F3F389152F6A3AD3001B0E15 /* XSVipOrderRecordCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSVipOrderRecordCell.swift; sourceTree = "<group>"; };
|
||||
F3F389172F6A4407001B0E15 /* XSStoreCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSStoreCell.swift; sourceTree = "<group>"; };
|
||||
F3F389192F6A444D001B0E15 /* XSStoreCoinsBigCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSStoreCoinsBigCell.swift; sourceTree = "<group>"; };
|
||||
F3F3891B2F6A4465001B0E15 /* XSStoreCoinsSmallCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSStoreCoinsSmallCell.swift; sourceTree = "<group>"; };
|
||||
F3F3891D2F6A4484001B0E15 /* XSStoreCoinsSpreadCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSStoreCoinsSpreadCell.swift; sourceTree = "<group>"; };
|
||||
F3F3891F2F6A487B001B0E15 /* XSStoreVipCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSStoreVipCell.swift; sourceTree = "<group>"; };
|
||||
F3F389212F6A4B8F001B0E15 /* XSCoinsPackViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSCoinsPackViewController.swift; sourceTree = "<group>"; };
|
||||
F3F389232F6A8B89001B0E15 /* XSHomeCoinsPackButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSHomeCoinsPackButton.swift; sourceTree = "<group>"; };
|
||||
F3F389262F6A9597001B0E15 /* XSBaseAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSBaseAlert.swift; sourceTree = "<group>"; };
|
||||
F3F389282F6A98C9001B0E15 /* XSAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSAlert.swift; sourceTree = "<group>"; };
|
||||
F3F3892A2F6AAC25001B0E15 /* XSVersionUpdateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSVersionUpdateModel.swift; sourceTree = "<group>"; };
|
||||
F3F3892C2F6B86F7001B0E15 /* XSLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSLoginView.swift; sourceTree = "<group>"; };
|
||||
F3F683EC2F56C380008AF250 /* XSHomeHistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSHomeHistoryView.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -482,6 +512,7 @@
|
||||
F347D29C2F03A5A100786648 /* Libs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F3F389252F6A955D001B0E15 /* Alert */,
|
||||
F3F388EA2F680CC7001B0E15 /* IapManager */,
|
||||
F3B312B52F319CA50093B180 /* Empty */,
|
||||
F347D3042F0A12DB00786648 /* HUD */,
|
||||
@ -560,6 +591,7 @@
|
||||
F3B312B82F30B0A10093B180 /* XSSearchSuggestionCell.swift */,
|
||||
F3B312B92F30B0A10093B180 /* XSSearchResultCell.swift */,
|
||||
F3F683EC2F56C380008AF250 /* XSHomeHistoryView.swift */,
|
||||
F3F389232F6A8B89001B0E15 /* XSHomeCoinsPackButton.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
@ -718,6 +750,7 @@
|
||||
F3F388D42F67DDBC001B0E15 /* XSMineWalletView.swift */,
|
||||
F3F388D62F67EDEC001B0E15 /* XSMineCoinsPackView.swift */,
|
||||
F3F388D82F67F9E1001B0E15 /* XSMineVipView.swift */,
|
||||
F3F3892C2F6B86F7001B0E15 /* XSLoginView.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
@ -727,6 +760,7 @@
|
||||
children = (
|
||||
F35547D92F3DAACB006F28CD /* XSMineItem.swift */,
|
||||
F355483E2F52BD07006F28CD /* XSFeedbackCountModel.swift */,
|
||||
F3F3892A2F6AAC25001B0E15 /* XSVersionUpdateModel.swift */,
|
||||
);
|
||||
path = Model;
|
||||
sourceTree = "<group>";
|
||||
@ -846,8 +880,9 @@
|
||||
F3F388FD2F6931D1001B0E15 /* XSConsumptionRecordsViewController.swift */,
|
||||
F3F389052F6939EE001B0E15 /* XSRewardCoinsViewController.swift */,
|
||||
F3F389032F6939C7001B0E15 /* XSOrderRecordsViewController.swift */,
|
||||
F3F3890B2F695055001B0E15 /* FACoinsRecordViewController.swift */,
|
||||
F3F3890D2F695068001B0E15 /* FAVipRecordViewController.swift */,
|
||||
F3F3890B2F695055001B0E15 /* XSCoinsOrderRecordViewController.swift */,
|
||||
F3F3890D2F695068001B0E15 /* XSVipOrderRecordViewController.swift */,
|
||||
F3F389212F6A4B8F001B0E15 /* XSCoinsPackViewController.swift */,
|
||||
);
|
||||
path = Controller;
|
||||
sourceTree = "<group>";
|
||||
@ -857,10 +892,18 @@
|
||||
children = (
|
||||
F3F388E22F680316001B0E15 /* XSStoreCoinsView.swift */,
|
||||
F3F388E42F68032A001B0E15 /* XSStoreVipView.swift */,
|
||||
F3F389172F6A4407001B0E15 /* XSStoreCell.swift */,
|
||||
F3F389192F6A444D001B0E15 /* XSStoreCoinsBigCell.swift */,
|
||||
F3F3891B2F6A4465001B0E15 /* XSStoreCoinsSmallCell.swift */,
|
||||
F3F3891D2F6A4484001B0E15 /* XSStoreCoinsSpreadCell.swift */,
|
||||
F3F3891F2F6A487B001B0E15 /* XSStoreVipCell.swift */,
|
||||
F3F388F92F69240E001B0E15 /* XSWalletHeaderView.swift */,
|
||||
F3F388FB2F692CEF001B0E15 /* XSWalletCell.swift */,
|
||||
F3F388FF2F693293001B0E15 /* XSConsumptionRecordsCell.swift */,
|
||||
F3F389072F6940BB001B0E15 /* XSRewardCoinsCell.swift */,
|
||||
F3F3890F2F695C5A001B0E15 /* XSSegmentedGradientIndicatorView.swift */,
|
||||
F3F389112F6A37E3001B0E15 /* XSCoinsOrderRecordCell.swift */,
|
||||
F3F389152F6A3AD3001B0E15 /* XSVipOrderRecordCell.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
@ -872,6 +915,7 @@
|
||||
F3F388E62F6805DE001B0E15 /* XSPayItem.swift */,
|
||||
F3F389012F693785001B0E15 /* XSBuyRecordsModel.swift */,
|
||||
F3F389092F694109001B0E15 /* XSSendCoinRecordModel.swift */,
|
||||
F3F389132F6A3812001B0E15 /* XSOrderRecordModel.swift */,
|
||||
);
|
||||
path = Model;
|
||||
sourceTree = "<group>";
|
||||
@ -888,6 +932,15 @@
|
||||
path = IapManager;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F3F389252F6A955D001B0E15 /* Alert */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F3F389262F6A9597001B0E15 /* XSBaseAlert.swift */,
|
||||
F3F389282F6A98C9001B0E15 /* XSAlert.swift */,
|
||||
);
|
||||
path = Alert;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@ -1028,6 +1081,7 @@
|
||||
F3F388EC2F680CEE001B0E15 /* XSIapManager.swift in Sources */,
|
||||
F347D3082F0A134500786648 /* XSHud.swift in Sources */,
|
||||
F347D29E2F03750000786648 /* XSTabBarItemContentView.swift in Sources */,
|
||||
F3F3891E2F6A4484001B0E15 /* XSStoreCoinsSpreadCell.swift in Sources */,
|
||||
F347D2FE2F0A0D0700786648 /* XSUserDefaultsKey.swift in Sources */,
|
||||
F347D2EE2F0A06FB00786648 /* XSURLPath.swift in Sources */,
|
||||
F347D2DC2F04EE5F00786648 /* XSHomeRankingsCell.swift in Sources */,
|
||||
@ -1044,21 +1098,24 @@
|
||||
F347D2E82F0A03AB00786648 /* XSNetworkModel.swift in Sources */,
|
||||
F35547FE2F4EC450006F28CD /* XSMyCollectViewController.swift in Sources */,
|
||||
F347D29B2F03740000786648 /* XSConfig.swift in Sources */,
|
||||
F3F3892D2F6B86F7001B0E15 /* XSLoginView.swift in Sources */,
|
||||
F3F388DF2F67FE6D001B0E15 /* XSStoreViewController.swift in Sources */,
|
||||
F35547F42F3EFC37006F28CD /* XSMyListCollectsCell.swift in Sources */,
|
||||
F347D2A72F03AAD700786648 /* XSViewController.swift in Sources */,
|
||||
F3585C342F148F0800EEC469 /* XSHomeViewModel.swift in Sources */,
|
||||
F347D2B02F03AE6700786648 /* XSHomeViewController.swift in Sources */,
|
||||
F3F389242F6A8B89001B0E15 /* XSHomeCoinsPackButton.swift in Sources */,
|
||||
F304E6102F67A9B600E9B0A6 /* XSShortDetailLockView.swift in Sources */,
|
||||
F3585C3A2F14999700EEC469 /* XSProgressView.swift in Sources */,
|
||||
F3F389062F6939EE001B0E15 /* XSRewardCoinsViewController.swift in Sources */,
|
||||
F3585C382F1497AF00EEC469 /* XSDiscoverControlView.swift in Sources */,
|
||||
F3F388D52F67DDBC001B0E15 /* XSMineWalletView.swift in Sources */,
|
||||
F3F389272F6A9597001B0E15 /* XSBaseAlert.swift in Sources */,
|
||||
F3F388D92F67F9E1001B0E15 /* XSMineVipView.swift in Sources */,
|
||||
F347D2D42F04BF3D00786648 /* XSHomeNewTitleView.swift in Sources */,
|
||||
F304E6122F67D74A00E9B0A6 /* XSVideoUnlockResult.swift in Sources */,
|
||||
F3F388FE2F6931D1001B0E15 /* XSConsumptionRecordsViewController.swift in Sources */,
|
||||
F3F3890C2F695055001B0E15 /* FACoinsRecordViewController.swift in Sources */,
|
||||
F3F3890C2F695055001B0E15 /* XSCoinsOrderRecordViewController.swift in Sources */,
|
||||
F347D28D2F03709200786648 /* AppDelegate.swift in Sources */,
|
||||
F3585C492F14EE8D00EEC469 /* XSShortDetailPlayerCell.swift in Sources */,
|
||||
F35548412F52BE6D006F28CD /* XSBaseWebViewController+Script.swift in Sources */,
|
||||
@ -1095,19 +1152,23 @@
|
||||
F347D2E02F04F7ED00786648 /* XSHomeCategoriesCell.swift in Sources */,
|
||||
F3F3890A2F694109001B0E15 /* XSSendCoinRecordModel.swift in Sources */,
|
||||
F347D3032F0A10B600786648 /* XSCryptorService.swift in Sources */,
|
||||
F3F3891C2F6A4465001B0E15 /* XSStoreCoinsSmallCell.swift in Sources */,
|
||||
F35547D42F3DA7A8006F28CD /* XSTableViewCell.swift in Sources */,
|
||||
F3F388F62F68F746001B0E15 /* XSPayDataRequest.swift in Sources */,
|
||||
F347D30E2F0A39DE00786648 /* XSHomeAPI.swift in Sources */,
|
||||
F3F3891A2F6A444D001B0E15 /* XSStoreCoinsBigCell.swift in Sources */,
|
||||
F3B312B72F319CBE0093B180 /* XSEmpty.swift in Sources */,
|
||||
F347D2F62F0A0B0B00786648 /* XSLoginToken.swift in Sources */,
|
||||
F347D2CA2F03DC9200786648 /* CGMutablePath+XS.swift in Sources */,
|
||||
F347D2BE2F03C24B00786648 /* XSCollectionView.swift in Sources */,
|
||||
F35547DA2F3DAACB006F28CD /* XSMineItem.swift in Sources */,
|
||||
F3F389182F6A4407001B0E15 /* XSStoreCell.swift in Sources */,
|
||||
F347D2D62F04C7D500786648 /* XSHomeNewCell.swift in Sources */,
|
||||
F3F388E92F68062F001B0E15 /* XSPayDateModel.swift in Sources */,
|
||||
F347D2CC2F03E04400786648 /* AppDelegate+Config.swift in Sources */,
|
||||
F347D3202F0A57A300786648 /* XSDiscoverPlayerCell.swift in Sources */,
|
||||
F35548022F4ECD5C006F28CD /* UIScrollView+Refresh.swift in Sources */,
|
||||
F3F3892B2F6AAC25001B0E15 /* XSVersionUpdateModel.swift in Sources */,
|
||||
F347D2EC2F0A060E00786648 /* XSNetworkTarget.swift in Sources */,
|
||||
F3F388E12F680090001B0E15 /* XSScrollView.swift in Sources */,
|
||||
F35547E92F3DDBDD006F28CD /* XSWebView.swift in Sources */,
|
||||
@ -1121,24 +1182,31 @@
|
||||
F3585C4B2F14FD1000EEC469 /* XSShortDetailPlayerControlView.swift in Sources */,
|
||||
F3F683ED2F56C380008AF250 /* XSHomeHistoryView.swift in Sources */,
|
||||
F347D2A12F03A84300786648 /* XSScreen.swift in Sources */,
|
||||
F3F389202F6A487B001B0E15 /* XSStoreVipCell.swift in Sources */,
|
||||
F35548062F4FD6DA006F28CD /* XSMinePlayHistoryView.swift in Sources */,
|
||||
F3F3890E2F695068001B0E15 /* FAVipRecordViewController.swift in Sources */,
|
||||
F3F3890E2F695068001B0E15 /* XSVipOrderRecordViewController.swift in Sources */,
|
||||
F3F388E72F6805DE001B0E15 /* XSPayItem.swift in Sources */,
|
||||
F3F389292F6A98C9001B0E15 /* XSAlert.swift in Sources */,
|
||||
F347D2992F03730E00786648 /* XSTabBarController.swift in Sources */,
|
||||
F3F389122F6A37E3001B0E15 /* XSCoinsOrderRecordCell.swift in Sources */,
|
||||
F35547D22F3DA757006F28CD /* XSTableView.swift in Sources */,
|
||||
F355480A2F4FE99F006F28CD /* XSMinePlayHistoryCell.swift in Sources */,
|
||||
F3B312AD2F30ACF60093B180 /* XSSearchHotSectionView.swift in Sources */,
|
||||
F3B312AE2F30ACF60093B180 /* XSSearchTagsView.swift in Sources */,
|
||||
F3F389082F6940BB001B0E15 /* XSRewardCoinsCell.swift in Sources */,
|
||||
F3B312AF2F30ACF60093B180 /* XSSearchTagCell.swift in Sources */,
|
||||
F3F389142F6A3812001B0E15 /* XSOrderRecordModel.swift in Sources */,
|
||||
F3B312B02F30ACF60093B180 /* XSSearchRecentView.swift in Sources */,
|
||||
F35548042F4EDF27006F28CD /* UIStackView+XS.swift in Sources */,
|
||||
F3B312B12F30ACF60093B180 /* XSSearchHeaderView.swift in Sources */,
|
||||
F3F389162F6A3AD3001B0E15 /* XSVipOrderRecordCell.swift in Sources */,
|
||||
F3B312B22F30ACF60093B180 /* XSSearchHotListItemView.swift in Sources */,
|
||||
F3B312B32F30ACF60093B180 /* XSSearchGradientButton.swift in Sources */,
|
||||
F3B312B42F30ACF60093B180 /* XSSearchHotListCardView.swift in Sources */,
|
||||
F35547F62F3F0407006F28CD /* XSMyListHistoryView.swift in Sources */,
|
||||
F3B312BF2F30B2000093B180 /* XSSearchHistoryHotView.swift in Sources */,
|
||||
F3F389222F6A4B8F001B0E15 /* XSCoinsPackViewController.swift in Sources */,
|
||||
F3F389102F695C5A001B0E15 /* XSSegmentedGradientIndicatorView.swift in Sources */,
|
||||
F35548432F52BFB8006F28CD /* XSWebMessageModel.swift in Sources */,
|
||||
F3B312BD2F30B0A10093B180 /* XSSearchSuggestionCell.swift in Sources */,
|
||||
F3B312BE2F30B0A10093B180 /* XSSearchResultCell.swift in Sources */,
|
||||
|
||||
@ -102,3 +102,22 @@ class XSTabBarController: ESTabBarController {
|
||||
return nav
|
||||
}
|
||||
}
|
||||
|
||||
extension XSTabBarController {
|
||||
|
||||
func checkUpdates() {
|
||||
|
||||
Task {
|
||||
guard let model = await XSSettingAPI.requestVersionUpdateData() else { return }
|
||||
|
||||
self.showUpdatesAlert(model)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func showUpdatesAlert(_ model: XSVersionUpdateModel) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,15 @@ extension String: SmartCodable {
|
||||
func localizedReplace(text: String?) -> String {
|
||||
return self.localized.replacingOccurrences(of: "##", with: text ?? "")
|
||||
}
|
||||
|
||||
func localizedReplace(text1: String, text2: String, text3: String? = nil) -> String {
|
||||
var string = self.localized.replacingOccurrences(of: "#1#", with: text1)
|
||||
string = string.replacingOccurrences(of: "#2#", with: text2)
|
||||
if let text = text3 {
|
||||
string = string.replacingOccurrences(of: "#3#", with: text)
|
||||
}
|
||||
return string
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
|
||||
@ -17,4 +17,14 @@ struct XSSettingAPI {
|
||||
let response: XSNetwork.Response<XSFeedbackCountModel> = await XSNetwork.request(parameters: param)
|
||||
return response.data
|
||||
}
|
||||
|
||||
static func requestVersionUpdateData() async -> XSVersionUpdateModel? {
|
||||
|
||||
var param = XSNetwork.Parameters(path: "/customer/versionControl")
|
||||
param.method = .get
|
||||
param.isLoding = false
|
||||
param.isToast = false
|
||||
let response: XSNetwork.Response<XSVersionUpdateModel> = await XSNetwork.request(parameters: param)
|
||||
return response.data
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,4 +128,19 @@ struct XSStoreAPI {
|
||||
let response: XSNetwork.Response<XSNetwork.List<XSSendCoinRecordModel>> = await XSNetwork.request(parameters: param)
|
||||
return response.data
|
||||
}
|
||||
|
||||
static func requestRechargeRecord(page: Int, buyType: BuyType) async -> XSNetwork.List<XSOrderRecordModel>? {
|
||||
let parameters: [String : Any] = [
|
||||
"page_size" : 20,
|
||||
"current_page" : page,
|
||||
"buy_type" : buyType.rawValue
|
||||
]
|
||||
|
||||
var param = XSNetwork.Parameters(path: "/getCustomerOrder")
|
||||
param.method = .get
|
||||
param.parameters = parameters
|
||||
|
||||
let response: XSNetwork.Response<XSNetwork.List<XSOrderRecordModel>> = await XSNetwork.request(parameters: param)
|
||||
return response.data
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,3 +19,16 @@ class XSScrollView: UIScrollView {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class XSPagerScrollView: XSScrollView {
|
||||
|
||||
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
let result = super.gestureRecognizerShouldBegin(gestureRecognizer)
|
||||
guard let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer else { return result }
|
||||
let translation = panGestureRecognizer.translation(in: self)
|
||||
if translation.x > 0, self.contentOffset.x == 0 {
|
||||
return false
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,6 +73,16 @@ class XSHomeViewController: XSViewController {
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var coinsPackButton: XSHomeCoinsPackButton = {
|
||||
let view = XSHomeCoinsPackButton()
|
||||
view.addAction(UIAction(handler: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
let vc = XSCoinsPackViewController()
|
||||
self.navigationController?.pushViewController(vc, animated: true)
|
||||
}), for: .touchUpInside)
|
||||
return view
|
||||
}()
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
@ -121,6 +131,7 @@ extension XSHomeViewController {
|
||||
view.addSubview(searchButton)
|
||||
view.addSubview(segmentedView)
|
||||
view.addSubview(historyView)
|
||||
view.addSubview(coinsPackButton)
|
||||
|
||||
searchButton.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(16)
|
||||
@ -150,6 +161,11 @@ extension XSHomeViewController {
|
||||
make.bottom.equalToSuperview().offset(-(XSScreen.customTabBarHeight + 5))
|
||||
}
|
||||
|
||||
coinsPackButton.snp.makeConstraints { make in
|
||||
make.right.equalToSuperview().inset(10)
|
||||
make.bottom.equalTo(historyView.snp.top).offset(-20)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
51
XSeri/Class/Home/View/XSHomeCoinsPackButton.swift
Normal file
@ -0,0 +1,51 @@
|
||||
//
|
||||
// XSHomeCoinsPackButton.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
|
||||
class XSHomeCoinsPackButton: UIControl {
|
||||
|
||||
private lazy var bgImageView = UIImageView(image: UIImage(named: "calendar_icon_01"))
|
||||
private lazy var textBgImageView = UIImageView(image: UIImage(named: "button_bg_image_02"))
|
||||
|
||||
private lazy var textLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 10, weight: .black).withBoldItalic()
|
||||
label.textColor = ._783902
|
||||
label.text = "Daily Coins".localized
|
||||
return label
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
addSubview(bgImageView)
|
||||
addSubview(textBgImageView)
|
||||
textBgImageView.addSubview(textLabel)
|
||||
|
||||
bgImageView.snp.makeConstraints { make in
|
||||
make.left.right.top.equalToSuperview()
|
||||
}
|
||||
|
||||
textBgImageView.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.bottom.equalToSuperview()
|
||||
make.top.equalToSuperview().offset(50)
|
||||
}
|
||||
|
||||
textLabel.snp.makeConstraints { make in
|
||||
make.centerY.equalToSuperview()
|
||||
make.centerX.equalToSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
@ -135,6 +135,10 @@ extension XSMineViewController: UITableViewDelegate, UITableViewDataSource {
|
||||
let vc = XSFeedbackViewController()
|
||||
self.navigationController?.pushViewController(vc, animated: true)
|
||||
|
||||
case .wallet:
|
||||
let vc = XSWalletViewController()
|
||||
self.navigationController?.pushViewController(vc, animated: true)
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -145,6 +149,7 @@ extension XSMineViewController {
|
||||
|
||||
private func createDataArr() {
|
||||
let arr = [
|
||||
XSMineItem(type: .wallet, iconImage: UIImage(named: "wallet_icon_01"), title: "My Wallet".localized),
|
||||
// XSMineItem(type: .setting, iconImage: UIImage(named: "setting_icon_01"), title: "Setting".localized),
|
||||
XSMineItem(type: .about, iconImage: UIImage(named: "about_icon_01"), title: "About".localized),
|
||||
XSMineItem(type: .feedback, iconImage: UIImage(named: "feedback_icon_01"), title: "Feedback".localized)
|
||||
|
||||
@ -15,6 +15,7 @@ struct XSMineItem {
|
||||
case feedback
|
||||
case web
|
||||
case safari
|
||||
case wallet
|
||||
///消费记录
|
||||
case consumptionRecords
|
||||
///购买记录
|
||||
|
||||
36
XSeri/Class/Mine/Model/XSVersionUpdateModel.swift
Normal file
@ -0,0 +1,36 @@
|
||||
//
|
||||
// XSVersionUpdateModel.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SmartCodable
|
||||
|
||||
struct XSVersionUpdateModel: SmartCodable {
|
||||
|
||||
var version_code: String?
|
||||
var des: String?
|
||||
var version_name: String?
|
||||
var force: Bool?
|
||||
|
||||
|
||||
func canUpdate() -> Bool {
|
||||
guard let version = version_code else { return false }
|
||||
|
||||
let result = kXSVersion.compare(version, options: .numeric)
|
||||
if result == .orderedAscending {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
static func mappingForKey() -> [SmartKeyTransformer]? {
|
||||
return [
|
||||
CodingKeys.des <--- ["description"]
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
175
XSeri/Class/Mine/View/XSLoginView.swift
Normal file
@ -0,0 +1,175 @@
|
||||
//
|
||||
// XSLoginView.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/19.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
import YYText
|
||||
|
||||
class XSLoginView: XSPanModalContentView {
|
||||
|
||||
|
||||
private lazy var bgView: UIImageView = {
|
||||
let view = UIImageView(image: UIImage(named: "bg_image_01"))
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var closeButton: UIButton = {
|
||||
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.dismiss(animated: true) { }
|
||||
}))
|
||||
button.setImage(UIImage(named: "close_icon_02"), for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 18, weight: .semibold)
|
||||
label.textColor = .white
|
||||
label.text = "Welcome to XSeri"
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var stackView: UIStackView = {
|
||||
let view = UIStackView(arrangedSubviews: [facebookButton, appleButton])
|
||||
view.axis = .vertical
|
||||
view.spacing = 18
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var facebookButton: UIButton = {
|
||||
var configuration = UIButton.Configuration.plain()
|
||||
configuration.background.cornerRadius = 8
|
||||
configuration.background.backgroundColor = ._0866_FF
|
||||
configuration.imagePadding = 12
|
||||
configuration.image = UIImage(named: "facebook_icon_01")
|
||||
configuration.attributedTitle = AttributedString("Login with Facebook".localized, attributes: AttributeContainer([
|
||||
.font : UIFont.font(ofSize: 14, weight: .semibold),
|
||||
.foregroundColor : UIColor.white
|
||||
]))
|
||||
|
||||
let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
|
||||
}))
|
||||
button.snp.makeConstraints { make in
|
||||
make.height.equalTo(48)
|
||||
}
|
||||
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var appleButton: UIButton = {
|
||||
var configuration = UIButton.Configuration.plain()
|
||||
configuration.background.cornerRadius = 8
|
||||
configuration.background.backgroundColor = .white
|
||||
configuration.imagePadding = 12
|
||||
configuration.image = UIImage(named: "apple_icon_01")
|
||||
configuration.attributedTitle = AttributedString("Login with Apple".localized, attributes: AttributeContainer([
|
||||
.font : UIFont.font(ofSize: 14, weight: .semibold),
|
||||
.foregroundColor : UIColor._333333
|
||||
]))
|
||||
|
||||
let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
|
||||
}))
|
||||
button.snp.makeConstraints { make in
|
||||
make.height.equalTo(48)
|
||||
}
|
||||
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var agreementLabel: YYLabel = {
|
||||
let userAgreementText = "User Agreement".localized
|
||||
let privacyPolicyText = "Privacy Policy".localized
|
||||
let text = "By continuing, you agree to the\n#1# and #2#".localizedReplace(text1: userAgreementText, text2: privacyPolicyText)
|
||||
|
||||
let userAgreementRange = (text as NSString).range(of: userAgreementText)
|
||||
let privacyPolicyRange = (text as NSString).range(of: privacyPolicyText)
|
||||
|
||||
let string = NSMutableAttributedString(string: text, attributes: [
|
||||
.font : UIFont.font(ofSize: 10, weight: .semibold),
|
||||
.foregroundColor : UIColor.FFDAA_4
|
||||
])
|
||||
|
||||
string.yy_setTextHighlight(userAgreementRange, color: .white, backgroundColor: nil) { [weak self] _, _, _, _ in
|
||||
guard let self = self else { return }
|
||||
let vc = XSBaseWebViewController()
|
||||
vc.webUrl = kXSUserAgreementWebUrl
|
||||
XSTool.topViewController?.navigationController?.pushViewController(vc, animated: true)
|
||||
self.dismiss(animated: true) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
string.yy_setTextHighlight(privacyPolicyRange, color: .white, backgroundColor: nil) { [weak self] _, _, _, _ in
|
||||
guard let self = self else { return }
|
||||
let vc = XSBaseWebViewController()
|
||||
vc.webUrl = kXSPrivacyPolicyWebUrl
|
||||
XSTool.topViewController?.navigationController?.pushViewController(vc, animated: true)
|
||||
self.dismiss(animated: true) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
let label = YYLabel()
|
||||
label.attributedText = string
|
||||
label.textAlignment = .center
|
||||
label.numberOfLines = 0
|
||||
return label
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.contentHeight = XSScreen.safeBottom + 320
|
||||
|
||||
xs_setupUI()
|
||||
}
|
||||
|
||||
@MainActor required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension XSLoginView {
|
||||
private func xs_setupUI() {
|
||||
addSubview(bgView)
|
||||
addSubview(closeButton)
|
||||
addSubview(titleLabel)
|
||||
addSubview(stackView)
|
||||
addSubview(agreementLabel)
|
||||
|
||||
bgView.snp.makeConstraints { make in
|
||||
make.left.right.top.equalToSuperview()
|
||||
}
|
||||
|
||||
closeButton.snp.makeConstraints { make in
|
||||
make.right.equalToSuperview().inset(23)
|
||||
make.top.equalToSuperview().offset(20)
|
||||
}
|
||||
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.top.equalTo(closeButton.snp.bottom).offset(12)
|
||||
}
|
||||
|
||||
stackView.snp.makeConstraints { make in
|
||||
make.width.equalTo(300)
|
||||
make.centerX.equalToSuperview()
|
||||
make.top.equalTo(closeButton.snp.bottom).offset(75)
|
||||
}
|
||||
|
||||
agreementLabel.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.bottom.equalToSuperview().inset(XSScreen.safeBottom + 20)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -44,6 +44,14 @@ class XSMineCoinsPackView: UIView {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
let tap = UITapGestureRecognizer { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
let vc = XSCoinsPackViewController()
|
||||
self.viewController?.navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
bgView.addGestureRecognizer(tap)
|
||||
|
||||
xs_setupUI()
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ class XSMineUserInfoView: UIView {
|
||||
avatarImageView.xs_setImage(userInfo?.avator, placeholder: UIImage(named: "avatar_placeholder_icon_01"))
|
||||
nickNameLabel.text = userInfo?.getNickName()
|
||||
idLabel.text = "ID: \(userInfo?.customer_id ?? "")"
|
||||
loginButton.isHidden = !(userInfo?.is_tourist ?? true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,6 +39,22 @@ class XSMineUserInfoView: UIView {
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var loginButton: UIButton = {
|
||||
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
let view = XSLoginView()
|
||||
view.present(in: nil)
|
||||
}))
|
||||
button.layer.cornerRadius = 16
|
||||
button.layer.masksToBounds = true
|
||||
button.layer.borderWidth = 1
|
||||
button.layer.borderColor = UIColor.white.withAlphaComponent(0.14).cgColor
|
||||
button.setTitle("Login".localized, for: .normal)
|
||||
button.setTitleColor(.white.withAlphaComponent(0.7), for: .normal)
|
||||
button.titleLabel?.font = .font(ofSize: 14, weight: .semibold)
|
||||
return button
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
@ -56,6 +73,7 @@ extension XSMineUserInfoView {
|
||||
addSubview(avatarImageView)
|
||||
addSubview(nickNameLabel)
|
||||
addSubview(idLabel)
|
||||
addSubview(loginButton)
|
||||
|
||||
avatarImageView.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(16)
|
||||
@ -74,6 +92,13 @@ extension XSMineUserInfoView {
|
||||
make.bottom.equalTo(avatarImageView).offset(-6)
|
||||
}
|
||||
|
||||
loginButton.snp.makeConstraints { make in
|
||||
make.centerY.equalTo(avatarImageView)
|
||||
make.right.equalToSuperview().inset(16)
|
||||
make.width.equalTo(86)
|
||||
make.height.equalTo(32)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
//
|
||||
// FACoinsRecordViewController.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/17.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class FACoinsRecordViewController: XSViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// MARK: - Navigation
|
||||
|
||||
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
// Get the new view controller using segue.destination.
|
||||
// Pass the selected object to the new view controller.
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
//
|
||||
// FAVipRecordViewController.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/17.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class FAVipRecordViewController: XSViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// MARK: - Navigation
|
||||
|
||||
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
// Get the new view controller using segue.destination.
|
||||
// Pass the selected object to the new view controller.
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
//
|
||||
// XSCoinsOrderRecordViewController.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/17.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
|
||||
class XSCoinsOrderRecordViewController: XSViewController {
|
||||
|
||||
private lazy var dataArr: [XSOrderRecordModel] = []
|
||||
private lazy var page = 1
|
||||
|
||||
private lazy var tableView: XSTableView = {
|
||||
let tableView = XSTableView(frame: .zero, style: .plain)
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
tableView.rowHeight = 70
|
||||
tableView.separatorColor = .white.withAlphaComponent(0.1)
|
||||
tableView.separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16)
|
||||
tableView.contentInset = .init(top: 10, left: 0, bottom: XSScreen.safeBottom + 10, right: 0)
|
||||
tableView.register(XSCoinsOrderRecordCell.self, forCellReuseIdentifier: "cell")
|
||||
tableView.ly_emptyView = XSEmpty.xs_emptyView()
|
||||
|
||||
tableView.xs_addRefreshHeader(insetTop: tableView.contentInset.top) { [weak self] in
|
||||
self?.handleHeaderRefresh(nil)
|
||||
}
|
||||
tableView.xs_addRefreshFooter(insetBottom: tableView.contentInset.bottom) { [weak self] in
|
||||
self?.handleFooterRefresh(nil)
|
||||
}
|
||||
return tableView
|
||||
}()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
self.view.backgroundColor = .clear
|
||||
|
||||
xs_setupUI()
|
||||
|
||||
Task {
|
||||
await requestDataArr(page: 1)
|
||||
}
|
||||
}
|
||||
|
||||
override func handleHeaderRefresh(_ completer: (() -> Void)?) {
|
||||
Task {
|
||||
await requestDataArr(page: 1)
|
||||
self.tableView.xs_endHeaderRefreshing()
|
||||
}
|
||||
}
|
||||
|
||||
override func handleFooterRefresh(_ completer: (() -> Void)?) {
|
||||
Task {
|
||||
await requestDataArr(page: self.page + 1)
|
||||
self.tableView.xs_endFooterRefreshing()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension XSCoinsOrderRecordViewController {
|
||||
|
||||
private func xs_setupUI() {
|
||||
view.addSubview(tableView)
|
||||
|
||||
tableView.snp.makeConstraints { make in
|
||||
make.left.right.bottom.equalToSuperview()
|
||||
make.top.equalToSuperview().offset(1)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//MARK: UITableViewDelegate UITableViewDataSource
|
||||
extension XSCoinsOrderRecordViewController: UITableViewDelegate, UITableViewDataSource {
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! XSCoinsOrderRecordCell
|
||||
cell.model = dataArr[indexPath.row]
|
||||
return cell
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return self.dataArr.count
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension XSCoinsOrderRecordViewController {
|
||||
|
||||
private func requestDataArr(page: Int) async {
|
||||
|
||||
guard let listModel = await XSStoreAPI.requestRechargeRecord(page: page, buyType: .coins) else { return }
|
||||
guard let list = listModel.list else { return }
|
||||
|
||||
if page == 1 {
|
||||
self.dataArr.removeAll()
|
||||
}
|
||||
self.dataArr += list
|
||||
self.page = page
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
25
XSeri/Class/Store/Controller/XSCoinsPackViewController.swift
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// XSCoinsPackViewController.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class XSCoinsPackViewController: XSViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
self.navigationController?.setNavigationBarHidden(false, animated: true)
|
||||
xs_setNavigationStyle()
|
||||
}
|
||||
|
||||
}
|
||||
@ -11,9 +11,10 @@ import JXSegmentedView
|
||||
|
||||
class XSOrderRecordsViewController: XSViewController {
|
||||
|
||||
private var segmentedItemWidth: CGFloat = 0
|
||||
|
||||
private lazy var viewControllers: [XSViewController] = {
|
||||
return [FACoinsRecordViewController(), FAVipRecordViewController()]
|
||||
return [XSCoinsOrderRecordViewController(), XSVipOrderRecordViewController()]
|
||||
}()
|
||||
|
||||
private lazy var bgImageView: UIImageView = {
|
||||
@ -21,27 +22,53 @@ class XSOrderRecordsViewController: XSViewController {
|
||||
return imageView
|
||||
}()
|
||||
|
||||
private lazy var segmentedContainerView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .white.withAlphaComponent(0.16)
|
||||
view.layer.cornerRadius = 16
|
||||
view.layer.masksToBounds = true
|
||||
return view
|
||||
}()
|
||||
|
||||
/// Segmented 数据源
|
||||
private lazy var segmentedDataSource: JXSegmentedTitleDataSource = {
|
||||
let dataSource = JXSegmentedTitleDataSource()
|
||||
dataSource.titles = ["Coin Record".localized, "VIP Record".localized]
|
||||
dataSource.titleNormalColor = UIColor.white.withAlphaComponent(0.55)
|
||||
dataSource.titleSelectedColor = .white
|
||||
dataSource.titleNormalFont = .font(ofSize: 16, weight: .medium)
|
||||
dataSource.titleSelectedColor = ._00181_A
|
||||
dataSource.titleNormalFont = .font(ofSize: 16, weight: .semibold)
|
||||
dataSource.titleSelectedFont = .font(ofSize: 16, weight: .semibold)
|
||||
dataSource.isTitleColorGradientEnabled = true
|
||||
dataSource.itemSpacing = 24
|
||||
dataSource.isItemSpacingAverageEnabled = false
|
||||
dataSource.itemSpacing = 0
|
||||
return dataSource
|
||||
}()
|
||||
|
||||
private lazy var segmentedIndicator: XSSegmentedGradientIndicatorView = {
|
||||
let indicator = XSSegmentedGradientIndicatorView()
|
||||
indicator.gradientColors = [
|
||||
UIColor.F_5_BD_7_E.cgColor,
|
||||
UIColor.FFEABC.cgColor,
|
||||
UIColor.FFCF_99.cgColor
|
||||
]
|
||||
indicator.gradientLocations = [0, 0.5, 1]
|
||||
indicator.startPoint = CGPoint(x: 0, y: 0.5)
|
||||
indicator.endPoint = CGPoint(x: 1, y: 0.5)
|
||||
indicator.indicatorHeight = 36
|
||||
indicator.indicatorCornerRadius = 18
|
||||
indicator.indicatorWidthIncrement = 0
|
||||
return indicator
|
||||
}()
|
||||
|
||||
/// Segmented 视图 (Tab 栏)
|
||||
private lazy var segmentedView: JXSegmentedView = {
|
||||
let view = JXSegmentedView()
|
||||
view.dataSource = segmentedDataSource
|
||||
view.delegate = self
|
||||
view.listContainer = listContainerView
|
||||
view.contentEdgeInsetLeft = 16
|
||||
view.contentEdgeInsetRight = 16
|
||||
view.contentEdgeInsetLeft = 6
|
||||
view.contentEdgeInsetRight = 6
|
||||
view.indicators = [segmentedIndicator]
|
||||
view.backgroundColor = .clear
|
||||
return view
|
||||
}()
|
||||
@ -53,7 +80,8 @@ class XSOrderRecordsViewController: XSViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.title = "Order Records".localized
|
||||
self.view.backgroundColor = .black
|
||||
xs_setupUI()
|
||||
}
|
||||
|
||||
@ -64,6 +92,18 @@ class XSOrderRecordsViewController: XSViewController {
|
||||
xs_setNavigationStyle()
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
let horizontalInset = segmentedView.contentEdgeInsetLeft + segmentedView.contentEdgeInsetRight
|
||||
let availableWidth = segmentedContainerView.bounds.width - horizontalInset
|
||||
let itemWidth = floor(availableWidth / CGFloat(max(segmentedDataSource.titles.count, 1)))
|
||||
guard itemWidth > 0, segmentedItemWidth != itemWidth else { return }
|
||||
segmentedItemWidth = itemWidth
|
||||
segmentedDataSource.itemWidth = itemWidth
|
||||
segmentedView.reloadDataWithoutListContainer()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -71,15 +111,27 @@ extension XSOrderRecordsViewController {
|
||||
|
||||
private func xs_setupUI() {
|
||||
view.addSubview(bgImageView)
|
||||
view.addSubview(segmentedContainerView)
|
||||
segmentedContainerView.addSubview(segmentedView)
|
||||
view.addSubview(listContainerView)
|
||||
|
||||
bgImageView.snp.makeConstraints { make in
|
||||
make.left.right.top.equalToSuperview()
|
||||
}
|
||||
|
||||
segmentedContainerView.snp.makeConstraints { make in
|
||||
make.left.right.equalToSuperview().inset(16)
|
||||
make.top.equalTo(self.view.safeAreaLayoutGuide).offset(10)
|
||||
make.height.equalTo(48)
|
||||
}
|
||||
|
||||
segmentedView.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
|
||||
listContainerView.snp.makeConstraints { make in
|
||||
make.left.right.bottom.equalToSuperview()
|
||||
make.top.equalTo(self.view.safeAreaLayoutGuide)
|
||||
make.top.equalTo(segmentedContainerView.snp.bottom).offset(8)
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,4 +153,9 @@ extension XSOrderRecordsViewController: JXSegmentedListContainerViewDataSource {
|
||||
func listContainerView(_ listContainerView: JXSegmentedListContainerView, initListAt index: Int) -> JXSegmentedListContainerViewListDelegate {
|
||||
return viewControllers[index]
|
||||
}
|
||||
|
||||
func scrollViewClass(in listContainerView: JXSegmentedListContainerView) -> AnyClass {
|
||||
return XSPagerScrollView.self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,6 +9,9 @@ import UIKit
|
||||
|
||||
class XSStoreViewController: XSViewController {
|
||||
|
||||
private var payRequest: XSPayDataRequest?
|
||||
|
||||
private var payDataModel: XSPayDateModel? = XSIapManager.manager.payDateModel
|
||||
|
||||
private lazy var scrollView: XSScrollView = {
|
||||
let scrollView = XSScrollView()
|
||||
@ -22,12 +25,32 @@ class XSStoreViewController: XSViewController {
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var coinsView: XSStoreCoinsView = {
|
||||
let view = XSStoreCoinsView()
|
||||
view.buyFinishHandle = { [weak self] in
|
||||
self?.buyFinish()
|
||||
}
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var vipView: XSStoreVipView = {
|
||||
let view = XSStoreVipView()
|
||||
view.buyFinishHandle = { [weak self] in
|
||||
self?.buyFinish()
|
||||
}
|
||||
return view
|
||||
}()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
self.title = "Store".localized
|
||||
|
||||
|
||||
xs_setupUI()
|
||||
|
||||
reloadData()
|
||||
|
||||
requestPayData()
|
||||
}
|
||||
|
||||
|
||||
@ -37,6 +60,50 @@ class XSStoreViewController: XSViewController {
|
||||
xs_setNavigationStyle()
|
||||
}
|
||||
|
||||
func reloadData() {
|
||||
self.stackView.xs_removeAllArrangedSubview()
|
||||
guard let model = self.payDataModel else { return }
|
||||
|
||||
if let sort = model.sort, sort.count > 0 {
|
||||
sort.forEach {
|
||||
if $0 == .vip {
|
||||
self.addVipView()
|
||||
} else if $0 == .coin {
|
||||
self.addCoinsView()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.addVipView()
|
||||
self.addCoinsView()
|
||||
}
|
||||
// self.stackView.addArrangedSubview(tipView)
|
||||
}
|
||||
|
||||
private func addCoinsView() {
|
||||
guard let model = self.payDataModel else { return }
|
||||
|
||||
var newList: [XSPayItem] = []
|
||||
if let list = model.list_coins, list.count > 0 {
|
||||
newList = list
|
||||
}
|
||||
if newList.count > 0 {
|
||||
coinsView.setDataArr(newList)
|
||||
self.stackView.addArrangedSubview(coinsView)
|
||||
}
|
||||
}
|
||||
|
||||
private func addVipView() {
|
||||
guard let list = payDataModel?.list_sub_vip else { return }
|
||||
guard list.count > 0 else { return }
|
||||
|
||||
self.vipView.dataArr = list
|
||||
self.stackView.addArrangedSubview(self.vipView)
|
||||
}
|
||||
|
||||
private func buyFinish() {
|
||||
self.requestPayData()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension XSStoreViewController {
|
||||
@ -58,3 +125,21 @@ extension XSStoreViewController {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension XSStoreViewController {
|
||||
|
||||
private func requestPayData() {
|
||||
payRequest = XSPayDataRequest()
|
||||
|
||||
payRequest?.requestProducts { [weak self] model in
|
||||
guard let self = self else { return }
|
||||
guard let model = model else { return }
|
||||
|
||||
self.payDataModel = model
|
||||
|
||||
self.reloadData()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,107 @@
|
||||
//
|
||||
// XSVipOrderRecordViewController.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/17.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class XSVipOrderRecordViewController: XSViewController {
|
||||
|
||||
private lazy var dataArr: [XSOrderRecordModel] = []
|
||||
private lazy var page = 1
|
||||
|
||||
private lazy var tableView: XSTableView = {
|
||||
let tableView = XSTableView(frame: .zero, style: .plain)
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
tableView.rowHeight = 70
|
||||
tableView.separatorColor = .white.withAlphaComponent(0.1)
|
||||
tableView.separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16)
|
||||
tableView.contentInset = .init(top: 10, left: 0, bottom: XSScreen.safeBottom + 10, right: 0)
|
||||
tableView.register(XSVipOrderRecordCell.self, forCellReuseIdentifier: "cell")
|
||||
tableView.ly_emptyView = XSEmpty.xs_emptyView()
|
||||
|
||||
tableView.xs_addRefreshHeader(insetTop: tableView.contentInset.top) { [weak self] in
|
||||
self?.handleHeaderRefresh(nil)
|
||||
}
|
||||
tableView.xs_addRefreshFooter(insetBottom: tableView.contentInset.bottom) { [weak self] in
|
||||
self?.handleFooterRefresh(nil)
|
||||
}
|
||||
return tableView
|
||||
}()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
self.view.backgroundColor = .clear
|
||||
|
||||
xs_setupUI()
|
||||
|
||||
Task {
|
||||
await requestDataArr(page: 1)
|
||||
}
|
||||
}
|
||||
|
||||
override func handleHeaderRefresh(_ completer: (() -> Void)?) {
|
||||
Task {
|
||||
await requestDataArr(page: 1)
|
||||
self.tableView.xs_endHeaderRefreshing()
|
||||
}
|
||||
}
|
||||
|
||||
override func handleFooterRefresh(_ completer: (() -> Void)?) {
|
||||
Task {
|
||||
await requestDataArr(page: self.page + 1)
|
||||
self.tableView.xs_endFooterRefreshing()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension XSVipOrderRecordViewController {
|
||||
|
||||
private func xs_setupUI() {
|
||||
view.addSubview(tableView)
|
||||
|
||||
tableView.snp.makeConstraints { make in
|
||||
make.left.right.bottom.equalToSuperview()
|
||||
make.top.equalToSuperview().offset(1)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//MARK: UITableViewDelegate UITableViewDataSource
|
||||
extension XSVipOrderRecordViewController: UITableViewDelegate, UITableViewDataSource {
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! XSVipOrderRecordCell
|
||||
cell.model = dataArr[indexPath.row]
|
||||
return cell
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return self.dataArr.count
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension XSVipOrderRecordViewController {
|
||||
|
||||
private func requestDataArr(page: Int) async {
|
||||
|
||||
guard let listModel = await XSStoreAPI.requestRechargeRecord(page: page, buyType: .vip) else { return }
|
||||
guard let list = listModel.list else { return }
|
||||
|
||||
if page == 1 {
|
||||
self.dataArr.removeAll()
|
||||
}
|
||||
self.dataArr += list
|
||||
self.page = page
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -46,6 +46,7 @@ class XSWalletViewController: XSViewController {
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
self.title = "Details".localized
|
||||
self.view.backgroundColor = .black
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(loginStateDidChangeNotification), name: XSLoginManager.loginStateDidChangeNotification, object: nil)
|
||||
|
||||
xs_setupUI()
|
||||
@ -124,6 +125,9 @@ extension XSWalletViewController: UITableViewDelegate, UITableViewDataSource {
|
||||
case .rewardCoins:
|
||||
vc = XSRewardCoinsViewController()
|
||||
|
||||
case .feedback:
|
||||
vc = XSFeedbackViewController()
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
16
XSeri/Class/Store/Model/XSOrderRecordModel.swift
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// XSOrderRecordModel.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SmartCodable
|
||||
|
||||
struct XSOrderRecordModel: SmartCodable {
|
||||
|
||||
var type: String?
|
||||
var value: String?
|
||||
var created_at: String?
|
||||
}
|
||||
80
XSeri/Class/Store/View/XSCoinsOrderRecordCell.swift
Normal file
@ -0,0 +1,80 @@
|
||||
//
|
||||
// XSCoinsOrderRecordCell.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
|
||||
class XSCoinsOrderRecordCell: XSTableViewCell {
|
||||
|
||||
var model: XSOrderRecordModel? {
|
||||
didSet {
|
||||
titleLabel.text = model?.type
|
||||
dateLabel.text = model?.created_at
|
||||
coinsLabel.text = "+\(model?.value ?? "0")"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private lazy var titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 16, weight: .semibold)
|
||||
label.textColor = .white
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var dateLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 14, weight: .regular)
|
||||
label.textColor = .FFDAA_4
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var coinsIconImageView = UIImageView(image: UIImage(named: "coins_icon_03"))
|
||||
|
||||
private lazy var coinsLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 16, weight: .semibold)
|
||||
label.textColor = .white
|
||||
return label
|
||||
}()
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
|
||||
|
||||
contentView.addSubview(titleLabel)
|
||||
contentView.addSubview(dateLabel)
|
||||
contentView.addSubview(coinsIconImageView)
|
||||
contentView.addSubview(coinsLabel)
|
||||
|
||||
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(32)
|
||||
make.top.equalToSuperview().offset(15)
|
||||
}
|
||||
|
||||
dateLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(titleLabel)
|
||||
make.bottom.equalToSuperview().offset(-15)
|
||||
}
|
||||
|
||||
coinsLabel.snp.makeConstraints { make in
|
||||
make.centerY.equalToSuperview()
|
||||
make.right.equalToSuperview().offset(-25)
|
||||
}
|
||||
|
||||
coinsIconImageView.snp.makeConstraints { make in
|
||||
make.centerY.equalToSuperview()
|
||||
make.right.equalTo(coinsLabel.snp.left).offset(-4)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
//
|
||||
// XSSegmentedGradientIndicatorView.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/17.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import JXSegmentedView
|
||||
|
||||
class XSSegmentedGradientIndicatorView: JXSegmentedIndicatorBackgroundView {
|
||||
|
||||
var gradientColors: [CGColor] = [] {
|
||||
didSet {
|
||||
gradientLayer.colors = gradientColors
|
||||
}
|
||||
}
|
||||
|
||||
var gradientLocations: [NSNumber]? {
|
||||
didSet {
|
||||
gradientLayer.locations = gradientLocations
|
||||
}
|
||||
}
|
||||
|
||||
var startPoint: CGPoint = CGPoint(x: 0, y: 0.5) {
|
||||
didSet {
|
||||
gradientLayer.startPoint = startPoint
|
||||
}
|
||||
}
|
||||
|
||||
var endPoint: CGPoint = CGPoint(x: 1, y: 0.5) {
|
||||
didSet {
|
||||
gradientLayer.endPoint = endPoint
|
||||
}
|
||||
}
|
||||
|
||||
private let gradientLayer = CAGradientLayer()
|
||||
|
||||
override func commonInit() {
|
||||
super.commonInit()
|
||||
indicatorColor = .clear
|
||||
backgroundColor = .clear
|
||||
layer.insertSublayer(gradientLayer, at: 0)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
backgroundColor = .clear
|
||||
gradientLayer.frame = bounds
|
||||
gradientLayer.cornerRadius = layer.cornerRadius
|
||||
gradientLayer.colors = gradientColors
|
||||
gradientLayer.locations = gradientLocations
|
||||
gradientLayer.startPoint = startPoint
|
||||
gradientLayer.endPoint = endPoint
|
||||
}
|
||||
|
||||
}
|
||||
15
XSeri/Class/Store/View/XSStoreCell.swift
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// XSStoreCell.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class XSStoreCell: UICollectionViewCell {
|
||||
|
||||
var item: XSPayItem?
|
||||
|
||||
var xs_isSelected: Bool?
|
||||
}
|
||||
29
XSeri/Class/Store/View/XSStoreCoinsBigCell.swift
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// XSStoreCoinsBigCell.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class XSStoreCoinsBigCell: XSStoreCell {
|
||||
|
||||
|
||||
private lazy var bgView: XSView = {
|
||||
let view = XSView()
|
||||
view.xs_colors = []
|
||||
return view
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
|
||||
}
|
||||
|
||||
@MainActor required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
12
XSeri/Class/Store/View/XSStoreCoinsSmallCell.swift
Normal file
@ -0,0 +1,12 @@
|
||||
//
|
||||
// XSStoreCoinsSmallCell.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class XSStoreCoinsSmallCell: XSStoreCell {
|
||||
|
||||
}
|
||||
12
XSeri/Class/Store/View/XSStoreCoinsSpreadCell.swift
Normal file
@ -0,0 +1,12 @@
|
||||
//
|
||||
// XSStoreCoinsSpreadCell.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class XSStoreCoinsSpreadCell: XSStoreCell {
|
||||
|
||||
}
|
||||
@ -10,6 +10,14 @@ import SnapKit
|
||||
|
||||
class XSStoreCoinsView: UIView {
|
||||
|
||||
var buyFinishHandle: (() -> Void)?
|
||||
|
||||
var shortPlayId: String?
|
||||
var videoId: String?
|
||||
|
||||
private lazy var dataArr: [[XSPayItem]] = []
|
||||
private var selectedIndexPath: IndexPath?
|
||||
|
||||
private lazy var titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 16, weight: .bold)
|
||||
@ -18,39 +26,43 @@ class XSStoreCoinsView: UIView {
|
||||
return label
|
||||
}()
|
||||
|
||||
// private lazy var collectionViewLayout: UICollectionViewCompositionalLayout = {
|
||||
// let config = UICollectionViewCompositionalLayoutConfiguration()
|
||||
// config.interSectionSpacing = 10
|
||||
//
|
||||
// let layout = UICollectionViewCompositionalLayout { [weak self] section, _ in
|
||||
// guard let self = self else { return nil}
|
||||
// guard let model = dataArr[section].first else { return nil }
|
||||
//
|
||||
// if model.buy_type == .subCoins {
|
||||
// return self.coinsBigLayoutSection()
|
||||
// } else if model.size == .big {
|
||||
// return self.bigLayoutSection()
|
||||
// } else {
|
||||
// return self.smallLayoutSection()
|
||||
// }
|
||||
// }
|
||||
// layout.configuration = config
|
||||
//
|
||||
// return layout
|
||||
// }()
|
||||
//
|
||||
// private lazy var collectionView: FACollectionView = {
|
||||
// let collectionView = FACollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
||||
// collectionView.delegate = self
|
||||
// collectionView.dataSource = self
|
||||
// collectionView.clipsToBounds = false
|
||||
// collectionView.isScrollEnabled = false
|
||||
// collectionView.register(FAStoreCoinsBigCell.self, forCellWithReuseIdentifier: "FAStoreCoinsBigCell")
|
||||
// collectionView.register(FAStoreCoinsSmallCell.self, forCellWithReuseIdentifier: "FAStoreCoinsSmallCell")
|
||||
// collectionView.register(FAStoreCoinsPackCell.self, forCellWithReuseIdentifier: "FAStoreCoinsPackCell")
|
||||
// collectionView.addObserver(self, forKeyPath: "contentSize", context: nil)
|
||||
// return collectionView
|
||||
// }()
|
||||
private lazy var collectionViewLayout: UICollectionViewCompositionalLayout = {
|
||||
let config = UICollectionViewCompositionalLayoutConfiguration()
|
||||
config.interSectionSpacing = 12
|
||||
|
||||
let layout = UICollectionViewCompositionalLayout { [weak self] section, environment in
|
||||
guard let self = self else { return nil}
|
||||
guard let model = dataArr[section].first else { return nil }
|
||||
|
||||
if model.size == .spread {
|
||||
return self.spreadLayoutSection(environment)
|
||||
} else if model.size == .big {
|
||||
return self.bigLayoutSection(environment)
|
||||
} else {
|
||||
return self.smallLayoutSection(environment)
|
||||
}
|
||||
}
|
||||
layout.configuration = config
|
||||
|
||||
return layout
|
||||
}()
|
||||
|
||||
private lazy var collectionView: XSCollectionView = {
|
||||
let collectionView = XSCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
||||
collectionView.delegate = self
|
||||
collectionView.dataSource = self
|
||||
collectionView.clipsToBounds = false
|
||||
collectionView.isScrollEnabled = false
|
||||
collectionView.register(XSStoreCoinsBigCell.self, forCellWithReuseIdentifier: XSPayItem.SizeType.big.rawValue)
|
||||
collectionView.register(XSStoreCoinsSmallCell.self, forCellWithReuseIdentifier: XSPayItem.SizeType.small.rawValue)
|
||||
collectionView.register(XSStoreCoinsSpreadCell.self, forCellWithReuseIdentifier: XSPayItem.SizeType.spread.rawValue)
|
||||
collectionView.addObserver(self, forKeyPath: "contentSize", context: nil)
|
||||
return collectionView
|
||||
}()
|
||||
|
||||
deinit {
|
||||
collectionView.removeObserver(self, forKeyPath: "contentSize")
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
@ -61,17 +73,153 @@ class XSStoreCoinsView: UIView {
|
||||
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 + 1
|
||||
self.collectionView.snp.updateConstraints { make in
|
||||
make.height.equalTo(height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setDataArr(_ arr: [XSPayItem]) {
|
||||
self.dataArr.removeAll()
|
||||
var bigArr: [XSPayItem] = []
|
||||
var smallArr: [XSPayItem] = []
|
||||
var spreadArr: [XSPayItem] = []
|
||||
|
||||
arr.forEach {
|
||||
if $0.size == .spread {
|
||||
spreadArr.append($0)
|
||||
} else if $0.size == .big {
|
||||
bigArr.append($0)
|
||||
} else {
|
||||
smallArr.append($0)
|
||||
}
|
||||
}
|
||||
|
||||
if bigArr.count > 0 {
|
||||
self.dataArr.append(bigArr)
|
||||
}
|
||||
if spreadArr.count > 0 {
|
||||
self.dataArr.append(spreadArr)
|
||||
}
|
||||
if smallArr.count > 0 {
|
||||
self.dataArr.append(smallArr)
|
||||
}
|
||||
self.collectionView.reloadData()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension XSStoreCoinsView {
|
||||
|
||||
private func xs_setupUI() {
|
||||
addSubview(titleLabel)
|
||||
addSubview(collectionView)
|
||||
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(16)
|
||||
make.top.equalToSuperview()
|
||||
}
|
||||
|
||||
collectionView.snp.makeConstraints { make in
|
||||
make.top.equalToSuperview().offset(36)
|
||||
make.left.right.equalToSuperview().inset(16)
|
||||
make.height.equalTo(1)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension XSStoreCoinsView {
|
||||
|
||||
private func bigLayoutSection(_ environment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection {
|
||||
let contentWidth = environment.container.effectiveContentSize.width
|
||||
let itemWidth = floor((contentWidth - 15) / 2)
|
||||
|
||||
let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .absolute(itemWidth), heightDimension: .fractionalHeight(1)))
|
||||
|
||||
let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(84)), subitems: [item])
|
||||
group.interItemSpacing = .fixed(15)
|
||||
|
||||
let layoutSection = NSCollectionLayoutSection(group: group)
|
||||
layoutSection.interGroupSpacing = 12
|
||||
layoutSection.contentInsets = .zero
|
||||
return layoutSection
|
||||
}
|
||||
|
||||
private func smallLayoutSection(_ environment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection {
|
||||
let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1 / 3), heightDimension: .fractionalHeight(1)))
|
||||
|
||||
let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(121)), subitems: [item])
|
||||
group.interItemSpacing = .fixed(8)
|
||||
|
||||
let layoutSection = NSCollectionLayoutSection(group: group)
|
||||
layoutSection.interGroupSpacing = 10
|
||||
layoutSection.contentInsets = .zero
|
||||
return layoutSection
|
||||
}
|
||||
|
||||
private func spreadLayoutSection(_ environment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection {
|
||||
let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)))
|
||||
|
||||
let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(84)), subitems: [item])
|
||||
|
||||
|
||||
let layoutSection = NSCollectionLayoutSection(group: group)
|
||||
layoutSection.interGroupSpacing = 10
|
||||
layoutSection.contentInsets = .zero
|
||||
return layoutSection
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: UICollectionViewDelegate UICollectionViewDataSource
|
||||
extension XSStoreCoinsView: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let item = dataArr[indexPath.section][indexPath.row]
|
||||
var identifier = item.size?.rawValue ?? ""
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! XSStoreCell
|
||||
cell.item = item
|
||||
cell.xs_isSelected = selectedIndexPath == indexPath
|
||||
return cell
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return dataArr[section].count
|
||||
}
|
||||
|
||||
func numberOfSections(in collectionView: UICollectionView) -> Int {
|
||||
return dataArr.count
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
let model = dataArr[indexPath.section][indexPath.row]
|
||||
self.selectedIndexPath = indexPath
|
||||
collectionView.reloadData()
|
||||
|
||||
// if model.buy_type == .subCoins {
|
||||
// let view = FACoinPackConfirmView()
|
||||
// view.shortPlayId = self.shortPlayId
|
||||
// view.videoId = self.videoId
|
||||
// view.model = model
|
||||
// view.buyFinishHandle = { [weak self] in
|
||||
// guard let self = self else { return }
|
||||
// FALogin.manager.requestUserInfo(completer: nil)
|
||||
// self.buyFinishHandle?()
|
||||
// }
|
||||
// view.present(in: nil)
|
||||
// } else {
|
||||
// FAIapManager.manager.start(model: model, shortPlayId: self.shortPlayId, videoId: self.videoId) { [weak self] finish in
|
||||
// guard let self = self else { return }
|
||||
// if finish {
|
||||
// FALogin.manager.requestUserInfo(completer: nil)
|
||||
// self.buyFinishHandle?()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
12
XSeri/Class/Store/View/XSStoreVipCell.swift
Normal file
@ -0,0 +1,12 @@
|
||||
//
|
||||
// XSStoreVipCell.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class XSStoreVipCell: XSStoreCell {
|
||||
|
||||
}
|
||||
@ -6,9 +6,140 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
|
||||
class XSStoreVipView: UIView {
|
||||
|
||||
var buyFinishHandle: (() -> Void)?
|
||||
|
||||
var shortPlayId: String?
|
||||
var videoId: String?
|
||||
|
||||
var dataArr: [XSPayItem] = [] {
|
||||
didSet {
|
||||
collectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
private lazy var titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 16, weight: .bold)
|
||||
label.textColor = .white
|
||||
label.text = "VIP Membership".localized
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var subtitleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 14, weight: .regular)
|
||||
label.textColor = .white.withAlphaComponent(0.5)
|
||||
label.text = "Auto renew,cancel anytime".localized
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var collectionViewLayout: UICollectionViewCompositionalLayout = {
|
||||
|
||||
let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)))
|
||||
|
||||
let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(110)), subitems: [item])
|
||||
|
||||
let layoutSection = NSCollectionLayoutSection(group: group)
|
||||
layoutSection.interGroupSpacing = 14
|
||||
layoutSection.contentInsets = .init(top: 0, leading: 16, bottom: 0, trailing: 16)
|
||||
|
||||
|
||||
let config = UICollectionViewCompositionalLayoutConfiguration()
|
||||
|
||||
let layout = UICollectionViewCompositionalLayout(section: layoutSection)
|
||||
layout.configuration = config
|
||||
|
||||
return layout
|
||||
}()
|
||||
|
||||
private lazy var collectionView: XSCollectionView = {
|
||||
let collectionView = XSCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
||||
collectionView.delegate = self
|
||||
collectionView.dataSource = self
|
||||
collectionView.isScrollEnabled = false
|
||||
collectionView.addObserver(self, forKeyPath: "contentSize", context: nil)
|
||||
collectionView.register(XSStoreVipCell.self, forCellWithReuseIdentifier: "cell")
|
||||
return collectionView
|
||||
}()
|
||||
|
||||
deinit {
|
||||
collectionView.removeObserver(self, forKeyPath: "contentSize")
|
||||
}
|
||||
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
xs_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 + 1
|
||||
self.collectionView.snp.updateConstraints { make in
|
||||
make.height.equalTo(height)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension XSStoreVipView {
|
||||
|
||||
private func xs_setupUI() {
|
||||
addSubview(titleLabel)
|
||||
addSubview(subtitleLabel)
|
||||
addSubview(collectionView)
|
||||
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(16)
|
||||
make.top.equalToSuperview()
|
||||
}
|
||||
|
||||
subtitleLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(titleLabel)
|
||||
make.top.equalTo(titleLabel.snp.bottom).offset(0)
|
||||
}
|
||||
|
||||
collectionView.snp.makeConstraints { make in
|
||||
make.left.right.bottom.equalToSuperview()
|
||||
make.top.equalToSuperview().offset(54)
|
||||
make.height.equalTo(1)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//MARK: UICollectionViewDelegate UICollectionViewDataSource
|
||||
extension XSStoreVipView: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! XSStoreVipCell
|
||||
cell.item = self.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 = self.dataArr[indexPath.row]
|
||||
|
||||
// FAIapManager.manager.start(model: model, shortPlayId: self.shortPlayId, videoId: self.videoId) { [weak self] finish in
|
||||
// guard let self = self else { return }
|
||||
// if finish {
|
||||
// FALogin.manager.requestUserInfo(completer: nil)
|
||||
// self.buyFinishHandle?()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
71
XSeri/Class/Store/View/XSVipOrderRecordCell.swift
Normal file
@ -0,0 +1,71 @@
|
||||
//
|
||||
// XSVipOrderRecordCell.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class XSVipOrderRecordCell: XSTableViewCell {
|
||||
|
||||
var model: XSOrderRecordModel? {
|
||||
didSet {
|
||||
titleLabel.text = model?.type
|
||||
dateLabel.text = model?.created_at
|
||||
countLabel.text = "+\(model?.value ?? "0")"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private lazy var titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 16, weight: .semibold)
|
||||
label.textColor = .white
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var dateLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 14, weight: .regular)
|
||||
label.textColor = .FFDAA_4
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var countLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 16, weight: .semibold)
|
||||
label.textColor = .white
|
||||
return label
|
||||
}()
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
|
||||
|
||||
contentView.addSubview(titleLabel)
|
||||
contentView.addSubview(dateLabel)
|
||||
contentView.addSubview(countLabel)
|
||||
|
||||
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(32)
|
||||
make.top.equalToSuperview().offset(15)
|
||||
}
|
||||
|
||||
dateLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(titleLabel)
|
||||
make.bottom.equalToSuperview().offset(-15)
|
||||
}
|
||||
|
||||
countLabel.snp.makeConstraints { make in
|
||||
make.centerY.equalToSuperview()
|
||||
make.right.equalToSuperview().offset(-25)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
159
XSeri/Libs/Alert/XSAlert.swift
Normal file
@ -0,0 +1,159 @@
|
||||
//
|
||||
// XSAlert.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
|
||||
class XSAlert: XSBaseAlert {
|
||||
|
||||
private var contentBorderImage = UIImage(named: "alert_bg_border_color_image")
|
||||
|
||||
private var buttonText: String? {
|
||||
didSet {
|
||||
self.highlightButton.updateConfiguration()
|
||||
}
|
||||
}
|
||||
|
||||
private lazy var titleBgView: UIView = {
|
||||
let view = XSView()
|
||||
view.xs_colors = [UIColor.black.withAlphaComponent(0).cgColor, UIColor.FFDAA_4.withAlphaComponent(0.34).cgColor, UIColor.black.withAlphaComponent(0).cgColor]
|
||||
view.xs_startPoint = .init(x: 0, y: 0.5)
|
||||
view.xs_endPoint = .init(x: 1, y: 0.5)
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 18, weight: .semibold)
|
||||
label.textColor = .FFDAA_4
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var imageView: UIImageView = {
|
||||
let imageView = UIImageView()
|
||||
return imageView
|
||||
}()
|
||||
|
||||
private lazy var textLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 12, weight: .regular)
|
||||
label.textColor = .white
|
||||
label.numberOfLines = 0
|
||||
label.textAlignment = .center
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var highlightButton: UIButton = {
|
||||
var configuration = UIButton.Configuration.plain()
|
||||
|
||||
let button = XSButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.handleHighlightButton()
|
||||
}))
|
||||
button.xs_colors = [UIColor._0_C_0701.withAlphaComponent(0).cgColor, UIColor._16110_E.cgColor, UIColor._574537.cgColor]
|
||||
button.xs_startPoint = .init(x: 0.5, y: 0)
|
||||
button.xs_endPoint = .init(x: 0.5, y: 1)
|
||||
button.layer.cornerRadius = 24
|
||||
button.layer.masksToBounds = true
|
||||
button.layer.borderWidth = 1
|
||||
button.layer.borderColor = UIColor.FFDAA_4.cgColor
|
||||
button.configurationUpdateHandler = { [weak self] button in
|
||||
guard let self = self else { return }
|
||||
var configuration = button.configuration
|
||||
|
||||
configuration?.attributedTitle = AttributedString(self.buttonText ?? "", attributes: AttributeContainer([
|
||||
.font : UIFont.font(ofSize: 14, weight: .semibold),
|
||||
.foregroundColor : UIColor.FFDAA_4
|
||||
]))
|
||||
|
||||
button.configuration = configuration
|
||||
|
||||
}
|
||||
return button
|
||||
}()
|
||||
|
||||
deinit {
|
||||
self.contentView.removeObserver(self, forKeyPath: "bounds")
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.contentView.backgroundColor = .black
|
||||
self.contentView.layer.cornerRadius = 36
|
||||
self.contentView.layer.masksToBounds = true
|
||||
self.contentView.layer.borderWidth = 1
|
||||
self.contentView.addObserver(self, forKeyPath: "bounds", context: nil)
|
||||
|
||||
xs_setupUI()
|
||||
}
|
||||
|
||||
@MainActor required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
}
|
||||
|
||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||
if keyPath == "bounds" {
|
||||
self.contentBorderImage = self.contentBorderImage?.xs_resized(to: self.contentView.bounds.size)
|
||||
self.contentView.layer.borderColor = UIColor(patternImage: self.contentBorderImage!).cgColor
|
||||
}
|
||||
}
|
||||
|
||||
func show(title: String, text: String, image: UIImage?, buttonText: String) -> Self {
|
||||
self.titleLabel.text = title
|
||||
self.textLabel.text = text
|
||||
self.imageView.image = image
|
||||
self.buttonText = buttonText
|
||||
return self
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension XSAlert {
|
||||
|
||||
private func xs_setupUI() {
|
||||
contentView.addSubview(titleBgView)
|
||||
titleBgView.addSubview(titleLabel)
|
||||
contentView.addSubview(imageView)
|
||||
contentView.addSubview(textLabel)
|
||||
contentView.addSubview(highlightButton)
|
||||
|
||||
titleBgView.snp.makeConstraints { make in
|
||||
make.top.equalToSuperview()
|
||||
make.left.right.equalToSuperview().inset(23)
|
||||
make.height.equalTo(48)
|
||||
make.width.equalTo(256)
|
||||
}
|
||||
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.center.equalToSuperview()
|
||||
}
|
||||
|
||||
imageView.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.centerY.equalTo(self.contentView.snp.top).inset(131)
|
||||
}
|
||||
|
||||
textLabel.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.right.lessThanOrEqualToSuperview().offset(-45)
|
||||
make.top.equalTo(titleBgView.snp.bottom).offset(152)
|
||||
make.height.greaterThanOrEqualTo(36)
|
||||
}
|
||||
|
||||
highlightButton.snp.makeConstraints { make in
|
||||
make.right.left.equalToSuperview().inset(40)
|
||||
make.top.equalTo(textLabel.snp.bottom).offset(18)
|
||||
make.bottom.equalToSuperview().offset(-17)
|
||||
make.height.equalTo(48)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
143
XSeri/Libs/Alert/XSBaseAlert.swift
Normal file
@ -0,0 +1,143 @@
|
||||
//
|
||||
// XSBaseAlert.swift
|
||||
// XSeri
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2026/3/18.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
|
||||
class XSBaseAlert: UIView {
|
||||
|
||||
var clickHighlightButton: (() -> Void)?
|
||||
|
||||
private(set) var containerView: UIView = {
|
||||
let view = UIView()
|
||||
return view
|
||||
}()
|
||||
|
||||
private(set) var contentView: UIView = {
|
||||
let view = UIView()
|
||||
return view
|
||||
}()
|
||||
|
||||
private(set) lazy var closeButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.setImage(UIImage(named: "close_icon_02"), for: .normal)
|
||||
button.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
backgroundColor = .black.withAlphaComponent(0.5)
|
||||
|
||||
|
||||
addSubview(containerView)
|
||||
containerView.addSubview(contentView)
|
||||
containerView.addSubview(closeButton)
|
||||
|
||||
containerView.snp.makeConstraints { make in
|
||||
make.center.equalToSuperview()
|
||||
}
|
||||
|
||||
contentView.snp.makeConstraints { make in
|
||||
make.left.right.top.equalToSuperview()
|
||||
make.bottom.equalToSuperview().offset(-76)
|
||||
}
|
||||
|
||||
closeButton.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.bottom.equalToSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@objc func show(in view: UIView? = nil) -> Self {
|
||||
guard self.superview == nil else { return self }
|
||||
|
||||
var inView: UIView
|
||||
if let view = view {
|
||||
inView = view
|
||||
} else {
|
||||
inView = XSBaseAlert.Window.manager.createWindow()
|
||||
}
|
||||
|
||||
inView.addSubview(self)
|
||||
self.frame = inView.bounds
|
||||
showAnimation()
|
||||
|
||||
return self
|
||||
}
|
||||
@objc func dismiss() {
|
||||
dismissAnimation()
|
||||
}
|
||||
|
||||
@objc func handleHighlightButton() {
|
||||
self.dismissAnimation()
|
||||
self.clickHighlightButton?()
|
||||
}
|
||||
}
|
||||
|
||||
extension XSBaseAlert {
|
||||
private func showAnimation() {
|
||||
containerView.transform = CGAffineTransform(translationX: 0, y: 200)
|
||||
|
||||
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0) {
|
||||
self.containerView.transform = CGAffineTransform.identity
|
||||
}
|
||||
}
|
||||
|
||||
private func dismissAnimation() {
|
||||
|
||||
UIView.animate(withDuration: 0.3) {
|
||||
self.alpha = 0
|
||||
self.containerView.transform = CGAffineTransform(translationX: 0, y: 500)
|
||||
} completion: { _ in
|
||||
self.removeFromSuperview()
|
||||
XSBaseAlert.Window.manager.dismissWindow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension XSBaseAlert {
|
||||
class Window {
|
||||
static let manager = Window()
|
||||
|
||||
private(set) var window: UIWindow?
|
||||
|
||||
func createWindow() -> UIWindow {
|
||||
guard let window = window else {
|
||||
let window = UIWindow(windowScene: XSTool.windowScene!)
|
||||
window.backgroundColor = .clear
|
||||
window.windowLevel = .alert
|
||||
window.isHidden = false
|
||||
self.window = window
|
||||
return window
|
||||
}
|
||||
return window
|
||||
}
|
||||
|
||||
func dismissWindow() {
|
||||
guard let window = self.window else { return }
|
||||
|
||||
var isHidden = true
|
||||
|
||||
window.subviews.forEach {
|
||||
if $0.isKind(of: XSBaseAlert.self) {
|
||||
isHidden = false
|
||||
}
|
||||
}
|
||||
if isHidden {
|
||||
window.isHidden = true
|
||||
self.window = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,14 +12,16 @@ class XSDeviceId: NSObject {
|
||||
static let shared = XSDeviceId()
|
||||
private let key = "com.xseri.deviceid"
|
||||
|
||||
let id: String
|
||||
|
||||
lazy var id: String = {
|
||||
private override init() {
|
||||
if let savedID = XSKeychain.shared.read(key: key) {
|
||||
return savedID
|
||||
self.id = savedID
|
||||
} else {
|
||||
let newID = UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString
|
||||
XSKeychain.shared.save(key: key, value: newID)
|
||||
return newID
|
||||
self.id = newID
|
||||
}
|
||||
}()
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x1A",
|
||||
"green" : "0x18",
|
||||
"red" : "0x00"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0x66",
|
||||
"red" : "0x08"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x01",
|
||||
"green" : "0x07",
|
||||
"red" : "0x0C"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x0E",
|
||||
"green" : "0x11",
|
||||
"red" : "0x16"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x33",
|
||||
"green" : "0x33",
|
||||
"red" : "0x33"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x37",
|
||||
"green" : "0x45",
|
||||
"red" : "0x57"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
22
XSeri/Source/Assets.xcassets/Image/icon/alert_bg_border_color_image.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Rectangle 346272052@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Rectangle 346272052@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 12 KiB |
22
XSeri/Source/Assets.xcassets/Image/icon/apple_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Vector@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Vector@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
XSeri/Source/Assets.xcassets/Image/icon/apple_icon_01.imageset/Vector@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 722 B |
BIN
XSeri/Source/Assets.xcassets/Image/icon/apple_icon_01.imageset/Vector@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 1020 B |
22
XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_02.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "按钮@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "按钮@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_02.imageset/按钮@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_02.imageset/按钮@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 11 KiB |
22
XSeri/Source/Assets.xcassets/Image/icon/calendar_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "签到板+飞舞的金币@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "签到板+飞舞的金币@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
XSeri/Source/Assets.xcassets/Image/icon/calendar_icon_01.imageset/签到板+飞舞的金币@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
XSeri/Source/Assets.xcassets/Image/icon/calendar_icon_01.imageset/签到板+飞舞的金币@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 53 KiB |
22
XSeri/Source/Assets.xcassets/Image/icon/close_icon_02.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame 126@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame 126@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
XSeri/Source/Assets.xcassets/Image/icon/close_icon_02.imageset/Frame 126@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
XSeri/Source/Assets.xcassets/Image/icon/close_icon_02.imageset/Frame 126@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
22
XSeri/Source/Assets.xcassets/Image/icon/coins_icon_03.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "新APP金币@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "新APP金币@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
XSeri/Source/Assets.xcassets/Image/icon/coins_icon_03.imageset/新APP金币@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
XSeri/Source/Assets.xcassets/Image/icon/coins_icon_03.imageset/新APP金币@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
22
XSeri/Source/Assets.xcassets/Image/icon/facebook_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Vector@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Vector@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
XSeri/Source/Assets.xcassets/Image/icon/facebook_icon_01.imageset/Vector@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 609 B |
BIN
XSeri/Source/Assets.xcassets/Image/icon/facebook_icon_01.imageset/Vector@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 830 B |
22
XSeri/Source/Assets.xcassets/Image/icon/wallet_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Wallet-three (钱包3)@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Wallet-three (钱包3)@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
XSeri/Source/Assets.xcassets/Image/icon/wallet_icon_01.imageset/Wallet-three (钱包3)@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 696 B |
BIN
XSeri/Source/Assets.xcassets/Image/icon/wallet_icon_01.imageset/Wallet-three (钱包3)@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 853 B |
@ -59,6 +59,10 @@
|
||||
"Expires in ## days" = "Expires in ## days";
|
||||
"Coin Record" = "Coin Record";
|
||||
"VIP Record" = "VIP Record";
|
||||
"Order Records" = "Order Records";
|
||||
"VIP Membership" = "VIP Membership";
|
||||
"Auto renew,cancel anytime" = "Auto renew,cancel anytime";
|
||||
"Daily Coins" = "Daily Coins";
|
||||
|
||||
"me_daily_1" = "Daily reward ready!";
|
||||
"me_daily_2" = "Claim your rewards now.";
|
||||
|
||||