This commit is contained in:
zeng 2025-10-23 15:53:39 +08:00
parent ece8f68003
commit 99d75582ac
113 changed files with 3852 additions and 154 deletions

View File

@ -16,6 +16,23 @@
F342969A2EA0F8E700A58F99 /* FAGenresListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296992EA0F8E700A58F99 /* FAGenresListViewController.swift */; };
F342969F2EA0FA2200A58F99 /* FAGenresListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F342969E2EA0FA2200A58F99 /* FAGenresListCell.xib */; };
F34296A02EA0FA2200A58F99 /* FAGenresListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F342969D2EA0FA2200A58F99 /* FAGenresListCell.swift */; };
F34296A82EA5D2C600A58F99 /* FACategoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296A72EA5D2C600A58F99 /* FACategoryModel.swift */; };
F34296AA2EA5DF1600A58F99 /* FAPopularListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296A92EA5DF1600A58F99 /* FAPopularListViewController.swift */; };
F34296AC2EA5E11A00A58F99 /* FANewListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296AB2EA5E11A00A58F99 /* FANewListViewController.swift */; };
F34296AE2EA5E4CE00A58F99 /* FAPopularModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296AD2EA5E4CE00A58F99 /* FAPopularModel.swift */; };
F34296B22EA604D400A58F99 /* FARankingListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296B12EA604D400A58F99 /* FARankingListViewController.swift */; };
F34296B42EA605E800A58F99 /* FARankingListHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296B32EA605E800A58F99 /* FARankingListHeaderView.swift */; };
F34296B62EA60C8E00A58F99 /* FAGradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296B52EA60C8E00A58F99 /* FAGradientView.swift */; };
F34296B92EA61C9100A58F99 /* FARankingListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F34296B82EA61C9100A58F99 /* FARankingListCell.xib */; };
F34296BA2EA61C9100A58F99 /* FARankingListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296B72EA61C9100A58F99 /* FARankingListCell.swift */; };
F34296BC2EA62C1A00A58F99 /* FAVideoLockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296BB2EA62C1A00A58F99 /* FAVideoLockView.swift */; };
F34296BE2EA765CA00A58F99 /* FAGradientButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296BD2EA765CA00A58F99 /* FAGradientButton.swift */; };
F34296C02EA7742900A58F99 /* FAVideoUnlockResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296BF2EA7742900A58F99 /* FAVideoUnlockResult.swift */; };
F34296D52EA8B70100A58F99 /* FAMeCoinsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296D42EA8B70100A58F99 /* FAMeCoinsView.swift */; };
F34296D92EA8BD0B00A58F99 /* FAMeTableViewHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296D82EA8BD0B00A58F99 /* FAMeTableViewHeaderView.swift */; };
F34296DB2EA8CA2B00A58F99 /* Date+FAAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296DA2EA8CA2400A58F99 /* Date+FAAdd.swift */; };
F34296DF2EA8D04800A58F99 /* FAStoreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296DE2EA8D04800A58F99 /* FAStoreViewController.swift */; };
F34296E12EA8D85D00A58F99 /* FABaseWebViewController+Script.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34296E02EA8D85400A58F99 /* FABaseWebViewController+Script.swift */; };
F37103312E978F8C00E7F171 /* FACollectViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F37103302E978F8C00E7F171 /* FACollectViewController.swift */; };
F37103352E97929F00E7F171 /* FACollectCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F37103342E97929F00E7F171 /* FACollectCell.xib */; };
F37103362E97929F00E7F171 /* FACollectCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F37103332E97929F00E7F171 /* FACollectCell.swift */; };
@ -127,6 +144,23 @@
F3DCC08B2E8BB16F00D58007 /* FARecommendViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3DCC08A2E8BB16F00D58007 /* FARecommendViewController.swift */; };
F3DCC08E2E8BB1F100D58007 /* FARecommendViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3DCC08D2E8BB1F100D58007 /* FARecommendViewModel.swift */; };
F3DCC0912E8BBB7600D58007 /* FARecommendPlayerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3DCC0902E8BBB7600D58007 /* FARecommendPlayerCell.swift */; };
F3EE96562EA8DFBD00A9306D /* FAWebMessageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EE96552EA8DFBD00A9306D /* FAWebMessageModel.swift */; };
F3EE96582EA8E1A000A9306D /* FAWalletViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EE96572EA8E1A000A9306D /* FAWalletViewController.swift */; };
F3EE96602EA8E39200A9306D /* FAWalletCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EE965E2EA8E39200A9306D /* FAWalletCell.swift */; };
F3EE96612EA8E39200A9306D /* FAWalletCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F3EE965F2EA8E39200A9306D /* FAWalletCell.xib */; };
F3EE96632EA8E61200A9306D /* FAWalletHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EE96622EA8E61200A9306D /* FAWalletHeaderView.swift */; };
F3EE96652EA9C63D00A9306D /* FAConsumptionRecordsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EE96642EA9C63D00A9306D /* FAConsumptionRecordsViewController.swift */; };
F3EE966A2EA9C72600A9306D /* FAConsumptionRecordsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EE96682EA9C72600A9306D /* FAConsumptionRecordsCell.swift */; };
F3EE966B2EA9C72600A9306D /* FAConsumptionRecordsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F3EE96692EA9C72600A9306D /* FAConsumptionRecordsCell.xib */; };
F3EE966D2EA9D29600A9306D /* FAOrderRecordsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EE966C2EA9D29600A9306D /* FAOrderRecordsViewController.swift */; };
F3EE966F2EA9F94E00A9306D /* FACoinRecordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EE966E2EA9F94E00A9306D /* FACoinRecordViewController.swift */; };
F3EE96712EA9F96E00A9306D /* FAVipRecordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EE96702EA9F96D00A9306D /* FAVipRecordViewController.swift */; };
F3EE96742EA9FA5D00A9306D /* FAOrderRecordCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F3EE96732EA9FA5C00A9306D /* FAOrderRecordCell.xib */; };
F3EE96752EA9FA5D00A9306D /* FAOrderRecordCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EE96722EA9FA5C00A9306D /* FAOrderRecordCell.swift */; };
F3EE96772EA9FD5F00A9306D /* FARewardCoinsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EE96762EA9FD5F00A9306D /* FARewardCoinsViewController.swift */; };
F3EE967A2EA9FE0800A9306D /* FARewardCoinsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F3EE96792EA9FE0800A9306D /* FARewardCoinsCell.xib */; };
F3EE967B2EA9FE0800A9306D /* FARewardCoinsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EE96782EA9FE0800A9306D /* FARewardCoinsCell.swift */; };
F3EE967D2EAA07F200A9306D /* FAMeListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EE967C2EAA07F200A9306D /* FAMeListViewController.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -141,6 +175,23 @@
F34296992EA0F8E700A58F99 /* FAGenresListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAGenresListViewController.swift; sourceTree = "<group>"; };
F342969D2EA0FA2200A58F99 /* FAGenresListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAGenresListCell.swift; sourceTree = "<group>"; };
F342969E2EA0FA2200A58F99 /* FAGenresListCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FAGenresListCell.xib; sourceTree = "<group>"; };
F34296A72EA5D2C600A58F99 /* FACategoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACategoryModel.swift; sourceTree = "<group>"; };
F34296A92EA5DF1600A58F99 /* FAPopularListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAPopularListViewController.swift; sourceTree = "<group>"; };
F34296AB2EA5E11A00A58F99 /* FANewListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FANewListViewController.swift; sourceTree = "<group>"; };
F34296AD2EA5E4CE00A58F99 /* FAPopularModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAPopularModel.swift; sourceTree = "<group>"; };
F34296B12EA604D400A58F99 /* FARankingListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FARankingListViewController.swift; sourceTree = "<group>"; };
F34296B32EA605E800A58F99 /* FARankingListHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FARankingListHeaderView.swift; sourceTree = "<group>"; };
F34296B52EA60C8E00A58F99 /* FAGradientView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAGradientView.swift; sourceTree = "<group>"; };
F34296B72EA61C9100A58F99 /* FARankingListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FARankingListCell.swift; sourceTree = "<group>"; };
F34296B82EA61C9100A58F99 /* FARankingListCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FARankingListCell.xib; sourceTree = "<group>"; };
F34296BB2EA62C1A00A58F99 /* FAVideoLockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAVideoLockView.swift; sourceTree = "<group>"; };
F34296BD2EA765CA00A58F99 /* FAGradientButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAGradientButton.swift; sourceTree = "<group>"; };
F34296BF2EA7742900A58F99 /* FAVideoUnlockResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAVideoUnlockResult.swift; sourceTree = "<group>"; };
F34296D42EA8B70100A58F99 /* FAMeCoinsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAMeCoinsView.swift; sourceTree = "<group>"; };
F34296D82EA8BD0B00A58F99 /* FAMeTableViewHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAMeTableViewHeaderView.swift; sourceTree = "<group>"; };
F34296DA2EA8CA2400A58F99 /* Date+FAAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+FAAdd.swift"; sourceTree = "<group>"; };
F34296DE2EA8D04800A58F99 /* FAStoreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreViewController.swift; sourceTree = "<group>"; };
F34296E02EA8D85400A58F99 /* FABaseWebViewController+Script.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FABaseWebViewController+Script.swift"; sourceTree = "<group>"; };
F37103302E978F8C00E7F171 /* FACollectViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACollectViewController.swift; sourceTree = "<group>"; };
F37103332E97929F00E7F171 /* FACollectCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACollectCell.swift; sourceTree = "<group>"; };
F37103342E97929F00E7F171 /* FACollectCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FACollectCell.xib; sourceTree = "<group>"; };
@ -255,6 +306,23 @@
F3DCC08A2E8BB16F00D58007 /* FARecommendViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FARecommendViewController.swift; sourceTree = "<group>"; };
F3DCC08D2E8BB1F100D58007 /* FARecommendViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FARecommendViewModel.swift; sourceTree = "<group>"; };
F3DCC0902E8BBB7600D58007 /* FARecommendPlayerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FARecommendPlayerCell.swift; sourceTree = "<group>"; };
F3EE96552EA8DFBD00A9306D /* FAWebMessageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAWebMessageModel.swift; sourceTree = "<group>"; };
F3EE96572EA8E1A000A9306D /* FAWalletViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAWalletViewController.swift; sourceTree = "<group>"; };
F3EE965E2EA8E39200A9306D /* FAWalletCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAWalletCell.swift; sourceTree = "<group>"; };
F3EE965F2EA8E39200A9306D /* FAWalletCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FAWalletCell.xib; sourceTree = "<group>"; };
F3EE96622EA8E61200A9306D /* FAWalletHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAWalletHeaderView.swift; sourceTree = "<group>"; };
F3EE96642EA9C63D00A9306D /* FAConsumptionRecordsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAConsumptionRecordsViewController.swift; sourceTree = "<group>"; };
F3EE96682EA9C72600A9306D /* FAConsumptionRecordsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAConsumptionRecordsCell.swift; sourceTree = "<group>"; };
F3EE96692EA9C72600A9306D /* FAConsumptionRecordsCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FAConsumptionRecordsCell.xib; sourceTree = "<group>"; };
F3EE966C2EA9D29600A9306D /* FAOrderRecordsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAOrderRecordsViewController.swift; sourceTree = "<group>"; };
F3EE966E2EA9F94E00A9306D /* FACoinRecordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinRecordViewController.swift; sourceTree = "<group>"; };
F3EE96702EA9F96D00A9306D /* FAVipRecordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAVipRecordViewController.swift; sourceTree = "<group>"; };
F3EE96722EA9FA5C00A9306D /* FAOrderRecordCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAOrderRecordCell.swift; sourceTree = "<group>"; };
F3EE96732EA9FA5C00A9306D /* FAOrderRecordCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FAOrderRecordCell.xib; sourceTree = "<group>"; };
F3EE96762EA9FD5F00A9306D /* FARewardCoinsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FARewardCoinsViewController.swift; sourceTree = "<group>"; };
F3EE96782EA9FE0800A9306D /* FARewardCoinsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FARewardCoinsCell.swift; sourceTree = "<group>"; };
F3EE96792EA9FE0800A9306D /* FARewardCoinsCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FARewardCoinsCell.xib; sourceTree = "<group>"; };
F3EE967C2EAA07F200A9306D /* FAMeListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAMeListViewController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -286,6 +354,29 @@
path = Pods;
sourceTree = "<group>";
};
F34296DC2EA8D02500A58F99 /* Store */ = {
isa = PBXGroup;
children = (
F34296DD2EA8D03200A58F99 /* C */,
F3EE965D2EA8E35A00A9306D /* V */,
);
path = Store;
sourceTree = "<group>";
};
F34296DD2EA8D03200A58F99 /* C */ = {
isa = PBXGroup;
children = (
F34296DE2EA8D04800A58F99 /* FAStoreViewController.swift */,
F3EE96572EA8E1A000A9306D /* FAWalletViewController.swift */,
F3EE96642EA9C63D00A9306D /* FAConsumptionRecordsViewController.swift */,
F3EE966C2EA9D29600A9306D /* FAOrderRecordsViewController.swift */,
F3EE966E2EA9F94E00A9306D /* FACoinRecordViewController.swift */,
F3EE96702EA9F96D00A9306D /* FAVipRecordViewController.swift */,
F3EE96762EA9FD5F00A9306D /* FARewardCoinsViewController.swift */,
);
path = C;
sourceTree = "<group>";
};
F371032E2E978F4D00E7F171 /* MyShort */ = {
isa = PBXGroup;
children = (
@ -337,6 +428,8 @@
F371037D2EA082C700E7F171 /* FAWebView.swift */,
F371037F2EA0839500E7F171 /* FABaseWebViewController.swift */,
F37103812EA0868100E7F171 /* FAAppWebViewController.swift */,
F34296E02EA8D85400A58F99 /* FABaseWebViewController+Script.swift */,
F3EE96552EA8DFBD00A9306D /* FAWebMessageModel.swift */,
);
path = WebView;
sourceTree = "<group>";
@ -428,6 +521,7 @@
F37103482E9DD97200E7F171 /* UIScrollView+FARefresh.swift */,
F371036A2E9E52FE00E7F171 /* UIStackView+FAAdd.swift */,
F37103832EA0873400E7F171 /* Dictionary+FAAdd.swift */,
F34296DA2EA8CA2400A58F99 /* Date+FAAdd.swift */,
);
path = Extension;
sourceTree = "<group>";
@ -456,6 +550,7 @@
F3A792C92E77F70C0097E0BC /* FAShortDetailModel.swift */,
F3A792CA2E77F70C0097E0BC /* FAShortPlayModel.swift */,
F3A792CB2E77F70C0097E0BC /* FAVideoInfoModel.swift */,
F34296BF2EA7742900A58F99 /* FAVideoUnlockResult.swift */,
);
path = M;
sourceTree = "<group>";
@ -478,6 +573,7 @@
F3DCC0462E8A65B000D58007 /* FAEpSelectorCell.swift */,
F3DCC0472E8A65B000D58007 /* FAEpSelectorCell.xib */,
F3DCC04A2E8A6EAE00D58007 /* FAEpMenuView.swift */,
F34296BB2EA62C1A00A58F99 /* FAVideoLockView.swift */,
);
path = V;
sourceTree = "<group>";
@ -544,6 +640,8 @@
children = (
F3A792E72E77F8580097E0BC /* FAHomeModuleItem.swift */,
F3A793112E78F8970097E0BC /* FAHomeItem.swift */,
F34296A72EA5D2C600A58F99 /* FACategoryModel.swift */,
F34296AD2EA5E4CE00A58F99 /* FAPopularModel.swift */,
);
path = M;
sourceTree = "<group>";
@ -575,6 +673,9 @@
F34296962EA0EAAA00A58F99 /* FAGenresCell.xib */,
F342969D2EA0FA2200A58F99 /* FAGenresListCell.swift */,
F342969E2EA0FA2200A58F99 /* FAGenresListCell.xib */,
F34296B32EA605E800A58F99 /* FARankingListHeaderView.swift */,
F34296B72EA61C9100A58F99 /* FARankingListCell.swift */,
F34296B82EA61C9100A58F99 /* FARankingListCell.xib */,
);
path = V;
sourceTree = "<group>";
@ -586,6 +687,9 @@
F371034C2E9DF9FB00E7F171 /* FASearchViewController.swift */,
F34296932EA0E8CB00A58F99 /* FAGenresViewController.swift */,
F34296992EA0F8E700A58F99 /* FAGenresListViewController.swift */,
F34296A92EA5DF1600A58F99 /* FAPopularListViewController.swift */,
F34296AB2EA5E11A00A58F99 /* FANewListViewController.swift */,
F34296B12EA604D400A58F99 /* FARankingListViewController.swift */,
);
path = C;
sourceTree = "<group>";
@ -636,6 +740,8 @@
F3A793172E790D440097E0BC /* FAImageView.swift */,
F3A798BD2E82ACD10097E0BC /* FAPanModalContentView.swift */,
F3DCC04C2E8A6F0D00D58007 /* FAScrollView.swift */,
F34296B52EA60C8E00A58F99 /* FAGradientView.swift */,
F34296BD2EA765CA00A58F99 /* FAGradientButton.swift */,
);
path = View;
sourceTree = "<group>";
@ -670,6 +776,7 @@
F3C9AE582E77DD8C00E25109 /* Class */ = {
isa = PBXGroup;
children = (
F34296DC2EA8D02500A58F99 /* Store */,
F3A792E02E77F8040097E0BC /* Home */,
F3DCC0532E8A8EB200D58007 /* Me */,
F3A792D52E77F70C0097E0BC /* Player */,
@ -721,6 +828,7 @@
isa = PBXGroup;
children = (
F3DCC0562E8A8EE800D58007 /* FAMeViewController.swift */,
F3EE967C2EAA07F200A9306D /* FAMeListViewController.swift */,
F37103722E9F9E0D00E7F171 /* FAAboutViewController.swift */,
F371037A2EA0820C00E7F171 /* FASettingViewController.swift */,
F37103852EA087FB00E7F171 /* FAFeedbackViewController.swift */,
@ -732,6 +840,8 @@
isa = PBXGroup;
children = (
F3DCC0592E8A931900D58007 /* FAMeHeaderView.swift */,
F34296D42EA8B70100A58F99 /* FAMeCoinsView.swift */,
F34296D82EA8BD0B00A58F99 /* FAMeTableViewHeaderView.swift */,
F3DCC0612E8A9E7600D58007 /* FAMeCell.swift */,
F3DCC0622E8A9E7600D58007 /* FAMeCell.xib */,
F37103742E9FA15B00E7F171 /* FAAboutCell.swift */,
@ -784,6 +894,22 @@
path = V;
sourceTree = "<group>";
};
F3EE965D2EA8E35A00A9306D /* V */ = {
isa = PBXGroup;
children = (
F3EE965E2EA8E39200A9306D /* FAWalletCell.swift */,
F3EE965F2EA8E39200A9306D /* FAWalletCell.xib */,
F3EE96622EA8E61200A9306D /* FAWalletHeaderView.swift */,
F3EE96682EA9C72600A9306D /* FAConsumptionRecordsCell.swift */,
F3EE96692EA9C72600A9306D /* FAConsumptionRecordsCell.xib */,
F3EE96722EA9FA5C00A9306D /* FAOrderRecordCell.swift */,
F3EE96732EA9FA5C00A9306D /* FAOrderRecordCell.xib */,
F3EE96782EA9FE0800A9306D /* FARewardCoinsCell.swift */,
F3EE96792EA9FE0800A9306D /* FARewardCoinsCell.xib */,
);
path = V;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -847,19 +973,24 @@
buildActionMask = 2147483647;
files = (
F34296982EA0EAAA00A58F99 /* FAGenresCell.xib in Resources */,
F3EE966B2EA9C72600A9306D /* FAConsumptionRecordsCell.xib in Resources */,
F3A798B72E828C180097E0BC /* Localizable.strings in Resources */,
F37103572E9E1FA500E7F171 /* FASearchRecordCell.xib in Resources */,
F34296B92EA61C9100A58F99 /* FARankingListCell.xib in Resources */,
F342969F2EA0FA2200A58F99 /* FAGenresListCell.xib in Resources */,
F39E663A2E77BAD0008AAAFA /* Assets.xcassets in Resources */,
F3EE96612EA8E39200A9306D /* FAWalletCell.xib in Resources */,
F37103772E9FA15B00E7F171 /* FAAboutCell.xib in Resources */,
F371035E2E9E2E7400E7F171 /* FASearchRecommendCell.xib in Resources */,
F37103462E9CF9EE00E7F171 /* FAHistoryCell.xib in Resources */,
F3A793292E795B4C0097E0BC /* FAHomeSectionTitleView.xib in Resources */,
F3A793232E7944FF0097E0BC /* FAHomeRecommendedCell.xib in Resources */,
F37103662E9E3ABC00E7F171 /* FASearchResultCell.xib in Resources */,
F3EE967A2EA9FE0800A9306D /* FARewardCoinsCell.xib in Resources */,
F3DCC0482E8A65B000D58007 /* FAEpSelectorCell.xib in Resources */,
F39E663C2E77BAD0008AAAFA /* LaunchScreen.storyboard in Resources */,
F3DCC0642E8A9E7600D58007 /* FAMeCell.xib in Resources */,
F3EE96742EA9FA5D00A9306D /* FAOrderRecordCell.xib in Resources */,
F37103352E97929F00E7F171 /* FACollectCell.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -924,13 +1055,20 @@
F38C25FD2E86A290008C22C3 /* FADefine.swift in Sources */,
F3A793182E790D440097E0BC /* FAImageView.swift in Sources */,
F3DCC05A2E8A931900D58007 /* FAMeHeaderView.swift in Sources */,
F34296AC2EA5E11A00A58F99 /* FANewListViewController.swift in Sources */,
F3A792B62E77E0CC0097E0BC /* FANetworkManager.swift in Sources */,
F3EE966A2EA9C72600A9306D /* FAConsumptionRecordsCell.swift in Sources */,
F3A792C62E77E93E0097E0BC /* FANavigationController.swift in Sources */,
F3EE966D2EA9D29600A9306D /* FAOrderRecordsViewController.swift in Sources */,
F34296BE2EA765CA00A58F99 /* FAGradientButton.swift in Sources */,
F34296A82EA5D2C600A58F99 /* FACategoryModel.swift in Sources */,
F37103692E9E44A000E7F171 /* FASearchViewModel.swift in Sources */,
F3A793122E78F8970097E0BC /* FAHomeItem.swift in Sources */,
F3A798BE2E82ACD10097E0BC /* FAPanModalContentView.swift in Sources */,
F3A792F22E77F8A80097E0BC /* FAHomeMustSeeView.swift in Sources */,
F3A793242E7944FF0097E0BC /* FAHomeRecommendedCell.swift in Sources */,
F34296B22EA604D400A58F99 /* FARankingListViewController.swift in Sources */,
F34296B42EA605E800A58F99 /* FARankingListHeaderView.swift in Sources */,
F3A792F42E77F8A80097E0BC /* FAHomeMustSeeContentView.swift in Sources */,
F3DCC05E2E8A9C5800D58007 /* FATableViewCell.swift in Sources */,
F371037E2EA082CD00E7F171 /* FAWebView.swift in Sources */,
@ -938,16 +1076,19 @@
F37103612E9E379E00E7F171 /* FASearchResultView.swift in Sources */,
F3A792F52E77F8A80097E0BC /* FAHomeRecommendedItemView.swift in Sources */,
F34296972EA0EAAA00A58F99 /* FAGenresCell.swift in Sources */,
F34296D92EA8BD0B00A58F99 /* FAMeTableViewHeaderView.swift in Sources */,
F37103842EA0873B00E7F171 /* Dictionary+FAAdd.swift in Sources */,
F3A792F72E77F8A80097E0BC /* FAHomeNewView.swift in Sources */,
F3A792F82E77F8A80097E0BC /* FAHomeMustSeeShortView.swift in Sources */,
F3A793042E77FB110097E0BC /* FAHomeViewController.swift in Sources */,
F34296DF2EA8D04800A58F99 /* FAStoreViewController.swift in Sources */,
F371034F2E9DFB2000E7F171 /* FASearchInputView.swift in Sources */,
F37103562E9E1FA500E7F171 /* FASearchRecordCell.swift in Sources */,
F37103792E9FA91C00E7F171 /* FAAboutHeaderView.swift in Sources */,
F3A792B12E77DFEE0097E0BC /* FALogin.swift in Sources */,
F3DCC0502E8A861300D58007 /* FAHUD.swift in Sources */,
F3DCC04D2E8A6F0D00D58007 /* FAScrollView.swift in Sources */,
F3EE967D2EAA07F200A9306D /* FAMeListViewController.swift in Sources */,
F3DCC0572E8A8EE800D58007 /* FAMeViewController.swift in Sources */,
F3A792E82E77F8590097E0BC /* FAHomeModuleItem.swift in Sources */,
F3DCC0452E89530200D58007 /* CGMutablePath+FARoundedCorner.swift in Sources */,
@ -962,12 +1103,14 @@
F3C9AE5B2E77DDE100E25109 /* FAKeychainHelper.swift in Sources */,
F371034D2E9DF9FB00E7F171 /* FASearchViewController.swift in Sources */,
F3A792B82E77E0EF0097E0BC /* FACryptorService.swift in Sources */,
F3EE967B2EA9FE0800A9306D /* FARewardCoinsCell.swift in Sources */,
F3A7932C2E796EF80097E0BC /* FAPlayerDetailControlView.swift in Sources */,
F3A798BC2E82AB6F0097E0BC /* FAEpSelectorView.swift in Sources */,
F37103512E9E1D7800E7F171 /* FASearchHomeView.swift in Sources */,
F34296942EA0E8CB00A58F99 /* FAGenresViewController.swift in Sources */,
F3A792AF2E77DFB90097E0BC /* FATokenModel.swift in Sources */,
F38C25FA2E86A217008C22C3 /* UIView+FAAdd.swift in Sources */,
F3EE96652EA9C63D00A9306D /* FAConsumptionRecordsViewController.swift in Sources */,
F342969A2EA0F8E700A58F99 /* FAGenresListViewController.swift in Sources */,
F3A7931A2E7911420097E0BC /* FAPagerViewTransformer.swift in Sources */,
F3DCC0672E8AA14A00D58007 /* FAMeItemModel.swift in Sources */,
@ -978,26 +1121,38 @@
F37103762E9FA15B00E7F171 /* FAAboutCell.swift in Sources */,
F37103412E97C20C00E7F171 /* UINavigationBar+FAAdd.swift in Sources */,
F3A792D82E77F70C0097E0BC /* FAShortDetailViewModel.swift in Sources */,
F3EE96562EA8DFBD00A9306D /* FAWebMessageModel.swift in Sources */,
F37103802EA0839500E7F171 /* FABaseWebViewController.swift in Sources */,
F3A793002E77FA0C0097E0BC /* SwiftUIExtension.swift in Sources */,
F3A792D92E77F70C0097E0BC /* FAShortDetailModel.swift in Sources */,
F34296C02EA7742900A58F99 /* FAVideoUnlockResult.swift in Sources */,
F3A792DA2E77F70C0097E0BC /* FAPlayerDetailCell.swift in Sources */,
F3A792DB2E77F70C0097E0BC /* FAVideoInfoModel.swift in Sources */,
F3A7930C2E7813FE0097E0BC /* FACollectionView.swift in Sources */,
F37103822EA0868100E7F171 /* FAAppWebViewController.swift in Sources */,
F37103712E9F964000E7F171 /* FALocalized.swift in Sources */,
F34296DB2EA8CA2B00A58F99 /* Date+FAAdd.swift in Sources */,
F3EE966F2EA9F94E00A9306D /* FACoinRecordViewController.swift in Sources */,
F3EE96632EA8E61200A9306D /* FAWalletHeaderView.swift in Sources */,
F34296E12EA8D85D00A58F99 /* FABaseWebViewController+Script.swift in Sources */,
F3A792DC2E77F70C0097E0BC /* FAPlayerDetailViewController.swift in Sources */,
F38C25FF2E86B663008C22C3 /* AppDelegate+FAConfig.swift in Sources */,
F37103592E9E281C00E7F171 /* FASearchRecommendView.swift in Sources */,
F34296D52EA8B70100A58F99 /* FAMeCoinsView.swift in Sources */,
F34296A02EA0FA2200A58F99 /* FAGenresListCell.swift in Sources */,
F3A792BF2E77E4B80097E0BC /* FATool.swift in Sources */,
F371033F2E97BDF800E7F171 /* FAHistoryViewController.swift in Sources */,
F3EE96752EA9FA5D00A9306D /* FAOrderRecordCell.swift in Sources */,
F34296BA2EA61C9100A58F99 /* FARankingListCell.swift in Sources */,
F3DCC08B2E8BB16F00D58007 /* FARecommendViewController.swift in Sources */,
F3DCC0492E8A65B000D58007 /* FAEpSelectorCell.swift in Sources */,
F3A793162E790CC90097E0BC /* FAHomeBannerCell.swift in Sources */,
F3EE96602EA8E39200A9306D /* FAWalletCell.swift in Sources */,
F3EE96772EA9FD5F00A9306D /* FARewardCoinsViewController.swift in Sources */,
F3DCC04B2E8A6EAE00D58007 /* FAEpMenuView.swift in Sources */,
F3DCC0632E8A9E7600D58007 /* FAMeCell.swift in Sources */,
F3A793142E78FC4C0097E0BC /* FAHomeBannerContentCell.swift in Sources */,
F3EE96712EA9F96E00A9306D /* FAVipRecordViewController.swift in Sources */,
F39E66372E77BAD0008AAAFA /* AppDelegate.swift in Sources */,
F3DCC08E2E8BB1F100D58007 /* FARecommendViewModel.swift in Sources */,
F371036B2E9E530400E7F171 /* UIStackView+FAAdd.swift in Sources */,
@ -1005,13 +1160,16 @@
F3A792BC2E77E2EE0097E0BC /* String+FAAdd.swift in Sources */,
F3A792AD2E77DF830097E0BC /* FAUserInfo.swift in Sources */,
F3A792DF2E77F7EA0097E0BC /* FAAPI.swift in Sources */,
F34296AA2EA5DF1600A58F99 /* FAPopularListViewController.swift in Sources */,
F3A7932A2E795B4C0097E0BC /* FAHomeSectionTitleView.swift in Sources */,
F3DCC05C2E8A9C1B00D58007 /* FATableView.swift in Sources */,
F37103492E9DD98600E7F171 /* UIScrollView+FARefresh.swift in Sources */,
F3A7931E2E793D000097E0BC /* FAHomeNewContentCell.swift in Sources */,
F34296BC2EA62C1A00A58F99 /* FAVideoLockView.swift in Sources */,
F39E66382E77BAD0008AAAFA /* SceneDelegate.swift in Sources */,
F371036E2E9E6E7800E7F171 /* FAEmpty.swift in Sources */,
F37103362E97929F00E7F171 /* FACollectCell.swift in Sources */,
F3EE96582EA8E1A000A9306D /* FAWalletViewController.swift in Sources */,
F3A793532E7BA54A0097E0BC /* FAPlayerEpUIButton.swift in Sources */,
F3A792EA2E77F8820097E0BC /* FAHomeViewModel.swift in Sources */,
F3A792C82E77EED10097E0BC /* Font+FAAdd.swift in Sources */,
@ -1020,6 +1178,8 @@
F3A798B92E828F5E0097E0BC /* FAPlayerProgressView.swift in Sources */,
F371035F2E9E2E7400E7F171 /* FASearchRecommendCell.swift in Sources */,
F3A793092E7812F60097E0BC /* FAWaterfallFlowLayout.swift in Sources */,
F34296AE2EA5E4CE00A58F99 /* FAPopularModel.swift in Sources */,
F34296B62EA60C8E00A58F99 /* FAGradientView.swift in Sources */,
F37103732E9F9E0D00E7F171 /* FAAboutViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -1060,6 +1220,8 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Fableon/Source/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Fableon;
INFOPLIST_KEY_NSCameraUsageDescription = "The APP needs to access your album to provide screenshots for feedback.";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "The APP needs to access your album to provide screenshots for feedback.";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = "";
@ -1096,6 +1258,8 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Fableon/Source/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Fableon;
INFOPLIST_KEY_NSCameraUsageDescription = "The APP needs to access your album to provide screenshots for feedback.";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "The APP needs to access your album to provide screenshots for feedback.";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = "";

View File

@ -6,8 +6,9 @@
//
import UIKit
import JXSegmentedView
class FAViewController: UIViewController {
class FAViewController: UIViewController, JXSegmentedListContainerViewListDelegate {
lazy var bgView: UIView = {
let view = UIImageView(image: UIImage(named: "背景"))
@ -45,8 +46,13 @@ class FAViewController: UIViewController {
return .lightContent
}
func listView() -> UIView {
return self.view
}
}
extension UIViewController {
func configNavigationBack(_ imageName: String = "Frame 3011") {
let image = UIImage(named: imageName)

View File

@ -0,0 +1,31 @@
//
// Date+FAAdd.swift
// Fableon
//
// Created by 鸿 on 2025/10/22.
//
import Foundation
extension Date {
///
func fa_differenceDay(_ date: Date) -> Int {
let dateComponents = Calendar.current.dateComponents([.day], from: self, to: date)
return dateComponents.day ?? 0
}
func fa_formatString(_ dateFormat: String) -> String {
let formatter = DateFormatter()
formatter.dateFormat = dateFormat
return formatter.string(from: self)
}
///
var br_isToday: Bool {
get {
return Calendar.current.isDateInToday(self)
}
}
}

View File

@ -37,4 +37,13 @@ extension UIScreen {
static var tabBarHeight: CGFloat {
return safeBottom + 49
}
///
static var widthRatio: CGFloat {
return UIScreen.width / 375
}
static func getRatioWidth(size: CGFloat) -> CGFloat {
return self.widthRatio * size
}
}

View File

@ -168,6 +168,87 @@ struct FAAPI {
completer?(response.data?.list)
}
}
static func requestGenresData(completer: ((_ list: [FACategoryModel]?) -> Void)?) {
let parameters = [
"short_play_num" : 5,
]
FANetworkManager.manager.request(FABaseURL + "/categoryListAppendShortPlay",
method: .get,
parameters: parameters,
isToast: true) { (response: FANetworkManager.Response<FANetworkManager.List<FACategoryModel>>) in
completer?(response.data?.list)
}
}
static func requestCategoryVideoList(id: String, page: Int, completer: ((_ listModel: FANetworkManager.List<FAShortPlayModel>?) -> Void)?) {
let parameters: [String : Any] = [
"category_id" : id,
"current_page" : page,
"page_size" : 20
]
FANetworkManager.manager.request(FABaseURL + "/videoList",
method: .get,
parameters: parameters,
isToast: true) { (response: FANetworkManager.Response<FANetworkManager.List<FAShortPlayModel>>) in
completer?(response.data)
}
}
static func requestPopularVideoList( completer: ((_ list: [FAShortPlayModel]?) -> Void)?) {
FANetworkManager.manager.request(FABaseURL + "/homeTop",
method: .post,
parameters: nil,
isToast: true) { (response: FANetworkManager.Response<FAPopularModel>) in
completer?(response.data?.hotData)
}
}
static func requestNewVideoList( completer: ((_ list: [FAShortPlayModel]?) -> Void)?) {
FANetworkManager.manager.request(FABaseURL + "/newShortPlayNoPaginate",
method: .post,
parameters: nil,
isToast: true) { (response: FANetworkManager.Response<FANetworkManager.List<FAShortPlayModel>>) in
completer?(response.data?.list)
}
}
static func requestRankingVideoList( completer: ((_ list: [FAShortPlayModel]?) -> Void)?) {
let parameters = [
"type" : "most_trending"
]
FANetworkManager.manager.request(FABaseURL + "/homeRanking",
method: .post,
parameters: parameters,
isToast: true) { (response: FANetworkManager.Response<FANetworkManager.List<FAShortPlayModel>>) in
completer?(response.data?.list)
}
}
///
static func requestCoinUnlockVideo(shortPlayId: String, videoId: String, completer: ((_ model: FAVideoUnlockResult?) -> Void)?) {
let parameters = [
"short_play_id" : shortPlayId,
"video_id" : videoId,
]
FANetworkManager.manager.request(FABaseURL + "/buy_video",
method: .post,
parameters: parameters,
isLoding: true,
isToast: true) { (response: FANetworkManager.Response<FAVideoUnlockResult>) in
completer?(response.data)
}
}
}

View File

@ -0,0 +1,44 @@
//
// FAGradientButton.swift
// Fableon
//
// Created by 鸿 on 2025/10/21.
//
import UIKit
class FAGradientButton: UIButton {
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
var fa_gradientLayer: CAGradientLayer {
return self.layer as! CAGradientLayer
}
var fa_colors: [CGColor]? {
didSet {
fa_gradientLayer.colors = fa_colors
}
}
var fa_startPoint: CGPoint = .zero {
didSet {
fa_gradientLayer.startPoint = fa_startPoint
}
}
var fa_endPoint: CGPoint = .zero {
didSet {
fa_gradientLayer.endPoint = fa_endPoint
}
}
var fa_locations: [NSNumber] = [] {
didSet {
fa_gradientLayer.locations = fa_locations
}
}
}

View File

@ -0,0 +1,44 @@
//
// FAGradientView.swift
// Fableon
//
// Created by 鸿 on 2025/10/20.
//
import UIKit
class FAGradientView: UIView {
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
var fa_gradientLayer: CAGradientLayer {
return self.layer as! CAGradientLayer
}
var fa_colors: [CGColor]? {
didSet {
fa_gradientLayer.colors = fa_colors
}
}
var fa_startPoint: CGPoint = .zero {
didSet {
fa_gradientLayer.startPoint = fa_startPoint
}
}
var fa_endPoint: CGPoint = .zero {
didSet {
fa_gradientLayer.endPoint = fa_endPoint
}
}
var fa_locations: [NSNumber] = [] {
didSet {
fa_gradientLayer.locations = fa_locations
}
}
}

View File

@ -7,7 +7,10 @@
import UIKit
class FAScrollView: UIScrollView {
class FAScrollView: UIScrollView, UIGestureRecognizerDelegate {
///
var allowSimultaneousRecognition = false
override init(frame: CGRect) {
super.init(frame: frame)
@ -18,4 +21,9 @@ class FAScrollView: UIScrollView {
fatalError("init(coder:) has not been implemented")
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return allowSimultaneousRecognition
}
}

View File

@ -18,8 +18,16 @@ class FAAppWebViewController: FABaseWebViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.autoTitle = false
if webUrl == kFAFeedBackListWebUrl {
self.title = "Feedback History".localized
} else if webUrl == kFAFeedBackHomeWebUrl {
self.title = "Feedback".localized
} else if webUrl == kFAFeedBackDetailWebUrl {
self.title = "Feedback Details".localized
}
// Do any additional setup after loading the view.
}
override func fa_webViewDidFinishLoad(_ webView: FAWebView) {

View File

@ -0,0 +1,104 @@
//
// FABaseWebViewController+Script.swift
// Fableon
//
// Created by 鸿 on 2025/10/22.
//
import UIKit
import WebKit
import ZLPhotoBrowser
import SmartCodable
///APP
let kFAWebMessageAPP = "js2app"
///
let kFAWebMessageOpenFeedbackList = "openFeedbackList"
///
let kFAWebMessageOpenFeedbackDetail = "openFeedbackDetail"
///
let kFAWebMessageOpenPhotoPicker = "openPhotoPicker"
extension FABaseWebViewController {
func fa_webViewUserContentController(didReceive message: WKScriptMessage) {
let name = message.name
let body = message.body
switch name {
case kFAWebMessageOpenFeedbackList:
let vc = FAAppWebViewController()
vc.webUrl = kFAFeedBackListWebUrl
self.navigationController?.pushViewController(vc, animated: true)
case kFAWebMessageOpenFeedbackDetail:
guard let body = body as? [String : Any] else { return }
guard let id = body["id"] as? Int else { return }
let vc = FAAppWebViewController()
vc.id = "\(id)"
vc.webUrl = kFAFeedBackDetailWebUrl
self.navigationController?.pushViewController(vc, animated: true)
case kFAWebMessageOpenPhotoPicker:
openPhotoPicker()
case kFAWebMessageAPP:
guard let body = message.body as? [String : Any] else { return }
guard let model = FAWebMessageModel.deserialize(from: body) else { return }
let type = model.type
let data = model.data
if type == "login" {
// VPLoginManager.manager.openLogin()
} else if type == "open_notify" {
// openNotify()
} else if type == "watch_video" {
let vc = FAPlayerDetailViewController()
vc.shortPlayId = data?.short_play_id
vc.activityId = data?.activity_id
self.navigationController?.pushViewController(vc, animated: true)
} else {
guard let urlStr = data?.link else { return }
guard let url = URL(string: urlStr) else { return }
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}
default:
break
}
}
///
private func openPhotoPicker() {
ZLPhotoConfiguration.default().allowSelectOriginal = false
ZLPhotoConfiguration.default().maxSelectCount = 1
ZLPhotoConfiguration.default().allowEditImage = false
ZLPhotoConfiguration.default().allowSelectVideo = false
ZLPhotoConfiguration.default().allowSelectGif = false
ZLPhotoConfiguration.default().allowTakePhotoInLibrary = false
let picker = ZLPhotoPicker()
picker.selectImageBlock = { [weak self] (results, _) in
guard let self = self else { return }
guard let image = results.first?.image else { return }
guard let imageData = image.jpegData(compressionQuality: 0.8) else { return }
let imageDataStr = imageData.base64EncodedString(options: .endLineWithCarriageReturn)
let js = "uploadConvertImage('\(imageDataStr)')"
self.webView.evaluateJavaScript(js)
}
picker.showPhotoLibrary(sender: self)
}
}

View File

@ -107,6 +107,6 @@ extension FABaseWebViewController: FAWebViewDelegate {
}
func fa_userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
self.fa_webViewUserContentController(didReceive: message)
}
}

View File

@ -0,0 +1,24 @@
//
// FAWebMessageModel.swift
// Fableon
//
// Created by 鸿 on 2025/10/22.
//
import UIKit
import SmartCodable
struct FAWebMessageModel: SmartCodable {
var type: String?
var data: FAWebMessageData?
}
struct FAWebMessageData: SmartCodable {
var activity_id: String?
var short_play_id: String?
var link: String?
}

View File

@ -34,6 +34,10 @@ class FAWebView: WKWebView {
weak var delegate: FAWebViewDelegate?
private(set) var scriptMessageHandlerArray: [String] = [
kFAWebMessageAPP,
kFAWebMessageOpenFeedbackList,
kFAWebMessageOpenFeedbackDetail,
kFAWebMessageOpenPhotoPicker,
]

View File

@ -9,6 +9,10 @@ import UIKit
class FAGenresListViewController: FAViewController {
var model: FACategoryModel?
private var dataArr: [FAShortPlayModel] = []
private var page = 1
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let width = floor((UIScreen.width - 32 - 16) / 3)
@ -28,13 +32,22 @@ class FAGenresListViewController: FAViewController {
collectionView.dataSource = self
collectionView.contentInset = .init(top: 20, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
collectionView.register(UINib(nibName: "FAGenresListCell", bundle: nil), forCellWithReuseIdentifier: "cell")
collectionView.fa_addRefreshHeader(insetTop: collectionView.contentInset.top) { [weak self] in
self?.handleHeaderRefresh(nil)
}
collectionView.fa_addRefreshFooter(insetBottom: 0) { [weak self] in
self?.handleFooterRefresh(nil)
}
return collectionView
}()
override func viewDidLoad() {
super.viewDidLoad()
self.title = model?.category_name
fa_setupLayout()
requestDataArr(page: 1, completer: nil)
}
override func viewWillAppear(_ animated: Bool) {
@ -43,7 +56,17 @@ class FAGenresListViewController: FAViewController {
fa_setNavigationStyle()
}
override func handleHeaderRefresh(_ completer: (() -> Void)?) {
requestDataArr(page: 1) { [weak self] in
self?.collectionView.fa_endHeaderRefreshing()
}
}
override func handleFooterRefresh(_ completer: (() -> Void)?) {
requestDataArr(page: page + 1) { [weak self] in
self?.collectionView.fa_endFooterRefreshing()
}
}
}
@ -64,11 +87,43 @@ extension FAGenresListViewController {
extension FAGenresListViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
return dataArr.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! FAGenresListCell
cell.model = dataArr[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let model = dataArr[indexPath.row]
let vc = FAPlayerDetailViewController()
vc.shortPlayId = model.short_play_id
self.navigationController?.pushViewController(vc, animated: true)
}
}
extension FAGenresListViewController {
private func requestDataArr(page: Int, completer: (() -> Void)?) {
guard let id = self.model?.id else { return }
FAAPI.requestCategoryVideoList(id: id, page: page) { [weak self] listModel in
guard let self = self else { return }
if let list = listModel?.list {
if page == 1 {
self.dataArr.removeAll()
}
self.dataArr += list
self.collectionView.reloadData()
self.page = page
}
completer?()
self.collectionView.fa_updateNoMoreDataState(listModel?.hasNextPage)
}
}
}

View File

@ -18,6 +18,8 @@ class FAGenresViewController: FAViewController {
.FCB_072_0_5
]
private lazy var dataArr: [FACategoryModel] = []
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 10
@ -39,6 +41,8 @@ class FAGenresViewController: FAViewController {
self.title = "Genres".localized
fa_setupLayout()
requestDataArr()
}
override func viewWillAppear(_ animated: Bool) {
@ -68,16 +72,33 @@ extension FAGenresViewController: UICollectionViewDelegate, UICollectionViewData
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! FAGenresCell
cell.bgView.backgroundColor = bgColorArr[indexPath.row % 5]
cell.model = self.dataArr[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
return self.dataArr.count
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = FAGenresListViewController()
vc.model = dataArr[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
}
extension FAGenresViewController {
private func requestDataArr() {
FAAPI.requestGenresData { [weak self] list in
guard let self = self else { return }
guard let list = list else { return }
self.dataArr = list
self.collectionView.reloadData()
}
}
}

View File

@ -0,0 +1,119 @@
//
// FANewListViewController.swift
// Fableon
//
// Created by 鸿 on 2025/10/20.
//
import UIKit
class FANewListViewController: FAViewController {
private var dataArr: [FAShortPlayModel] = []
// private var page = 1
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let width = floor((UIScreen.width - 32 - 16) / 3)
let height = 145 / 109 * width + 41
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 12
layout.minimumInteritemSpacing = 8
layout.itemSize = .init(width: width, height: height)
layout.sectionInset = .init(top: 0, left: 16, bottom: 0, right: 16)
return layout
}()
private lazy var collectionView: FACollectionView = {
let collectionView = FACollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.contentInset = .init(top: 20, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
collectionView.register(UINib(nibName: "FAGenresListCell", bundle: nil), forCellWithReuseIdentifier: "cell")
collectionView.fa_addRefreshHeader(insetTop: collectionView.contentInset.top) { [weak self] in
self?.handleHeaderRefresh(nil)
}
// collectionView.fa_addRefreshFooter(insetBottom: 0) { [weak self] in
// self?.handleFooterRefresh(nil)
// }
return collectionView
}()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "New".localized
fa_setupLayout()
requestDataArr(page: 1, completer: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
fa_setNavigationStyle()
}
override func handleHeaderRefresh(_ completer: (() -> Void)?) {
requestDataArr(page: 1) { [weak self] in
self?.collectionView.fa_endHeaderRefreshing()
}
}
// override func handleFooterRefresh(_ completer: (() -> Void)?) {
// requestDataArr(page: page + 1) { [weak self] in
// self?.collectionView.fa_endFooterRefreshing()
// }
// }
}
extension FANewListViewController {
private func fa_setupLayout() {
view.addSubview(collectionView)
collectionView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(UIScreen.navBarHeight)
}
}
}
//MARK: UICollectionViewDelegate UICollectionViewDataSource
extension FANewListViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataArr.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! FAGenresListCell
cell.model = dataArr[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let model = dataArr[indexPath.row]
let vc = FAPlayerDetailViewController()
vc.shortPlayId = model.short_play_id
self.navigationController?.pushViewController(vc, animated: true)
}
}
extension FANewListViewController {
private func requestDataArr(page: Int, completer: (() -> Void)?) {
FAAPI.requestNewVideoList { [weak self] list in
guard let self = self else { return }
if let list = list {
self.dataArr = list
self.collectionView.reloadData()
}
completer?()
}
}
}

View File

@ -0,0 +1,121 @@
//
// FAPopularListViewController.swift
// Fableon
//
// Created by 鸿 on 2025/10/20.
//
import UIKit
class FAPopularListViewController: FAViewController {
private var dataArr: [FAShortPlayModel] = []
// private var page = 1
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let width = floor((UIScreen.width - 32 - 16) / 3)
let height = 145 / 109 * width + 41
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 12
layout.minimumInteritemSpacing = 8
layout.itemSize = .init(width: width, height: height)
layout.sectionInset = .init(top: 0, left: 16, bottom: 0, right: 16)
return layout
}()
private lazy var collectionView: FACollectionView = {
let collectionView = FACollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.contentInset = .init(top: 20, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
collectionView.register(UINib(nibName: "FAGenresListCell", bundle: nil), forCellWithReuseIdentifier: "cell")
collectionView.fa_addRefreshHeader(insetTop: collectionView.contentInset.top) { [weak self] in
self?.handleHeaderRefresh(nil)
}
// collectionView.fa_addRefreshFooter(insetBottom: 0) { [weak self] in
// self?.handleFooterRefresh(nil)
// }
return collectionView
}()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Popular".localized
fa_setupLayout()
requestDataArr(page: 1, completer: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
fa_setNavigationStyle()
}
override func handleHeaderRefresh(_ completer: (() -> Void)?) {
requestDataArr(page: 1) { [weak self] in
self?.collectionView.fa_endHeaderRefreshing()
}
}
// override func handleFooterRefresh(_ completer: (() -> Void)?) {
// requestDataArr(page: page + 1) { [weak self] in
// self?.collectionView.fa_endFooterRefreshing()
// }
// }
}
extension FAPopularListViewController {
private func fa_setupLayout() {
view.addSubview(collectionView)
collectionView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(UIScreen.navBarHeight)
}
}
}
//MARK: UICollectionViewDelegate UICollectionViewDataSource
extension FAPopularListViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataArr.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! FAGenresListCell
cell.model = dataArr[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let model = dataArr[indexPath.row]
let vc = FAPlayerDetailViewController()
vc.shortPlayId = model.short_play_id
self.navigationController?.pushViewController(vc, animated: true)
}
}
extension FAPopularListViewController {
private func requestDataArr(page: Int, completer: (() -> Void)?) {
FAAPI.requestPopularVideoList { [weak self] list in
guard let self = self else { return }
if let list = list {
self.dataArr = list
self.collectionView.reloadData()
}
completer?()
}
}
}

View File

@ -0,0 +1,128 @@
//
// FARankingListViewController.swift
// Fableon
//
// Created by 鸿 on 2025/10/20.
//
import UIKit
class FARankingListViewController: FAViewController {
private lazy var dataArr: [FAShortPlayModel] = []
private lazy var headerView: FARankingListHeaderView = {
let view = FARankingListHeaderView()
return view
}()
private lazy var contentView: UIView = {
let view = UIView()
view.fa_setRoundedCorner(topLeft: 27, topRight: 27, bottomLeft: 0, bottomRight: 0)
view.backgroundColor = ._5_CA_8_FF_0_2
return view
}()
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.itemSize = .init(width: UIScreen.width - 32, height: 70)
layout.minimumLineSpacing = 15
return layout
}()
private lazy var collectionView: FACollectionView = {
let collectionView = FACollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.showsVerticalScrollIndicator = false
collectionView.showsHorizontalScrollIndicator = false
collectionView.contentInset = .init(top: 15, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
collectionView.register(UINib(nibName: "FARankingListCell", bundle: nil), forCellWithReuseIdentifier: "cell")
return collectionView
}()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Rankings".localized
fa_setupLayout()
requestDataArr(completer: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
fa_setNavigationStyle()
}
}
extension FARankingListViewController {
private func fa_setupLayout() {
view.addSubview(headerView)
view.addSubview(contentView)
contentView.addSubview(collectionView)
headerView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalToSuperview().offset(UIScreen.navBarHeight)
}
contentView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerX.equalToSuperview()
make.top.equalTo(headerView.snp.bottom)
make.bottom.equalToSuperview()
}
collectionView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
}
//MARK: UICollectionViewDelegate UICollectionViewDataSource
extension FARankingListViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! FARankingListCell
cell.model = dataArr[indexPath.row + 3]
cell.number = indexPath.row + 4
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let count = self.dataArr.count - 3
if count < 0 {
return 0
}
return count
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let model = dataArr[indexPath.row + 3]
let vc = FAPlayerDetailViewController()
vc.shortPlayId = model.short_play_id
self.navigationController?.pushViewController(vc, animated: true)
}
}
extension FARankingListViewController {
private func requestDataArr(completer: (() -> Void)?) {
FAAPI.requestRankingVideoList { [weak self] list in
guard let self = self else { return }
if let list = list {
self.dataArr = list
self.headerView.dataArr = list
self.collectionView.reloadData()
}
completer?()
}
}
}

View File

@ -0,0 +1,16 @@
//
// FACategoryModel.swift
// Fableon
//
// Created by 鸿 on 2025/10/20.
//
import UIKit
import SmartCodable
struct FACategoryModel: SmartCodable {
var id: String?
var category_name: String?
var short_play_list: [FAShortPlayModel]?
}

View File

@ -0,0 +1,15 @@
//
// FAPopularModel.swift
// Fableon
//
// Created by 鸿 on 2025/10/20.
//
import UIKit
import SmartCodable
struct FAPopularModel: SmartCodable {
var hotData: [FAShortPlayModel]?
}

View File

@ -25,12 +25,12 @@ struct FAHomeMustSeeContentView: View {
.padding(.leading, 10)
.padding(.top, 12)
Spacer()
// Image("Frame 3013")
// .padding(.top, 14)
// .padding(.trailing, 10)
// .onTapGesture {
// self.viewModel.pushMustSee(moduleKey)
// }
Image("Frame 3013")
.padding(.top, 14)
.padding(.trailing, 10)
.onTapGesture {
self.viewModel.pushMustSee(moduleKey)
}
}
VStack(spacing: 11) {

View File

@ -31,9 +31,9 @@ struct FAHomeMustSeeShortView: View {
KFImage(URL(string: model.image_url ?? ""))
.resizable()
.scaledToFill()
.frame(width: 76, height: 102)
.cornerRadius(5)
.clipped()
.frame(width: 76, height: 102)
.padding(.leading, 9)
VStack(alignment: .leading) {

View File

@ -9,6 +9,23 @@ import UIKit
class FAGenresCell: UICollectionViewCell {
var model: FACategoryModel? {
didSet {
var list = model?.short_play_list
let firstModel = list?.removeFirst()
hotCountLabel.text = "\(firstModel?.watch_total ?? 0)"
nameLabel.text = model?.category_name
coverImageView.fa_setImage(firstModel?.image_url)
list?.enumerated().forEach {
let i = $0
let imageView = smallImageViewArr[i]
imageView.fa_setImage($1.image_url)
}
}
}
@IBOutlet weak var bgView: UIView!
@ -18,7 +35,7 @@ class FAGenresCell: UICollectionViewCell {
@IBOutlet weak var hotCountLabel: UILabel!
private lazy var smallImageViewArr: [UIView] = []
private lazy var smallImageViewArr: [UIImageView] = []
override func awakeFromNib() {
super.awakeFromNib()

View File

@ -45,6 +45,7 @@
<constraint firstItem="LbS-0P-tAf" firstAttribute="leading" secondItem="T3X-Bw-MIB" secondAttribute="leading" constant="152" id="Nrm-d1-SY5"/>
<constraint firstItem="dr5-yQ-gX9" firstAttribute="top" secondItem="LbS-0P-tAf" secondAttribute="bottom" constant="9" id="PFQ-Cy-rgv"/>
<constraint firstItem="JZp-LI-PlL" firstAttribute="centerY" secondItem="dr5-yQ-gX9" secondAttribute="centerY" id="Rx2-sn-DTb"/>
<constraint firstItem="hqB-yr-uhy" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="LbS-0P-tAf" secondAttribute="trailing" constant="10" id="fGa-4l-D8P"/>
<constraint firstItem="dr5-yQ-gX9" firstAttribute="leading" secondItem="LbS-0P-tAf" secondAttribute="leading" id="i4k-zs-CdI"/>
<constraint firstItem="JZp-LI-PlL" firstAttribute="leading" secondItem="dr5-yQ-gX9" secondAttribute="trailing" constant="1" id="jH5-jE-fIE"/>
<constraint firstItem="hqB-yr-uhy" firstAttribute="centerY" secondItem="LbS-0P-tAf" secondAttribute="centerY" id="llL-b1-yDx"/>

View File

@ -9,6 +9,17 @@ import UIKit
class FAGenresListCell: UICollectionViewCell {
var model: FAShortPlayModel? {
didSet {
coverImageView.fa_setImage(model?.image_url)
nameLabel.text = model?.name
}
}
@IBOutlet weak var coverImageView: FAImageView!
@IBOutlet weak var nameLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code

View File

@ -45,6 +45,10 @@
<constraint firstItem="7Si-zr-lN5" firstAttribute="top" secondItem="utm-U3-G8N" secondAttribute="bottom" constant="7" id="z5d-BM-XIT"/>
</constraints>
<size key="customSize" width="229" height="312"/>
<connections>
<outlet property="coverImageView" destination="utm-U3-G8N" id="0fD-cC-dSP"/>
<outlet property="nameLabel" destination="7Si-zr-lN5" id="gk6-pC-F5O"/>
</connections>
<point key="canvasLocation" x="275.57251908396944" y="133.80281690140845"/>
</collectionViewCell>
</objects>

View File

@ -0,0 +1,42 @@
//
// FARankingListCell.swift
// Fableon
//
// Created by 鸿 on 2025/10/20.
//
import UIKit
class FARankingListCell: UICollectionViewCell {
var model: FAShortPlayModel? {
didSet {
coverImageView.fa_setImage(model?.image_url)
nameLabel.text = model?.name
epLabel.text = "Ep.##".localizedReplace(text: "\(model?.episode_total ?? 0)")
countLabel.text = "\(model?.watch_total ?? 0)"
}
}
var number: Int = 0 {
didSet {
numberLabel.text = "\(number)"
}
}
@IBOutlet weak var numberLabel: UILabel!
@IBOutlet weak var coverImageView: FAImageView!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var epLabel: UILabel!
@IBOutlet weak var countLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}

View File

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="FARankingListCell" customModule="Fableon" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="509" height="100"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="509" height="100"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="1" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="b90-Bd-F4g">
<rect key="frame" x="12.333333333333336" y="37" width="11.666666666666664" height="26.333333333333329"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
<color key="textColor" name="#FFFFFF_0.8"/>
<nil key="highlightedColor"/>
</label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="HfV-02-Jbt" customClass="FAImageView" customModule="Fableon" customModuleProvider="target">
<rect key="frame" x="37" y="0.0" width="53" height="100"/>
<constraints>
<constraint firstAttribute="width" constant="53" id="WTo-v1-t4m"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="6"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5za-xf-gcJ">
<rect key="frame" x="98" y="7.0000000000000009" width="31.666666666666657" height="14.333333333333336"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="12"/>
<color key="textColor" name="#FFFFFF"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pzn-o5-BYh">
<rect key="frame" x="464.33333333333331" y="43" width="31.666666666666686" height="14.333333333333336"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="12"/>
<color key="textColor" name="#FFFFFF"/>
<nil key="highlightedColor"/>
</label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" image="Frame 2915" translatesAutoresizingMaskIntoConstraints="NO" id="v0e-ss-7Lj">
<rect key="frame" x="450.33333333333331" y="44" width="12" height="12"/>
</imageView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jIu-B2-ciK">
<rect key="frame" x="98" y="27.333333333333329" width="39.666666666666657" height="12"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Y10-wf-GlX">
<rect key="frame" x="9.0000000000000018" y="1.3333333333333357" width="21.666666666666671" height="9.6666666666666661"/>
<fontDescription key="fontDescription" type="system" pointSize="8"/>
<color key="textColor" name="#FFFFFF"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" name="#6D6D6D_0.4"/>
<constraints>
<constraint firstItem="Y10-wf-GlX" firstAttribute="centerY" secondItem="jIu-B2-ciK" secondAttribute="centerY" id="Noe-0G-SoL"/>
<constraint firstItem="Y10-wf-GlX" firstAttribute="centerX" secondItem="jIu-B2-ciK" secondAttribute="centerX" id="OKR-8b-RT4"/>
<constraint firstAttribute="height" constant="12" id="rWB-mc-vsy"/>
<constraint firstItem="Y10-wf-GlX" firstAttribute="leading" secondItem="jIu-B2-ciK" secondAttribute="leading" constant="9" id="uaB-MB-QSe"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="layer.masksToBounds" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="6"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
</subviews>
</view>
<viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
<constraints>
<constraint firstItem="jIu-B2-ciK" firstAttribute="leading" secondItem="5za-xf-gcJ" secondAttribute="leading" id="AYK-bK-VBE"/>
<constraint firstItem="pzn-o5-BYh" firstAttribute="leading" secondItem="v0e-ss-7Lj" secondAttribute="trailing" constant="2" id="BW0-ml-TWj"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="5za-xf-gcJ" secondAttribute="trailing" constant="80" id="U8T-vE-4vW"/>
<constraint firstAttribute="bottom" secondItem="HfV-02-Jbt" secondAttribute="bottom" id="VqO-XA-aRF"/>
<constraint firstItem="b90-Bd-F4g" firstAttribute="centerY" secondItem="gTV-IL-0wX" secondAttribute="centerY" id="bUl-mD-lff"/>
<constraint firstItem="HfV-02-Jbt" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="37" id="fOE-wQ-TM2"/>
<constraint firstItem="b90-Bd-F4g" firstAttribute="centerX" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="18" id="fuA-UT-HIO"/>
<constraint firstItem="5za-xf-gcJ" firstAttribute="leading" secondItem="HfV-02-Jbt" secondAttribute="trailing" constant="8" id="gx3-xg-AsW"/>
<constraint firstAttribute="trailing" secondItem="pzn-o5-BYh" secondAttribute="trailing" constant="13" id="pyb-RR-iSG"/>
<constraint firstItem="jIu-B2-ciK" firstAttribute="top" secondItem="5za-xf-gcJ" secondAttribute="bottom" constant="6" id="q6S-9v-82E"/>
<constraint firstItem="HfV-02-Jbt" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="sia-ic-tox"/>
<constraint firstItem="5za-xf-gcJ" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" constant="7" id="tow-FC-CE1"/>
<constraint firstItem="pzn-o5-BYh" firstAttribute="centerY" secondItem="gTV-IL-0wX" secondAttribute="centerY" id="z2P-bm-B0K"/>
<constraint firstItem="v0e-ss-7Lj" firstAttribute="centerY" secondItem="gTV-IL-0wX" secondAttribute="centerY" id="zJs-uc-PRZ"/>
</constraints>
<size key="customSize" width="509" height="100"/>
<connections>
<outlet property="countLabel" destination="pzn-o5-BYh" id="CEy-h1-Fnx"/>
<outlet property="coverImageView" destination="HfV-02-Jbt" id="0mc-gM-SHd"/>
<outlet property="epLabel" destination="Y10-wf-GlX" id="c5I-WJ-Opb"/>
<outlet property="nameLabel" destination="5za-xf-gcJ" id="LCr-OO-vcb"/>
<outlet property="numberLabel" destination="b90-Bd-F4g" id="3m4-as-KAS"/>
</connections>
<point key="canvasLocation" x="489.3129770992366" y="58.450704225352112"/>
</collectionViewCell>
</objects>
<resources>
<image name="Frame 2915" width="12" height="12"/>
<namedColor name="#6D6D6D_0.4">
<color red="0.42745098039215684" green="0.42745098039215684" blue="0.42745098039215684" alpha="0.40000000596046448" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="#FFFFFF">
<color red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="#FFFFFF_0.8">
<color red="1" green="1" blue="1" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

View File

@ -0,0 +1,212 @@
//
// FARankingListHeaderView.swift
// Fableon
//
// Created by 鸿 on 2025/10/20.
//
import UIKit
class FARankingListHeaderView: UIView {
override var intrinsicContentSize: CGSize {
return .init(width: UIScreen.width, height: 150 + coverSize)
}
var dataArr: [FAShortPlayModel] = [] {
didSet {
oneView.isHidden = true
twoView.isHidden = true
threeView.isHidden = true
if dataArr.count > 0 {
oneView.isHidden = false
oneView.model = dataArr[0]
}
if dataArr.count > 1 {
twoView.isHidden = false
twoView.model = dataArr[1]
}
if dataArr.count > 2 {
threeView.isHidden = false
threeView.model = dataArr[2]
}
}
}
private lazy var margin = UIScreen.getRatioWidth(size: 43)
private lazy var spacing = 10.0
private lazy var itemWidth = floor((UIScreen.width - margin * 2 - spacing * 2) / 3)
private lazy var coverSize = itemWidth - UIScreen.getRatioWidth(size: 36)
private lazy var oneView: FARankingListHeaderItemView = {
let view = FARankingListHeaderItemView()
view.isHidden = true
view.coverSize = coverSize
view.numberImageView.image = UIImage(named: "皇冠1")
return view
}()
private lazy var twoView: FARankingListHeaderItemView = {
let view = FARankingListHeaderItemView()
view.isHidden = true
view.coverSize = coverSize
view.numberImageView.image = UIImage(named: "皇冠2")
return view
}()
private lazy var threeView: FARankingListHeaderItemView = {
let view = FARankingListHeaderItemView()
view.isHidden = true
view.coverSize = coverSize
view.numberImageView.image = UIImage(named: "皇冠3")
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(oneView)
addSubview(twoView)
addSubview(threeView)
oneView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.width.equalTo(itemWidth)
make.top.equalToSuperview().offset(32)
make.bottom.equalToSuperview().offset(-10)
}
twoView.snp.makeConstraints { make in
make.right.equalTo(oneView.snp.left).offset(-10)
make.top.equalTo(oneView).offset(25)
make.width.equalTo(oneView)
make.bottom.equalToSuperview().offset(-10)
}
threeView.snp.makeConstraints { make in
make.left.equalTo(oneView.snp.right).offset(10)
make.top.equalTo(twoView).offset(5)
make.width.equalTo(oneView)
make.bottom.equalToSuperview().offset(-10)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class FARankingListHeaderItemView: UIView {
// override var intrinsicContentSize: CGSize {
// return .init(width: 97, height: 140 + coverSize)
// }
var model: FAShortPlayModel? {
didSet {
coverImageView.fa_setImage(model?.image_url)
nameLabel.text = model?.name
}
}
var coverSize: CGFloat = 60 {
didSet {
self.invalidateIntrinsicContentSize()
coverImageView.snp.updateConstraints { make in
make.width.height.equalTo(coverSize)
}
coverImageView.layer.cornerRadius = coverSize / 2
}
}
private lazy var bgView: UIView = {
let view = FAGradientView()
view.fa_locations = [0, 1]
view.fa_colors = [UIColor._1680_FB.cgColor, UIColor._79_A_7_DB.withAlphaComponent(0).cgColor]
view.fa_startPoint = .init(x: 0.5, y: 0)
view.fa_endPoint = .init(x: 0.5, y: 1)
return view
}()
private lazy var coverImageView: FAImageView = {
let imageView = FAImageView()
imageView.layer.borderWidth = 2
imageView.layer.borderColor = UIColor.FFFFFF.cgColor
return imageView
}()
private(set) lazy var numberImageView: UIImageView = {
let imageView = UIImageView()
return imageView
}()
private lazy var nameLabel: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.textColor = .FFFFFF
label.font = .font(ofSize: 14, weight: .bold)
label.numberOfLines = 2
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(bgView)
addSubview(coverImageView)
addSubview(numberImageView)
addSubview(nameLabel)
bgView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
coverImageView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(20)
make.width.height.equalTo(60)
}
numberImageView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(-5)
make.top.equalToSuperview().offset(-8)
}
nameLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.right.lessThanOrEqualToSuperview().offset(-9)
make.top.equalTo(coverImageView.snp.bottom).offset(10)
}
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap))
addGestureRecognizer(tap)
}
@objc private func handleTap() {
let vc = FAPlayerDetailViewController()
vc.shortPlayId = self.model?.short_play_id
self.viewController?.navigationController?.pushViewController(vc, animated: true)
}
override func layoutSubviews() {
super.layoutSubviews()
bgView.fa_setRoundedCorner(topLeft: self.width / 2, topRight: self.width / 2, bottomLeft: 0, bottomRight: 0)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -126,9 +126,27 @@ extension FAHomeViewModel {
}
func pushMustSee(_ key: FAHomeModuleItem.ModuleKey?) {
debugLog("\(key?.rawValue)")
var vc: FAViewController?
let vc = FAGenresViewController()
switch key {
case .v3_recommand:
vc = FAPopularListViewController()
case .cagetory_recommand:
vc = FAGenresViewController()
case .new_recommand:
vc = FANewListViewController()
case .week_ranking:
vc = FARankingListViewController()
default:
break
}
if let vc = vc {
FATool.topViewController?.navigationController?.pushViewController(vc, animated: true)
}
}
}

View File

@ -13,18 +13,17 @@ class FAFeedbackViewController: FAAppWebViewController {
self.webUrl = kFAFeedBackHomeWebUrl
super.viewDidLoad()
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "Frame 2085663258"), style: .plain, target: self, action: #selector(handleRightBarButton))
}
/*
// MARK: - Navigation
@objc private func handleRightBarButton() {
let vc = FAAppWebViewController()
vc.webUrl = kFAFeedBackListWebUrl
self.navigationController?.pushViewController(vc, animated: true)
// 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.
}
*/
}

View File

@ -0,0 +1,154 @@
//
// FAMeListViewController.swift
// Fableon
//
// Created by 鸿 on 2025/10/23.
//
import UIKit
import JXPagingView
class FAMeListViewController: FAViewController, JXPagingViewListViewDelegate {
var scrollCallback: ((_ : UIScrollView) -> Void)?
private lazy var dataArr: [FAMeItemModel] = {
let arr = [
FAMeItemModel(type: .feedback, name: "Feedback".localized, icon: UIImage(named: "icon_feedback")),
FAMeItemModel(type: .about, name: "About".localized, icon: UIImage(named: "icon_about")),
// FAMeItemModel(type: .setting, name: "Setting".localized, icon: UIImage(named: "icon_setting"))
FAMeItemModel(type: .privacyPolicy, name: "Privacy Policy".localized, icon: UIImage(named: "icon_privacy")),
FAMeItemModel(type: .userAgreement, name: "User Agreement".localized, icon: UIImage(named: "icon_user")),
FAMeItemModel(type: .visitWebsite, name: "Visit Website".localized, icon: UIImage(named: "icon_visit")),
]
return arr
}()
private lazy var tableViewHeaderView: FAMeTableViewHeaderView = {
let view = FAMeTableViewHeaderView(frame: .init(x: 0, y: 0, width: UIScreen.width, height: 124))
view.userInfo = FALogin.manager.userInfo
return view
}()
private lazy var contentView: UIView = {
let view = UIView()
view.backgroundColor = ._4_D_4_A_4_A.withAlphaComponent(0.5)
view.fa_setRoundedCorner(topLeft: 28, topRight: 0, bottomLeft: 0, bottomRight: 0)
return view
}()
private lazy var tableView: FATableView = {
let tableView = FATableView(frame: .zero, style: .plain)
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = 56
tableView.separatorInset = .init(top: 0, left: 32, bottom: 0, right: 32)
tableView.register(UINib(nibName: "FAMeCell", bundle: nil), forCellReuseIdentifier: "cell")
// tableView.addObserver(self, forKeyPath: "contentSize", context: nil)
return tableView
}()
deinit {
NotificationCenter.default.removeObserver(self)
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(userInfoUpdateNotification), name: FALogin.userInfoUpdateNotification, object: nil)
self.bgView.isHidden = true
fa_setupLayout()
}
@objc private func userInfoUpdateNotification() {
tableViewHeaderView.userInfo = FALogin.manager.userInfo
}
func listScrollView() -> UIScrollView {
return self.tableView
}
func listViewDidScrollCallback(callback: @escaping (UIScrollView) -> ()) {
self.scrollCallback = callback
}
}
extension FAMeListViewController {
private func fa_setupLayout() {
tableView.tableHeaderView = self.tableViewHeaderView
view.addSubview(contentView)
contentView.addSubview(tableView)
contentView.snp.makeConstraints { make in
make.left.right.top.bottom.equalToSuperview()
}
tableView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(19)
}
}
}
//MARK: UITableViewDelegate, UITableViewDataSource
extension FAMeListViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FAMeCell
cell.item = dataArr[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArr.count
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.scrollCallback?(scrollView)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = dataArr[indexPath.row]
switch item.type {
case .about:
let vc = FAAboutViewController()
self.navigationController?.pushViewController(vc, animated: true)
case .setting:
let vc = FASettingViewController()
self.navigationController?.pushViewController(vc, animated: true)
case .feedback:
let vc = FAFeedbackViewController()
self.navigationController?.pushViewController(vc, animated: true)
case .privacyPolicy:
let vc = FABaseWebViewController()
vc.webUrl = FAWebBaseURL + "/private"
self.navigationController?.pushViewController(vc, animated: true)
case .userAgreement:
let vc = FABaseWebViewController()
vc.webUrl = FAWebBaseURL + "/user_policy"
self.navigationController?.pushViewController(vc, animated: true)
case .visitWebsite:
if let url = URL(string: FAWebBaseURL) {
UIApplication.shared.open(url)
}
default:
break
}
}
}

View File

@ -7,53 +7,36 @@
import UIKit
import YYText
import JXSegmentedView
import JXPagingView
class FAMeViewController: FAViewController {
private lazy var dataArr: [FAMeItemModel] = {
let arr = [
// FAMeItemModel(type: .feedback, name: "Feedback".localized, icon: UIImage(named: "icon_feedback")),
FAMeItemModel(type: .about, name: "About".localized, icon: UIImage(named: "icon_about")),
// FAMeItemModel(type: .setting, name: "Setting".localized, icon: UIImage(named: "icon_setting"))
FAMeItemModel(type: .privacyPolicy, name: "Privacy Policy".localized, icon: UIImage(named: "icon_privacy")),
FAMeItemModel(type: .userAgreement, name: "User Agreement".localized, icon: UIImage(named: "icon_user")),
FAMeItemModel(type: .visitWebsite, name: "Visit Website".localized, icon: UIImage(named: "icon_visit")),
]
return arr
}()
private lazy var scrollView: FAScrollView = {
let scrollView = FAScrollView()
return scrollView
}()
private lazy var headerView: FAMeHeaderView = {
let view = FAMeHeaderView()
view.userInfo = FALogin.manager.userInfo
return view
}()
private lazy var contentView: UIView = {
let view = UIView()
view.backgroundColor = ._4_D_4_A_4_A.withAlphaComponent(0.5)
view.fa_setRoundedCorner(topLeft: 30, topRight: 30, bottomLeft: 0, bottomRight: 0)
private lazy var listView: FAMeListViewController = FAMeListViewController()
private lazy var segmentedView: JXSegmentedView = {
let view = JXSegmentedView()
return view
}()
private lazy var tableView: FATableView = {
let tableView = FATableView(frame: .zero, style: .plain)
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = 56
tableView.separatorInset = .init(top: 0, left: 32, bottom: 0, right: 32)
tableView.register(UINib(nibName: "FAMeCell", bundle: nil), forCellReuseIdentifier: "cell")
tableView.addObserver(self, forKeyPath: "contentSize", context: nil)
return tableView
private lazy var pagingView: JXPagingView = {
let view = JXPagingView(delegate: self)
view.mainTableView.backgroundColor = .clear
view.listContainerView.backgroundColor = .clear
view.listContainerView.listCellBackgroundColor = .clear
view.mainTableView.fa_addRefreshHeader(insetTop: 0) { [weak self] in
self?.handleHeaderRefresh(nil)
}
return view
}()
deinit {
NotificationCenter.default.removeObserver(self)
}
@ -62,6 +45,8 @@ class FAMeViewController: FAViewController {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(userInfoUpdateNotification), name: FALogin.userInfoUpdateNotification, object: nil)
fa_setupLayout()
}
@ -75,13 +60,12 @@ class FAMeViewController: FAViewController {
FALogin.manager.requestUserInfo(completer: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "contentSize" {
self.updateLayout()
override func handleHeaderRefresh(_ completer: (() -> Void)?) {
FALogin.manager.requestUserInfo { [weak self] in
self?.pagingView.mainTableView.fa_endHeaderRefreshing()
}
}
@objc private func userInfoUpdateNotification() {
headerView.userInfo = FALogin.manager.userInfo
}
@ -91,98 +75,43 @@ class FAMeViewController: FAViewController {
extension FAMeViewController {
private func fa_setupLayout() {
view.addSubview(scrollView)
scrollView.addSubview(headerView)
scrollView.addSubview(contentView)
contentView.addSubview(tableView)
view.addSubview(pagingView)
scrollView.snp.makeConstraints { make in
pagingView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(UIScreen.safeTop)
}
headerView.snp.makeConstraints { make in
make.left.centerX.equalToSuperview()
make.top.equalToSuperview().offset(40)
}
contentView.snp.makeConstraints { make in
make.left.centerX.equalToSuperview()
make.top.equalTo(headerView.snp.bottom).offset(24)
make.bottom.equalToSuperview()
make.height.equalTo(UIScreen.height - UIScreen.tabBarHeight - UIScreen.safeTop)
}
tableView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(19)
}
}
private func updateLayout() {
let maxHeight = UIScreen.height - UIScreen.tabBarHeight - UIScreen.safeTop
let minHeight = UIScreen.height - UIScreen.tabBarHeight - UIScreen.safeTop - self.headerView.height - 40 - 24
var height = self.tableView.contentSize.height
if height > maxHeight {
height = maxHeight
}
if height < minHeight {
height = minHeight
}
contentView.snp.updateConstraints { make in
make.height.equalTo(height)
}
}
}
//MARK: UITableViewDelegate, UITableViewDataSource
extension FAMeViewController: UITableViewDelegate, UITableViewDataSource {
//MARK: JXPagingViewDelegate
extension FAMeViewController: JXPagingViewDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FAMeCell
cell.item = dataArr[indexPath.row]
return cell
func pagingView(_ pagingView: JXPagingView, initListAtIndex index: Int) -> any JXPagingViewListViewDelegate {
return listView
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArr.count
func tableHeaderViewHeight(in pagingView: JXPagingView) -> Int {
return Int(ceill(headerView.contentHeight))
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = dataArr[indexPath.row]
switch item.type {
case .about:
let vc = FAAboutViewController()
self.navigationController?.pushViewController(vc, animated: true)
case .setting:
let vc = FASettingViewController()
self.navigationController?.pushViewController(vc, animated: true)
case .feedback:
let vc = FAFeedbackViewController()
self.navigationController?.pushViewController(vc, animated: true)
case .privacyPolicy:
let vc = FABaseWebViewController()
vc.webUrl = FAWebBaseURL + "/private"
self.navigationController?.pushViewController(vc, animated: true)
case .userAgreement:
let vc = FABaseWebViewController()
vc.webUrl = FAWebBaseURL + "/user_policy"
self.navigationController?.pushViewController(vc, animated: true)
case .visitWebsite:
if let url = URL(string: FAWebBaseURL) {
UIApplication.shared.open(url)
func tableHeaderView(in pagingView: JXPagingView) -> UIView {
return headerView
}
default:
break
func heightForPinSectionHeader(in pagingView: JXPagingView) -> Int {
return 0
}
func viewForPinSectionHeader(in pagingView: JXPagingView) -> UIView {
return UIView()
}
func numberOfLists(in pagingView: JXPagingView) -> Int {
return 1
}
}

View File

@ -16,6 +16,12 @@ struct FAMeItemModel {
case privacyPolicy
case userAgreement
case visitWebsite
///
case consumptionRecords
///
case purchaseRecords
///
case rewardCoins
}

View File

@ -0,0 +1,83 @@
//
// FAMeCoinsView.swift
// Fableon
//
// Created by 鸿 on 2025/10/22.
//
import UIKit
class FAMeCoinsView: UIControl {
var title: String? {
didSet {
titleLabel.text = title
}
}
var count: Int = 0 {
didSet {
coinLabel.text = "\(count)"
}
}
private lazy var coinImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "coins_icon_01"))
imageView.setContentHuggingPriority(.required, for: .horizontal)
imageView.setContentCompressionResistancePriority(.required, for: .horizontal)
return imageView
}()
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 10, weight: .bold)
label.textColor = .F_7_F_497
return label
}()
private lazy var coinLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 14, weight: .bold)
label.textColor = .FFFFFF
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension FAMeCoinsView {
private func fa_setupLayout() {
addSubview(coinImageView)
addSubview(titleLabel)
addSubview(coinLabel)
coinImageView.snp.makeConstraints { make in
make.left.equalToSuperview()
make.centerY.equalToSuperview()
make.top.equalToSuperview()
}
titleLabel.snp.makeConstraints { make in
make.top.equalToSuperview().offset(-2)
make.left.equalTo(coinImageView.snp.right).offset(10)
make.right.lessThanOrEqualToSuperview()
}
coinLabel.snp.makeConstraints { make in
make.left.equalTo(titleLabel)
make.right.lessThanOrEqualToSuperview()
make.bottom.equalToSuperview().offset(0)
}
}
}

View File

@ -9,6 +9,8 @@ import UIKit
class FAMeHeaderView: UIView {
var contentHeight: CGFloat = 40 + 66 + 40 + 28 + 24
var userInfo: FAUserInfo? {
didSet {
avatarImageView.fa_setImage(userInfo?.avator)
@ -16,6 +18,10 @@ class FAMeHeaderView: UIView {
userNameLabel.text = userInfo?.getNickName()
idLabel.text = "ID\(userInfo?.customer_id ?? "")"
coinsView.count = userInfo?.coin_left_total ?? 0
bonusCoinsView.count = userInfo?.send_coin_left_total ?? 0
}
}
@ -39,6 +45,31 @@ class FAMeHeaderView: UIView {
return label
}()
private lazy var coinsView: FAMeCoinsView = {
let view = FAMeCoinsView()
view.title = "Coins".localized
view.count = 0
view.addAction(UIAction(handler: { [weak self] _ in
guard let self = self else { return }
let vc = FAWalletViewController()
self.viewController?.navigationController?.pushViewController(vc, animated: true)
}), for: .touchUpInside)
return view
}()
private lazy var bonusCoinsView: FAMeCoinsView = {
let view = FAMeCoinsView()
view.title = "Bonus"
view.count = 0
view.addAction(UIAction(handler: { [weak self] _ in
guard let self = self else { return }
let vc = FAWalletViewController()
self.viewController?.navigationController?.pushViewController(vc, animated: true)
}), for: .touchUpInside)
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
@ -55,12 +86,13 @@ extension FAMeHeaderView {
addSubview(avatarImageView)
addSubview(userNameLabel)
addSubview(idLabel)
addSubview(coinsView)
addSubview(bonusCoinsView)
avatarImageView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.top.equalToSuperview()
make.top.equalToSuperview().offset(40)
make.width.height.equalTo(66)
make.bottom.equalToSuperview()
}
userNameLabel.snp.makeConstraints { make in
@ -73,6 +105,16 @@ extension FAMeHeaderView {
make.left.equalTo(userNameLabel)
make.top.equalTo(userNameLabel.snp.bottom).offset(2)
}
coinsView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.top.equalTo(avatarImageView.snp.bottom).offset(40)
}
bonusCoinsView.snp.makeConstraints { make in
make.top.equalTo(coinsView)
make.left.equalTo(coinsView.snp.right).offset(40)
}
}
}

View File

@ -0,0 +1,165 @@
//
// FAMeTableViewHeaderView.swift
// Fableon
//
// Created by 鸿 on 2025/10/22.
//
import UIKit
class FAMeTableViewHeaderView: UIView {
var userInfo: FAUserInfo? {
didSet {
if userInfo?.is_vip == true {
subscribeView.isHidden = true
expirationView.isHidden = false
vipImageView.image = UIImage(named: "vip_image_02")
titleLabel.text = "Activated".localized
subtitleLabel.text = "vip_tip_01".localized
} else {
subscribeView.isHidden = false
expirationView.isHidden = true
vipImageView.image = UIImage(named: "vip_image_01")
titleLabel.text = "Not Activated".localized
subtitleLabel.text = "vip_tip_02".localized
}
expirationView.setNeedsUpdateConfiguration()
}
}
private lazy var contentView: FAGradientView = {
let view = FAGradientView()
view.fa_colors = [UIColor.C_3_FAFF.cgColor, UIColor._7_FDBF_5.cgColor, UIColor._105_C_91.cgColor]
view.fa_locations = [0, 0.5, 1]
view.fa_startPoint = .init(x: 0, y: 0.5)
view.fa_endPoint = .init(x: 1, y: 0.5)
view.fa_setRoundedCorner(topLeft: 28, topRight: 6, bottomLeft: 6, bottomRight: 6)
return view
}()
private lazy var vipImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "vip_image_01"))
return imageView
}()
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 18, weight: .bold)
label.textColor = ._3_A_271_E
return label
}()
private lazy var subtitleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .regular)
label.textColor = ._000000.withAlphaComponent(0.5)
return label
}()
private lazy var subscribeView: UIButton = {
var config = UIButton.Configuration.plain()
config.attributedTitle = AttributedString("Subscribe".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor._333333
]))
let button = FAGradientButton(configuration: config)
button.isUserInteractionEnabled = false
button.layer.cornerRadius = 18
button.layer.masksToBounds = true
button.fa_colors = [UIColor.BEDFFF.cgColor, UIColor._52_A_2_F_1.cgColor]
button.fa_locations = [0, 1]
button.fa_startPoint = .init(x: 0, y: 0.5)
button.fa_endPoint = .init(x: 1, y: 0.5)
return button
}()
private lazy var expirationView: UIButton = {
var config = UIButton.Configuration.plain()
config.contentInsets = .zero
config.image = UIImage(named: "Frame 3021")
config.imagePadding = 4
let button = UIButton(configuration: config)
button.isUserInteractionEnabled = false
button.configurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
let date = Date(timeIntervalSince1970: userInfo?.vip_end_time ?? 0)
let text = "vip_expires_date".localizedReplace(text: date.fa_formatString("yyyy-MM-dd"))
button.configuration?.attributedTitle = AttributedString(text, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 12, weight: .medium),
.foregroundColor : UIColor._3_A_271_E
]))
}
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
let tap = UITapGestureRecognizer { [weak self] _ in
guard let self = self else { return }
let vc = FAStoreViewController()
self.viewController?.navigationController?.pushViewController(vc, animated: true)
}
contentView.addGestureRecognizer(tap)
fa_setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension FAMeTableViewHeaderView {
private func fa_setupLayout() {
addSubview(contentView)
contentView.addSubview(vipImageView)
contentView.addSubview(titleLabel)
contentView.addSubview(subtitleLabel)
contentView.addSubview(subscribeView)
contentView.addSubview(expirationView)
contentView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerX.equalToSuperview()
make.top.bottom.equalToSuperview()
}
vipImageView.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.right.equalToSuperview().offset(-4)
}
titleLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.top.equalToSuperview().offset(16)
}
subtitleLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.top.equalTo(titleLabel.snp.bottom).offset(3)
}
subscribeView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.bottom.equalToSuperview().offset(-16)
make.height.equalTo(36)
make.width.equalTo(130)
}
expirationView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.bottom.equalToSuperview().offset(-16)
}
}
}

View File

@ -0,0 +1,26 @@
//
// FAVideoUnlockResult.swift
// Fableon
//
// Created by 鸿 on 2025/10/21.
//
import UIKit
import SmartCodable
struct FAVideoUnlockResult: SmartCodable {
enum Status: String, SmartCaseDefaultable {
///
case jump = "jump"
///
case noPlay = "no_play"
///
case notEnough = "not_enough"
///
case success = "success"
}
var status: Status?
}

View File

@ -12,6 +12,8 @@ class FAEpSelectorCell: UICollectionViewCell {
var model: FAVideoInfoModel? {
didSet {
numberLabel.text = model?.episode
lockImageView.isHidden = !(model?.is_lock ?? true)
}
}
@ -31,6 +33,9 @@ class FAEpSelectorCell: UICollectionViewCell {
@IBOutlet weak var numberLabel: UILabel!
@IBOutlet weak var lockImageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
self.layer.masksToBounds = false

View File

@ -23,18 +23,27 @@
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" image="lock_icon_01" translatesAutoresizingMaskIntoConstraints="NO" id="SWr-gt-V62">
<rect key="frame" x="169" y="237" width="12" height="12"/>
</imageView>
</subviews>
</view>
<viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
<constraints>
<constraint firstItem="yhy-MQ-hyT" firstAttribute="centerX" secondItem="gTV-IL-0wX" secondAttribute="centerX" id="CFQ-ov-ult"/>
<constraint firstItem="yhy-MQ-hyT" firstAttribute="centerY" secondItem="gTV-IL-0wX" secondAttribute="centerY" id="Ov2-Ct-sBR"/>
<constraint firstAttribute="bottom" secondItem="SWr-gt-V62" secondAttribute="bottom" constant="1" id="yP9-YB-f8b"/>
<constraint firstAttribute="trailing" secondItem="SWr-gt-V62" secondAttribute="trailing" constant="4" id="zsC-gY-FUL"/>
</constraints>
<size key="customSize" width="185" height="250"/>
<connections>
<outlet property="lockImageView" destination="SWr-gt-V62" id="Y64-xm-3BX"/>
<outlet property="numberLabel" destination="yhy-MQ-hyT" id="SSw-lq-oFo"/>
</connections>
<point key="canvasLocation" x="241.98473282442748" y="111.9718309859155"/>
</collectionViewCell>
</objects>
<resources>
<image name="lock_icon_01" width="12" height="12"/>
</resources>
</document>

View File

@ -14,11 +14,17 @@ class FAPlayerDetailCell: JXPlayerListCell {
return FAPlayerDetailControlView.self
}
var fa_viewModel: FAShortDetailViewModel {
return self.viewModel as! FAShortDetailViewModel
}
override var model: Any? {
didSet {
let model = self.model as? FAVideoInfoModel
self.player.setPlayUrl(url: model?.video_url ?? "")
self.lockView.isHidden = !(model?.is_lock ?? true)
lockView.videoInfo = model
}
}
@ -29,9 +35,26 @@ class FAPlayerDetailCell: JXPlayerListCell {
}
}
var hasLastEpisodeUnlocked: Bool = false {
didSet {
self.lockView.hasLastEpisodeUnlocked = hasLastEpisodeUnlocked
}
}
private lazy var lockView: FAVideoLockView = {
let view = FAVideoLockView()
view.clickUnlockButton = { [weak self] in
self?.fa_viewModel.handleUnlockVideo()
}
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
@MainActor required init?(coder: NSCoder) {
@ -41,6 +64,18 @@ class FAPlayerDetailCell: JXPlayerListCell {
}
extension FAPlayerDetailCell {
private func fa_setupLayout() {
addSubview(lockView)
lockView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
}
extension FAPlayerDetailCell {
override func jx_playerReadyToPlay(_ player: JXPlayer) {

View File

@ -24,7 +24,7 @@ class FAPlayerDetailControlView: JXPlayerListControlView {
override var model: Any? {
didSet {
// let model = self.model as? FAVideoInfoModel
let model = self.model as? FAVideoInfoModel
updateEp()
}
@ -133,6 +133,8 @@ class FAPlayerDetailControlView: JXPlayerListControlView {
return button
}()
deinit {
NotificationCenter.default.removeObserver(self)
}
@ -230,6 +232,8 @@ extension FAPlayerDetailControlView {
make.height.equalTo(44)
}
}
}

View File

@ -0,0 +1,80 @@
//
// FAVideoLockView.swift
// Fableon
//
// Created by 鸿 on 2025/10/20.
//
import UIKit
class FAVideoLockView: UIView {
var clickUnlockButton: (() -> Void)?
var videoInfo: FAVideoInfoModel? {
didSet {
unlockButton.setNeedsUpdateConfiguration()
}
}
var hasLastEpisodeUnlocked = false {
didSet {
unlockButton.setNeedsUpdateConfiguration()
}
}
private lazy var unlockButton: UIButton = {
var config = UIButton.Configuration.plain()
config.image = UIImage(named: "lock_icon_02")
config.imagePadding = 6
let button = FAGradientButton(configuration: config, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
self.clickUnlockButton?()
}))
button.configurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
let attributeContainer = AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .medium),
.foregroundColor : UIColor._000000
])
if hasLastEpisodeUnlocked {
button.configuration?.attributedTitle = .init("video_lock_tip_text".localized, attributes: attributeContainer)
} else {
button.configuration?.attributedTitle = .init("Unlocking costs ## coins".localizedReplace(text: "\(videoInfo?.coins ?? 0)"), attributes: attributeContainer)
}
}
button.layer.cornerRadius = 26
button.layer.masksToBounds = true
button.fa_colors = [UIColor.BEDFFF.cgColor, UIColor._52_A_2_F_1.cgColor]
button.fa_locations = [0, 1]
button.fa_startPoint = .init(x: 0, y: 0.5)
button.fa_endPoint = .init(x: 1, y: 0.5)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = ._000000.withAlphaComponent(0.6)
fa_setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension FAVideoLockView {
private func fa_setupLayout() {
addSubview(unlockButton)
unlockButton.snp.makeConstraints { make in
make.center.equalToSuperview()
make.left.equalToSuperview().offset(44)
make.height.equalTo(52)
}
}
}

View File

@ -13,6 +13,7 @@ import FDFullscreenPopGesture
class FAPlayerDetailViewController: JXPlayerListViewController {
var shortPlayId: String?
var activityId: String?
override var ViewModelClass: JXPlayerListViewModel.Type {
return FAShortDetailViewModel.self
@ -77,10 +78,16 @@ class FAPlayerDetailViewController: JXPlayerListViewController {
}
override func play() {
super.play()
let videoInfo = self.viewModel.currentCell?.model as? FAVideoInfoModel
if videoInfo?.is_lock != true {
super.play()
FAAPI.requestCreatePlayHistory(videoId: videoInfo?.short_play_video_id, shortPlayId: videoInfo?.short_play_id)
return
}
self.pause()
}
}
@ -110,6 +117,13 @@ extension FAPlayerDetailViewController: JXPlayerListViewControllerDelegate, JXPl
let cell = self.dequeueReusableCell(withReuseIdentifier: "FAPlayerDetailCell", for: indexPath) as! FAPlayerDetailCell
cell.model = self.fa_viewModel.dataArr[indexPath.section].episodeList?[indexPath.row]
cell.shortModel = self.fa_viewModel.dataArr[indexPath.section].shortPlayInfo
let upRow = indexPath.row - 1
if upRow >= 0, let videoInfo = self.fa_viewModel.dataArr[indexPath.section].episodeList?[upRow], videoInfo.is_lock == true {
cell.hasLastEpisodeUnlocked = true
} else {
cell.hasLastEpisodeUnlocked = false
}
return cell
}

View File

@ -110,5 +110,61 @@ extension FAShortDetailViewModel {
self.popView = view
}
func handleUnlockVideo() {
unlockVideo { [weak self] finish in
if finish {
self?.playerListVC?.reloadData {
self?.playerListVC?.play()
}
}
}
}
///
func openRechargeView() {
guard let videoInfo = self.currentCell?.model as? FAVideoInfoModel else { return }
guard self.popView == nil else { return }
}
}
extension FAShortDetailViewModel {
private func unlockVideo(completer: ((_ finish: Bool) -> Void)?) {
guard let videoInfo = self.currentCell?.model as? FAVideoInfoModel else { return }
guard let shortPlayId = videoInfo.short_play_id else { return }
guard let videoId = videoInfo.short_play_video_id else { return }
FAAPI.requestCoinUnlockVideo(shortPlayId: shortPlayId, videoId: videoId) { [weak self] model in
guard let self = self else { return }
guard let model = model else {
completer?(false)
return
}
switch model.status {
case .jump:
FAToast.show(text: "buy_fail_toast_02".localized)
case .noPlay:
FAToast.show(text: "buy_fail_toast_01".localized)
case .notEnough:
self.openRechargeView()
default: break
}
if model.status == .success {
FALogin.manager.requestUserInfo {
videoInfo.is_lock = false
completer?(true)
}
} else {
completer?(false)
}
}
}
}

View File

@ -0,0 +1,52 @@
//
// FACoinRecordViewController.swift
// Fableon
//
// Created by 鸿 on 2025/10/23.
//
import UIKit
class FACoinRecordViewController: FAViewController {
private lazy var tableView: FATableView = {
let tableView = FATableView(frame: .zero, style: .plain)
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = 72
tableView.separatorColor = .FFFFFF.withAlphaComponent(0.1)
tableView.separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16)
tableView.contentInset = .init(top: 10, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
tableView.register(UINib(nibName: "FAOrderRecordCell", bundle: nil), forCellReuseIdentifier: "cell")
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
bgView.isHidden = true
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview()
}
}
}
//MARK: UITableViewDelegate UITableViewDataSource
extension FACoinRecordViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FAOrderRecordCell
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
}

View File

@ -0,0 +1,66 @@
//
// FAConsumptionRecordsViewController.swift
// Fableon
//
// Created by 鸿 on 2025/10/23.
//
import UIKit
class FAConsumptionRecordsViewController: FAViewController {
private lazy var tableView: FATableView = {
let tableView = FATableView(frame: .zero, style: .plain)
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = 71
tableView.separatorColor = .FFFFFF.withAlphaComponent(0.1)
tableView.separatorInset = .init(top: 0, left: 34, bottom: 0, right: 34)
tableView.contentInset = .init(top: 10, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
tableView.register(UINib(nibName: "FAConsumptionRecordsCell", bundle: nil), forCellReuseIdentifier: "cell")
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Consumption Records".localized
fa_setupLayout()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
fa_setNavigationStyle()
}
}
extension FAConsumptionRecordsViewController {
private func fa_setupLayout() {
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(UIScreen.navBarHeight)
}
}
}
//MARK: UITableViewDelegate UITableViewDataSource
extension FAConsumptionRecordsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FAConsumptionRecordsCell
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
}

View File

@ -0,0 +1,112 @@
//
// FAOrderRecordsViewController.swift
// Fableon
//
// Created by 鸿 on 2025/10/23.
//
import UIKit
import JXSegmentedView
class FAOrderRecordsViewController: FAViewController {
private lazy var titles = ["Coin Record".localized, "VIP Record".localized]
private lazy var viewControllers = [FACoinRecordViewController(), FAVipRecordViewController()]
private lazy var segmentedDataSource: JXSegmentedTitleDataSource = {
let dataSource = JXSegmentedTitleDataSource()
dataSource.itemWidth = (UIScreen.width - 32 - 24) / CGFloat(titles.count)
dataSource.titles = titles
dataSource.isTitleMaskEnabled = true
dataSource.titleNormalColor = .FFFFFF.withAlphaComponent(0.5)
dataSource.titleSelectedColor = ._333333
dataSource.titleNormalFont = .font(ofSize: 14, weight: .bold)
dataSource.titleSelectedFont = .font(ofSize: 14, weight: .bold)
dataSource.itemSpacing = 0
return dataSource
}()
private lazy var segmentedIndicator: JXSegmentedIndicatorBackgroundView = {
let indicator = JXSegmentedIndicatorBackgroundView()
indicator.indicatorHeight = 36
indicator.indicatorWidthIncrement = 0
indicator.indicatorColor = ._74_BBFF
return indicator
}()
private lazy var segmentedView: JXSegmentedView = {
let view = JXSegmentedView()
view.backgroundColor = .FFFFFF.withAlphaComponent(0.15)
view.layer.cornerRadius = 24
view.layer.masksToBounds = true
view.dataSource = segmentedDataSource
view.delegate = self
view.indicators = [segmentedIndicator]
view.listContainer = listContainerView
return view
}()
private lazy var listContainerView: JXSegmentedListContainerView = {
let view = JXSegmentedListContainerView(dataSource: self)
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Order Records".localized
fa_setupLayout()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
fa_setNavigationStyle()
}
}
extension FAOrderRecordsViewController {
private func fa_setupLayout() {
view.addSubview(segmentedView)
view.addSubview(listContainerView)
segmentedView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(20 + UIScreen.navBarHeight)
make.height.equalTo(48)
}
listContainerView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalTo(segmentedView.snp.bottom)
}
}
}
//MARK: JXSegmentedViewDelegate
extension FAOrderRecordsViewController: JXSegmentedViewDelegate {
func segmentedView(_ segmentedView: JXSegmentedView, didSelectedItemAt index: Int) {
}
}
//MARK: JXSegmentedViewDelegate
extension FAOrderRecordsViewController: JXSegmentedListContainerViewDataSource {
func listContainerView(_ listContainerView: JXSegmentedListContainerView, initListAt index: Int) -> any JXSegmentedListContainerViewListDelegate {
return viewControllers[index]
}
func numberOfLists(in listContainerView: JXSegmentedListContainerView) -> Int {
return self.titles.count
}
}

View File

@ -0,0 +1,55 @@
//
// FARewardCoinsViewController.swift
// Fableon
//
// Created by 鸿 on 2025/10/23.
//
import UIKit
class FARewardCoinsViewController: FAViewController {
private lazy var tableView: FATableView = {
let tableView = FATableView(frame: .zero, style: .plain)
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = 87
tableView.separatorColor = .FFFFFF.withAlphaComponent(0.1)
tableView.separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16)
tableView.contentInset = .init(top: 10, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
tableView.register(UINib(nibName: "FARewardCoinsCell", bundle: nil), forCellReuseIdentifier: "cell")
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Reward coins"
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(UIScreen.navBarHeight + 10)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
fa_setNavigationStyle()
}
}
//MARK: UITableViewDelegate UITableViewDataSource
extension FARewardCoinsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FARewardCoinsCell
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
}

View File

@ -0,0 +1,58 @@
//
// FAStoreViewController.swift
// Fableon
//
// Created by 鸿 on 2025/10/22.
//
import UIKit
class FAStoreViewController: FAViewController {
private lazy var scrollView: FAScrollView = {
let scrollView = FAScrollView()
return scrollView
}()
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .medium)
label.textColor = .FFFFFF
label.text = "store_title_1".localized
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Store".localized
fa_setupLayout()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
fa_setNavigationStyle()
}
}
extension FAStoreViewController {
private func fa_setupLayout() {
view.addSubview(scrollView)
scrollView.addSubview(titleLabel)
scrollView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(UIScreen.navBarHeight)
}
titleLabel.snp.makeConstraints { make in
make.top.equalToSuperview().offset(0)
make.centerX.equalToSuperview()
}
}
}

View File

@ -0,0 +1,53 @@
//
// FAVipRecordViewController.swift
// Fableon
//
// Created by 鸿 on 2025/10/23.
//
import UIKit
class FAVipRecordViewController: FAViewController {
private lazy var tableView: FATableView = {
let tableView = FATableView(frame: .zero, style: .plain)
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = 72
tableView.separatorColor = .FFFFFF.withAlphaComponent(0.1)
tableView.separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16)
tableView.contentInset = .init(top: 10, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
tableView.register(UINib(nibName: "FAOrderRecordCell", bundle: nil), forCellReuseIdentifier: "cell")
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
bgView.isHidden = true
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview()
}
}
}
//MARK: UITableViewDelegate UITableViewDataSource
extension FAVipRecordViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FAOrderRecordCell
cell.iconImageView.isHidden = true
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
}

View File

@ -0,0 +1,115 @@
//
// FAWalletViewController.swift
// Fableon
//
// Created by 鸿 on 2025/10/22.
//
import UIKit
class FAWalletViewController: FAViewController {
private lazy var dataArr: [FAMeItemModel] = [
FAMeItemModel(type: .consumptionRecords, name: "Consumption Records".localized),
FAMeItemModel(type: .purchaseRecords, name: "Purchase Records".localized),
FAMeItemModel(type: .rewardCoins, name: "Reward Coins".localized),
]
private lazy var tableView: FATableView = {
let tableView = FATableView(frame: .zero, style: .plain)
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = 46
tableView.separatorColor = .FFFFFF.withAlphaComponent(0.1)
tableView.separatorInset = .init(top: 0, left: 32, bottom: 0, right: 32)
tableView.register(UINib(nibName: "FAWalletCell", bundle: nil), forCellReuseIdentifier: "cell")
return tableView
}()
private lazy var headerView: FAWalletHeaderView = {
let view = FAWalletHeaderView(frame: .init(x: 0, y: 0, width: UIScreen.width, height: 190))
view.userInfo = FALogin.manager.userInfo
return view
}()
deinit {
NotificationCenter.default.removeObserver(self)
}
override func viewDidLoad() {
super.viewDidLoad()
self.title = "My Wallet".localized
NotificationCenter.default.addObserver(self, selector: #selector(userInfoUpdateNotification), name: FALogin.userInfoUpdateNotification, object: nil)
fa_setupLayout()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
fa_setNavigationStyle()
}
@objc private func userInfoUpdateNotification() {
headerView.userInfo = FALogin.manager.userInfo
}
}
extension FAWalletViewController {
private func fa_setupLayout() {
tableView.tableHeaderView = self.headerView
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(UIScreen.navBarHeight)
}
}
}
//MARK: UITableViewDelegate UITableViewDataSource
extension FAWalletViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FAWalletCell
cell.item = dataArr[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArr.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = dataArr[indexPath.row]
var vc: FAViewController?
switch item.type {
case .consumptionRecords:
vc = FAConsumptionRecordsViewController()
case .purchaseRecords:
vc = FAOrderRecordsViewController()
case .rewardCoins:
vc = FARewardCoinsViewController()
default:
break
}
if let vc = vc {
self.navigationController?.pushViewController(vc, animated: true)
}
}
}

View File

@ -0,0 +1,37 @@
//
// FAConsumptionRecordsCell.swift
// Fableon
//
// Created by 鸿 on 2025/10/23.
//
import UIKit
class FAConsumptionRecordsCell: FATableViewCell {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var subtitleLabel: UILabel!
@IBOutlet weak var dateLabel: UILabel!
@IBOutlet weak var coinsLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
titleLabel.text = "Purchase Single Episode dafdaaafafaafdasfadfsafdafadsadfas";
subtitleLabel.text = "Ep.8 Romantic Flash Marriage In Progress"
dateLabel.text = "2024-6-10 23:41:18"
coinsLabel.text = "-10 Coins"
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="78" id="KGk-i7-Jjw" customClass="FAConsumptionRecordsCell" customModule="Fableon" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="878" height="78"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="878" height="78"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0Qg-wC-QCD">
<rect key="frame" x="34" y="16" width="31.666666666666671" height="14"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="12"/>
<color key="textColor" name="#FFFFFF"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lf9-w0-dNJ">
<rect key="frame" x="34" y="47" width="31" height="15"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" name="#999999"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="1000" verticalHuggingPriority="251" horizontalCompressionResistancePriority="1000" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IuW-ER-ctV">
<rect key="frame" x="813" y="16" width="31" height="14"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" name="#999999"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="1000" verticalHuggingPriority="251" horizontalCompressionResistancePriority="1000" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xFu-h2-S6t">
<rect key="frame" x="809" y="47.333333333333336" width="35" height="14.333333333333336"/>
<fontDescription key="fontDescription" type="system" weight="black" pointSize="12"/>
<color key="textColor" name="#74BBFF"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="xFu-h2-S6t" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="lf9-w0-dNJ" secondAttribute="trailing" constant="60" id="3pp-Hv-jjx"/>
<constraint firstItem="0Qg-wC-QCD" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="34" id="7Sa-Zu-Jht"/>
<constraint firstAttribute="trailing" secondItem="xFu-h2-S6t" secondAttribute="trailing" constant="34" id="A4h-wE-hHi"/>
<constraint firstAttribute="trailing" secondItem="IuW-ER-ctV" secondAttribute="trailing" constant="34" id="EY7-Rl-2pK"/>
<constraint firstItem="0Qg-wC-QCD" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="16" id="NZF-V8-Ahh"/>
<constraint firstItem="IuW-ER-ctV" firstAttribute="centerY" secondItem="0Qg-wC-QCD" secondAttribute="centerY" id="TpD-Pr-GkC"/>
<constraint firstAttribute="bottom" secondItem="lf9-w0-dNJ" secondAttribute="bottom" constant="16" id="aOU-nd-Yba"/>
<constraint firstItem="IuW-ER-ctV" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="0Qg-wC-QCD" secondAttribute="trailing" constant="30" id="f03-XN-emY"/>
<constraint firstItem="lf9-w0-dNJ" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="34" id="sL6-f0-Z2v"/>
<constraint firstItem="xFu-h2-S6t" firstAttribute="centerY" secondItem="lf9-w0-dNJ" secondAttribute="centerY" id="yo1-GK-eyN"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="coinsLabel" destination="xFu-h2-S6t" id="WIS-W6-IhA"/>
<outlet property="dateLabel" destination="IuW-ER-ctV" id="Oun-nw-9d5"/>
<outlet property="subtitleLabel" destination="lf9-w0-dNJ" id="fAw-4D-Yxg"/>
<outlet property="titleLabel" destination="0Qg-wC-QCD" id="6sV-aY-AF6"/>
</connections>
<point key="canvasLocation" x="925.19083969465646" y="53.521126760563384"/>
</tableViewCell>
</objects>
<resources>
<namedColor name="#74BBFF">
<color red="0.45490196078431372" green="0.73333333333333328" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="#999999">
<color red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="#FFFFFF">
<color red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

View File

@ -0,0 +1,33 @@
//
// FAOrderRecordCell.swift
// Fableon
//
// Created by 鸿 on 2025/10/23.
//
import UIKit
class FAOrderRecordCell: FATableViewCell {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var dateLabel: UILabel!
@IBOutlet weak var countLabel: UILabel!
@IBOutlet weak var iconImageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="81" id="KGk-i7-Jjw" customClass="FAOrderRecordCell" customModule="Fableon" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="379" height="81"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="379" height="81"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1Q0-xP-S3w">
<rect key="frame" x="31.999999999999996" y="17" width="36.666666666666657" height="17"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="14"/>
<color key="textColor" name="#FFFFFF"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="OVF-ZK-B4x">
<rect key="frame" x="32" y="49.666666666666664" width="31" height="14.333333333333336"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" name="#999999"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="L8z-xW-Sg5">
<rect key="frame" x="307" y="32" width="40" height="17"/>
<fontDescription key="fontDescription" type="system" weight="black" pointSize="14"/>
<color key="textColor" name="#74BBFF"/>
<nil key="highlightedColor"/>
</label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="coins_icon_03" translatesAutoresizingMaskIntoConstraints="NO" id="pco-tO-eSC">
<rect key="frame" x="289" y="33.666666666666664" width="14" height="14"/>
</imageView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="L8z-xW-Sg5" secondAttribute="trailing" constant="32" id="4C1-LR-UXd"/>
<constraint firstAttribute="bottom" secondItem="OVF-ZK-B4x" secondAttribute="bottom" constant="17" id="BY7-of-buV"/>
<constraint firstItem="OVF-ZK-B4x" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="32" id="EO3-Zf-zHi"/>
<constraint firstItem="1Q0-xP-S3w" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="17" id="Ppz-xm-p7R"/>
<constraint firstItem="pco-tO-eSC" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="SJG-aY-VPm"/>
<constraint firstItem="L8z-xW-Sg5" firstAttribute="leading" secondItem="pco-tO-eSC" secondAttribute="trailing" constant="4" id="XWi-cM-bca"/>
<constraint firstItem="1Q0-xP-S3w" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="32" id="bRm-qi-1Pd"/>
<constraint firstItem="L8z-xW-Sg5" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="uCA-8Y-gx7"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="countLabel" destination="L8z-xW-Sg5" id="QGO-va-hKz"/>
<outlet property="dateLabel" destination="OVF-ZK-B4x" id="0Cr-xn-8Bb"/>
<outlet property="iconImageView" destination="pco-tO-eSC" id="UWj-Ao-NQn"/>
<outlet property="titleLabel" destination="1Q0-xP-S3w" id="dWF-T9-buC"/>
</connections>
<point key="canvasLocation" x="183.96946564885496" y="54.577464788732399"/>
</tableViewCell>
</objects>
<resources>
<image name="coins_icon_03" width="14" height="14"/>
<namedColor name="#74BBFF">
<color red="0.45490196078431372" green="0.73333333333333328" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="#999999">
<color red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="#FFFFFF">
<color red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

View File

@ -0,0 +1,43 @@
//
// FARewardCoinsCell.swift
// Fableon
//
// Created by 鸿 on 2025/10/23.
//
import UIKit
class FARewardCoinsCell: FATableViewCell {
@IBOutlet weak var dateLabel: UILabel!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var expiresIconImageView: UIImageView!
@IBOutlet weak var expiresLabel: UILabel!
@IBOutlet weak var expiredLabel: UILabel!
@IBOutlet weak var countLabel: UILabel!
@IBOutlet weak var remainingLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
expiredLabel.text = "Expired".localized
expiredLabel.isHidden = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="109" id="KGk-i7-Jjw" customClass="FARewardCoinsCell" customModule="Fableon" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="367" height="109"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="367" height="109"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QaO-WU-u9r">
<rect key="frame" x="32" y="15" width="31" height="14.333333333333336"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" name="#999999"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="n33-4s-wzC">
<rect key="frame" x="31.999999999999996" y="46" width="36.666666666666657" height="17"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="14"/>
<color key="textColor" name="#FFFFFF"/>
<nil key="highlightedColor"/>
</label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="time icon" translatesAutoresizingMaskIntoConstraints="NO" id="UAN-Fu-NVc">
<rect key="frame" x="32" y="82" width="12" height="12"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="70n-hD-JF8">
<rect key="frame" x="48" y="81" width="31" height="14.333333333333329"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" name="#74BBFF"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Oig-Ch-QGH">
<rect key="frame" x="295" y="26" width="40" height="17"/>
<fontDescription key="fontDescription" type="system" weight="black" pointSize="14"/>
<color key="textColor" name="#74BBFF"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jAC-rE-uKr">
<rect key="frame" x="304" y="77.666666666666671" width="31" height="14.333333333333329"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" name="#8B8B8B"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Expired" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uKs-9u-8Of">
<rect key="frame" x="31.999999999999996" y="81" width="42.666666666666643" height="14.333333333333329"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" name="#8B8B8B"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="Oig-Ch-QGH" secondAttribute="trailing" constant="32" id="3ez-Vh-RVN"/>
<constraint firstItem="UAN-Fu-NVc" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="32" id="85i-RW-mTA"/>
<constraint firstItem="Oig-Ch-QGH" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="26" id="8lJ-j7-0EM"/>
<constraint firstItem="70n-hD-JF8" firstAttribute="centerY" secondItem="UAN-Fu-NVc" secondAttribute="centerY" id="CK6-ov-VSa"/>
<constraint firstItem="70n-hD-JF8" firstAttribute="leading" secondItem="UAN-Fu-NVc" secondAttribute="trailing" constant="4" id="FLN-Sf-dOW"/>
<constraint firstItem="n33-4s-wzC" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="32" id="OJi-3y-oKv"/>
<constraint firstAttribute="trailing" secondItem="jAC-rE-uKr" secondAttribute="trailing" constant="32" id="TVI-ld-6BY"/>
<constraint firstItem="QaO-WU-u9r" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="15" id="dAY-I7-CyX"/>
<constraint firstItem="QaO-WU-u9r" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="32" id="mRS-pB-qcP"/>
<constraint firstAttribute="bottom" secondItem="jAC-rE-uKr" secondAttribute="bottom" constant="17" id="mtt-fe-fmp"/>
<constraint firstItem="uKs-9u-8Of" firstAttribute="centerY" secondItem="UAN-Fu-NVc" secondAttribute="centerY" id="nLp-Xx-ouo"/>
<constraint firstItem="uKs-9u-8Of" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="32" id="npS-od-aJU"/>
<constraint firstAttribute="bottom" secondItem="UAN-Fu-NVc" secondAttribute="bottom" constant="15" id="sRE-Oe-n90"/>
<constraint firstItem="n33-4s-wzC" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="xJm-u9-7Ra"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="countLabel" destination="Oig-Ch-QGH" id="jif-nc-jHk"/>
<outlet property="dateLabel" destination="QaO-WU-u9r" id="fzy-pA-2hL"/>
<outlet property="expiredLabel" destination="uKs-9u-8Of" id="zXO-tL-2TQ"/>
<outlet property="expiresIconImageView" destination="UAN-Fu-NVc" id="co1-QO-DHe"/>
<outlet property="expiresLabel" destination="70n-hD-JF8" id="tb7-to-JnU"/>
<outlet property="nameLabel" destination="n33-4s-wzC" id="RoK-PM-5Jm"/>
<outlet property="remainingLabel" destination="jAC-rE-uKr" id="tFc-78-oNw"/>
</connections>
<point key="canvasLocation" x="174.80916030534351" y="64.436619718309856"/>
</tableViewCell>
</objects>
<resources>
<image name="time icon" width="12" height="12"/>
<namedColor name="#74BBFF">
<color red="0.45490196078431372" green="0.73333333333333328" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="#8B8B8B">
<color red="0.54509803921568623" green="0.54509803921568623" blue="0.54509803921568623" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="#999999">
<color red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="#FFFFFF">
<color red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

View File

@ -0,0 +1,33 @@
//
// FAWalletCell.swift
// Fableon
//
// Created by 鸿 on 2025/10/22.
//
import UIKit
class FAWalletCell: FATableViewCell {
var item: FAMeItemModel? {
didSet {
titleLabel.text = item?.name
}
}
@IBOutlet weak var titleLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="FAWalletCell" customModule="Fableon" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dNW-KD-5Wv">
<rect key="frame" x="32" y="15" width="33" height="14.333333333333336"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="12"/>
<color key="textColor" name="#FFFFFF"/>
<nil key="highlightedColor"/>
</label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Frame 3015" translatesAutoresizingMaskIntoConstraints="NO" id="5vD-3A-9IP">
<rect key="frame" x="278" y="17" width="10" height="10"/>
</imageView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="5vD-3A-9IP" secondAttribute="trailing" constant="32" id="7YN-X5-1W8"/>
<constraint firstItem="dNW-KD-5Wv" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="32" id="SRC-mm-4up"/>
<constraint firstItem="dNW-KD-5Wv" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="fyz-P4-xJH"/>
<constraint firstItem="5vD-3A-9IP" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="dNW-KD-5Wv" secondAttribute="trailing" constant="70" id="peE-kR-BX3"/>
<constraint firstItem="5vD-3A-9IP" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="wP0-9k-9tW"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="titleLabel" destination="dNW-KD-5Wv" id="Mhc-0s-z9j"/>
</connections>
<point key="canvasLocation" x="139" y="42"/>
</tableViewCell>
</objects>
<resources>
<image name="Frame 3015" width="10" height="10"/>
<namedColor name="#FFFFFF">
<color red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

View File

@ -0,0 +1,158 @@
//
// FAWalletHeaderView.swift
// Fableon
//
// Created by 鸿 on 2025/10/22.
//
import UIKit
class FAWalletHeaderView: UIView {
var userInfo: FAUserInfo? {
didSet {
coinsView.setNeedsUpdateConfiguration()
bonusView.setNeedsUpdateConfiguration()
}
}
private lazy var contentView: UIView = {
let view = UIView()
view.backgroundColor = ._5_CA_8_FF_0_2
view.layer.cornerRadius = 12
view.layer.masksToBounds = true
return view
}()
private lazy var storeButton: UIButton = {
let button = FAGradientButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
let vc = FAStoreViewController()
self.viewController?.navigationController?.pushViewController(vc, animated: true)
}))
button.layer.cornerRadius = 24
button.layer.masksToBounds = true
button.fa_colors = [UIColor.BEDFFF.cgColor, UIColor._52_A_2_F_1.cgColor]
button.fa_locations = [0, 1]
button.fa_startPoint = .init(x: 0, y: 0.5)
button.fa_endPoint = .init(x: 1, y: 0.5)
button.setTitle("Store".localized, for: .normal)
button.setTitleColor(._000000, for: .normal)
button.titleLabel?.font = .font(ofSize: 18, weight: .bold)
return button
}()
private lazy var lineView: UIView = {
let view = UIView()
view.backgroundColor = .FFFFFF.withAlphaComponent(0.1)
return view
}()
private lazy var coinsView: UIButton = {
var config = UIButton.Configuration.plain()
config.image = UIImage(named: "coins_icon_02")
config.imagePadding = 11
config.attributedTitle = AttributedString("Coins".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 10, weight: .bold),
.foregroundColor : UIColor.F_7_F_497
]))
let button = UIButton(configuration: config)
button.isUserInteractionEnabled = false
button.configurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
button.configuration?.attributedSubtitle = AttributedString("\(self.userInfo?.coin_left_total ?? 0)", attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor.FFFFFF
]))
}
return button
}()
private lazy var bonusView: UIButton = {
var config = UIButton.Configuration.plain()
config.image = UIImage(named: "coins_icon_02")
config.imagePadding = 11
config.imagePlacement = .trailing
config.titleAlignment = .leading
config.attributedTitle = AttributedString("Bonus".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 10, weight: .bold),
.foregroundColor : UIColor.F_7_F_497
]))
let button = UIButton(configuration: config)
button.isUserInteractionEnabled = false
button.configurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
button.configuration?.attributedSubtitle = AttributedString("\(self.userInfo?.send_coin_left_total ?? 0)", attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor.FFFFFF
]))
}
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension FAWalletHeaderView {
private func fa_setupLayout() {
addSubview(contentView)
contentView.addSubview(storeButton)
contentView.addSubview(lineView)
contentView.addSubview(coinsView)
contentView.addSubview(bonusView)
contentView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(25)
make.height.equalTo(150)
}
storeButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerX.equalToSuperview()
make.bottom.equalToSuperview().offset(-14)
make.height.equalTo(48)
}
lineView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(33)
make.width.equalTo(1)
make.height.equalTo(30)
}
coinsView.snp.makeConstraints { make in
make.left.equalToSuperview()
make.right.equalTo(lineView.snp.left)
make.centerY.equalTo(lineView)
}
bonusView.snp.makeConstraints { make in
make.left.equalTo(lineView.snp.right)
make.right.equalToSuperview()
make.centerY.equalTo(lineView)
}
}
}

View File

@ -15,6 +15,10 @@ class FAUserInfo: NSObject, SmartCodable, NSSecureCoding {
var is_tourist: Bool?
var family_name: String?
var avator: String?
var coin_left_total: Int?
var send_coin_left_total: Int?
var is_vip: Bool?
var vip_end_time: TimeInterval?
func getNickName() -> String {
if let name = family_name, !name.isEmpty {
@ -24,6 +28,10 @@ class FAUserInfo: NSObject, SmartCodable, NSSecureCoding {
}
}
var totalCoins: Int {
return (coin_left_total ?? 0) + (send_coin_left_total ?? 0)
}
required override init() { }
static var supportsSecureCoding: Bool {
@ -36,6 +44,10 @@ class FAUserInfo: NSObject, SmartCodable, NSSecureCoding {
coder.encode(is_tourist, forKey: "is_tourist")
coder.encode(avator, forKey: "avator")
coder.encode(family_name, forKey: "family_name")
coder.encode(coin_left_total, forKey: "coin_left_total")
coder.encode(send_coin_left_total, forKey: "send_coin_left_total")
coder.encode(is_vip, forKey: "is_vip")
coder.encode(vip_end_time, forKey: "vip_end_time")
}
required init?(coder: NSCoder) {
@ -45,5 +57,9 @@ class FAUserInfo: NSObject, SmartCodable, NSSecureCoding {
is_tourist = coder.decodeObject(of: NSNumber.self, forKey: "is_tourist")?.boolValue
avator = coder.decodeObject(of: NSString.self, forKey: "avator") as? String
family_name = coder.decodeObject(of: NSString.self, forKey: "family_name") as? String
coin_left_total = coder.decodeObject(of: NSNumber.self, forKey: "coin_left_total")?.intValue
send_coin_left_total = coder.decodeObject(of: NSNumber.self, forKey: "send_coin_left_total")?.intValue
is_vip = coder.decodeObject(of: NSNumber.self, forKey: "is_vip")?.boolValue
vip_end_time = coder.decodeObject(of: NSNumber.self, forKey: "vip_end_time")?.doubleValue
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x91",
"green" : "0x5C",
"red" : "0x10"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFB",
"green" : "0x80",
"red" : "0x16"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x1E",
"green" : "0x27",
"red" : "0x3A"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFF",
"green" : "0xBB",
"red" : "0x74"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xDB",
"green" : "0xA7",
"red" : "0x79"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xF5",
"green" : "0xDB",
"red" : "0x7F"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFF",
"green" : "0xFA",
"red" : "0xC3"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x97",
"green" : "0xF4",
"red" : "0xF7"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.800",
"blue" : "0xFF",
"green" : "0xFF",
"red" : "0xFF"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,25 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame 2085663258@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame 2085663258@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "original"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 B

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame 3021@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame 3021@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 B

View 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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View 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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View 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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame 2958@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame 2958@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "time icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "time icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 770 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Some files were not shown because too many files have changed in this diff Show More