diff --git a/YuMi.xcodeproj/project.pbxproj b/YuMi.xcodeproj/project.pbxproj index 783cd26..4e04be9 100644 --- a/YuMi.xcodeproj/project.pbxproj +++ b/YuMi.xcodeproj/project.pbxproj @@ -442,6 +442,11 @@ 4C520D882D89A78C0051C784 /* VisitorListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C520D872D89A78C0051C784 /* VisitorListViewController.m */; }; 4C5527BC2D1BDCDE00833FFD /* RoomLevelInfoModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C5527BB2D1BDCDE00833FFD /* RoomLevelInfoModel.m */; }; 4C5527BF2D1C099500833FFD /* RoomResourceManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C5527BE2D1C099500833FFD /* RoomResourceManager.m */; }; + 4C59C0FD2EA2508F00D1F7BD /* EPFriendFollowingFans.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C59C0F82EA2508F00D1F7BD /* EPFriendFollowingFans.swift */; }; + 4C59C0FE2EA2508F00D1F7BD /* EPMessageSegmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C59C0FB2EA2508F00D1F7BD /* EPMessageSegmentView.swift */; }; + 4C59C0FF2EA2508F00D1F7BD /* EPMessageMainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C59C0FA2EA2508F00D1F7BD /* EPMessageMainViewController.swift */; }; + 4C59C1002EA2508F00D1F7BD /* EPBaseListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C59C0F72EA2508F00D1F7BD /* EPBaseListViewController.swift */; }; + 4C59C1012EA2508F00D1F7BD /* EPMessageListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C59C0F92EA2508F00D1F7BD /* EPMessageListVC.swift */; }; 4C5C37232D0C1C7900BA9AB8 /* RegionListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C5C37222D0C1C7900BA9AB8 /* RegionListViewController.m */; }; 4C6C92C02D1172D9000A4693 /* RegionListInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C6C92BF2D1172D9000A4693 /* RegionListInfo.m */; }; 4C6E1F752CEAEC3C0073D0A3 /* ShoppingMallTagView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C6E1F742CEAEC3C0073D0A3 /* ShoppingMallTagView.m */; }; @@ -884,7 +889,7 @@ 54FFD3832C9BD12600DE61E5 /* 5.svga in Resources */ = {isa = PBXBuildFile; fileRef = 54FFD37F2C9BD12600DE61E5 /* 5.svga */; }; 54FFD3842C9BD12600DE61E5 /* 3.svga in Resources */ = {isa = PBXBuildFile; fileRef = 54FFD37D2C9BD12600DE61E5 /* 3.svga */; }; 54FFD3852C9BD12600DE61E5 /* 2.svga in Resources */ = {isa = PBXBuildFile; fileRef = 54FFD37C2C9BD12600DE61E5 /* 2.svga */; }; - 73FFADDC93E195344047A2EC /* Pods_YuMi.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CACF623970097D653132D69A /* Pods_YuMi.framework */; }; + 6996748A711C8E6931880AD0 /* Pods_YuMi.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3148BBD005110C1B57D04595 /* Pods_YuMi.framework */; }; 9B0086C627BA392B0032BD2B /* AnchorStageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B0086C527BA392B0032BD2B /* AnchorStageView.m */; }; 9B0086CA27BA4F570032BD2B /* AnchorMicroView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B0086C927BA4F570032BD2B /* AnchorMicroView.m */; }; 9B044DA0282D32F700DE4859 /* MicroInviteExtModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B044D9F282D32F700DE4859 /* MicroInviteExtModel.m */; }; @@ -2431,6 +2436,7 @@ 23FF42782AA6E19C0055733C /* HomeMenuSourceModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HomeMenuSourceModel.m; sourceTree = ""; }; 23FF428C2AAB2D3A0055733C /* XPCandyTreeBuyView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPCandyTreeBuyView.h; sourceTree = ""; }; 23FF428D2AAB2D3A0055733C /* XPCandyTreeBuyView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPCandyTreeBuyView.m; sourceTree = ""; }; + 3148BBD005110C1B57D04595 /* Pods_YuMi.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_YuMi.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4C0642722E97BD6D00BAF413 /* EPMineHeaderView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPMineHeaderView.h; sourceTree = ""; }; 4C0642732E97BD6D00BAF413 /* EPMineHeaderView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EPMineHeaderView.m; sourceTree = ""; }; 4C0642792E97BD6D00BAF413 /* EPMomentCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPMomentCell.h; sourceTree = ""; }; @@ -2527,6 +2533,11 @@ 4C5527BB2D1BDCDE00833FFD /* RoomLevelInfoModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RoomLevelInfoModel.m; sourceTree = ""; }; 4C5527BD2D1C099500833FFD /* RoomResourceManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoomResourceManager.h; sourceTree = ""; }; 4C5527BE2D1C099500833FFD /* RoomResourceManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RoomResourceManager.m; sourceTree = ""; }; + 4C59C0F72EA2508F00D1F7BD /* EPBaseListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPBaseListViewController.swift; sourceTree = ""; }; + 4C59C0F82EA2508F00D1F7BD /* EPFriendFollowingFans.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPFriendFollowingFans.swift; sourceTree = ""; }; + 4C59C0F92EA2508F00D1F7BD /* EPMessageListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPMessageListVC.swift; sourceTree = ""; }; + 4C59C0FA2EA2508F00D1F7BD /* EPMessageMainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPMessageMainViewController.swift; sourceTree = ""; }; + 4C59C0FB2EA2508F00D1F7BD /* EPMessageSegmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPMessageSegmentView.swift; sourceTree = ""; }; 4C5C37212D0C1C7900BA9AB8 /* RegionListViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RegionListViewController.h; sourceTree = ""; }; 4C5C37222D0C1C7900BA9AB8 /* RegionListViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RegionListViewController.m; sourceTree = ""; }; 4C6C92BE2D1172D9000A4693 /* RegionListInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RegionListInfo.h; sourceTree = ""; }; @@ -2982,8 +2993,8 @@ 4CD19EAD2E9CDFC30069DAA0 /* EPLoginInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPLoginInputView.swift; sourceTree = ""; }; 4CD19EB02E9D12600069DAA0 /* EPEditSettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPEditSettingViewController.swift; sourceTree = ""; }; 4CD19EB22E9D141A0069DAA0 /* EPMineViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPMineViewController.h; sourceTree = ""; }; - 4CD19EB62E9D15000069DAA0 /* EPAboutUsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPAboutUsViewController.swift; sourceTree = ""; }; 4CD19EB32E9D141A0069DAA0 /* EPMineViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EPMineViewController.m; sourceTree = ""; }; + 4CD19EB62E9D15000069DAA0 /* EPAboutUsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPAboutUsViewController.swift; sourceTree = ""; }; 4CD401452E7183A8003F5009 /* XPPartyRoomItemCollectionViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPPartyRoomItemCollectionViewCell.h; sourceTree = ""; }; 4CD401462E7183A8003F5009 /* XPPartyRoomItemCollectionViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPPartyRoomItemCollectionViewCell.m; sourceTree = ""; }; 4CD401482E718E36003F5009 /* XPBlankRoomModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPBlankRoomModel.h; sourceTree = ""; }; @@ -3209,7 +3220,7 @@ 54FFD37D2C9BD12600DE61E5 /* 3.svga */ = {isa = PBXFileReference; lastKnownFileType = file; path = 3.svga; sourceTree = ""; }; 54FFD37E2C9BD12600DE61E5 /* 4.svga */ = {isa = PBXFileReference; lastKnownFileType = file; path = 4.svga; sourceTree = ""; }; 54FFD37F2C9BD12600DE61E5 /* 5.svga */ = {isa = PBXFileReference; lastKnownFileType = file; path = 5.svga; sourceTree = ""; }; - 7DB00EC07F1D0ADFF900B38D /* Pods-YuMi.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YuMi.debug.xcconfig"; path = "Target Support Files/Pods-YuMi/Pods-YuMi.debug.xcconfig"; sourceTree = ""; }; + 56B26C27109C1DD2002D0624 /* Pods-YuMi.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YuMi.release.xcconfig"; path = "Target Support Files/Pods-YuMi/Pods-YuMi.release.xcconfig"; sourceTree = ""; }; 9B0086C427BA392B0032BD2B /* AnchorStageView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AnchorStageView.h; sourceTree = ""; }; 9B0086C527BA392B0032BD2B /* AnchorStageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AnchorStageView.m; sourceTree = ""; }; 9B0086C827BA4F570032BD2B /* AnchorMicroView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AnchorMicroView.h; sourceTree = ""; }; @@ -3495,8 +3506,6 @@ 9BFE0D912899042600F53C24 /* XPTaskCompleteTipView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPTaskCompleteTipView.m; sourceTree = ""; }; 9BFE992C288142FD009DA429 /* RoomClassifyModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoomClassifyModel.h; sourceTree = ""; }; 9BFE992D288142FD009DA429 /* RoomClassifyModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RoomClassifyModel.m; sourceTree = ""; }; - B66633E061B1B34177CD011C /* Pods-YuMi.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YuMi.release.xcconfig"; path = "Target Support Files/Pods-YuMi/Pods-YuMi.release.xcconfig"; sourceTree = ""; }; - CACF623970097D653132D69A /* Pods_YuMi.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_YuMi.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E801273E27E323C800BAC3F2 /* XPRoomPKViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPRoomPKViewController.h; sourceTree = ""; }; E801273F27E323C800BAC3F2 /* XPRoomPKViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPRoomPKViewController.m; sourceTree = ""; }; E801274127E323E500BAC3F2 /* XPRoomPKPresenter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPRoomPKPresenter.h; sourceTree = ""; }; @@ -4803,6 +4812,7 @@ E8F65C1E286998C9009BB5B9 /* XPMineShareViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPMineShareViewController.m; sourceTree = ""; }; E8F65C202869A36F009BB5B9 /* ContentShareMonentsModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContentShareMonentsModel.h; sourceTree = ""; }; E8F65C212869A36F009BB5B9 /* ContentShareMonentsModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContentShareMonentsModel.m; sourceTree = ""; }; + ECF5B2AAE493057A3DBCCFAF /* Pods-YuMi.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YuMi.debug.xcconfig"; path = "Target Support Files/Pods-YuMi/Pods-YuMi.debug.xcconfig"; sourceTree = ""; }; F1D8556D2931FC86008C418F /* XPRoomYearActivityView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPRoomYearActivityView.h; sourceTree = ""; }; F1D8556E2931FC86008C418F /* XPRoomYearActivityView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPRoomYearActivityView.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -4821,11 +4831,11 @@ 237701082BCF73CE00D661F1 /* Security.framework in Frameworks */, 4CD15D922D7EC2AC00D9279F /* CoreTelephony.framework in Frameworks */, 234489082AC3C5DA0070E5D5 /* SudMGP.framework in Frameworks */, - 73FFADDC93E195344047A2EC /* Pods_YuMi.framework in Frameworks */, 186A531926FC592100D67B2C /* libresolv.tbd in Frameworks */, E87888F42738C30E00BF1D57 /* StoreKit.framework in Frameworks */, 9BA8A47727C60DF7000365A3 /* AVFoundation.framework in Frameworks */, 9BA8A47527C60D9F000365A3 /* AudioToolbox.framework in Frameworks */, + 6996748A711C8E6931880AD0 /* Pods_YuMi.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -6473,6 +6483,7 @@ 4C0642922E98EF0A00BAF413 /* E-P */ = { isa = PBXGroup; children = ( + 4C59C0FC2EA2508F00D1F7BD /* NewMessage */, 4CD19C852E9CB31C0069DAA0 /* NewLogin */, 4C1E98C22E9A45160031AE79 /* Common */, 4C0642752E97BD6D00BAF413 /* NewMine */, @@ -6532,6 +6543,18 @@ path = GZIP; sourceTree = ""; }; + 4C59C0FC2EA2508F00D1F7BD /* NewMessage */ = { + isa = PBXGroup; + children = ( + 4C59C0F72EA2508F00D1F7BD /* EPBaseListViewController.swift */, + 4C59C0F82EA2508F00D1F7BD /* EPFriendFollowingFans.swift */, + 4C59C0F92EA2508F00D1F7BD /* EPMessageListVC.swift */, + 4C59C0FA2EA2508F00D1F7BD /* EPMessageMainViewController.swift */, + 4C59C0FB2EA2508F00D1F7BD /* EPMessageSegmentView.swift */, + ); + path = NewMessage; + sourceTree = ""; + }; 4C7989F02D195293006AE07B /* RoomMode */ = { isa = PBXGroup; children = ( @@ -7965,7 +7988,7 @@ 23E56B3B2B03564B00C8DAC9 /* CoreTelephony.framework */, E87888F32738C30E00BF1D57 /* StoreKit.framework */, 186A531826FC591100D67B2C /* libresolv.tbd */, - CACF623970097D653132D69A /* Pods_YuMi.framework */, + 3148BBD005110C1B57D04595 /* Pods_YuMi.framework */, ); name = Frameworks; sourceTree = ""; @@ -7973,8 +7996,8 @@ D09C770DC30B9BAAEAFC7945 /* Pods */ = { isa = PBXGroup; children = ( - 7DB00EC07F1D0ADFF900B38D /* Pods-YuMi.debug.xcconfig */, - B66633E061B1B34177CD011C /* Pods-YuMi.release.xcconfig */, + ECF5B2AAE493057A3DBCCFAF /* Pods-YuMi.debug.xcconfig */, + 56B26C27109C1DD2002D0624 /* Pods-YuMi.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -11634,14 +11657,14 @@ isa = PBXNativeTarget; buildConfigurationList = 189DD54226DE255600AB55B1 /* Build configuration list for PBXNativeTarget "YuMi" */; buildPhases = ( - 1865B406E358C680125F108D /* [CP] Check Pods Manifest.lock */, + 1BE44CE1939801EC8D8A3B98 /* [CP] Check Pods Manifest.lock */, 189DD52526DE255300AB55B1 /* Sources */, 189DD52626DE255300AB55B1 /* Frameworks */, 189DD52726DE255300AB55B1 /* Resources */, - 8311720C3643AC2030B96510 /* [CP] Embed Pods Frameworks */, - 4C25F8F9E2D1F501119C383D /* [CP] Copy Pods Resources */, 18E7B21326E8CD220064BC9B /* Embed Frameworks */, 232C43E62AB0754700D4B2ED /* CopyFiles */, + 5D1CA1F69A65602D6A906867 /* [CP] Embed Pods Frameworks */, + 86EC598DD301B1F442E90F99 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -11937,7 +11960,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 1865B406E358C680125F108D /* [CP] Check Pods Manifest.lock */ = { + 1BE44CE1939801EC8D8A3B98 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -11959,28 +11982,7 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 4C25F8F9E2D1F501119C383D /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-YuMi/Pods-YuMi-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-YuMi/Pods-YuMi-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-YuMi/Pods-YuMi-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 8311720C3643AC2030B96510 /* [CP] Embed Pods Frameworks */ = { + 5D1CA1F69A65602D6A906867 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -12001,6 +12003,27 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-YuMi/Pods-YuMi-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + 86EC598DD301B1F442E90F99 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-YuMi/Pods-YuMi-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-YuMi/Pods-YuMi-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-YuMi/Pods-YuMi-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -13068,6 +13091,11 @@ E87AE7FC277AAC450037823A /* XPRoomTagPresenter.m in Sources */, E85E7B652A4EC35A00B6D00A /* XPExchangeDiamondsModel.m in Sources */, E878894C273A607C00BF1D57 /* XPGiftUserCollectionViewCell.m in Sources */, + 4C59C0FD2EA2508F00D1F7BD /* EPFriendFollowingFans.swift in Sources */, + 4C59C0FE2EA2508F00D1F7BD /* EPMessageSegmentView.swift in Sources */, + 4C59C0FF2EA2508F00D1F7BD /* EPMessageMainViewController.swift in Sources */, + 4C59C1002EA2508F00D1F7BD /* EPBaseListViewController.swift in Sources */, + 4C59C1012EA2508F00D1F7BD /* EPMessageListVC.swift in Sources */, 23E9E9A72A80F1C300B792F2 /* XPNewMineHallIncomeVC.m in Sources */, 2331C1712A5EB71000E1D940 /* XPNobleCenterTableHeadView.m in Sources */, 181D7F212727D9DB00B7C059 /* SocialStageView.m in Sources */, @@ -13457,6 +13485,7 @@ 189DD54026DE255600AB55B1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -13516,6 +13545,7 @@ 189DD54126DE255600AB55B1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -13568,7 +13598,7 @@ }; 189DD54326DE255600AB55B1 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7DB00EC07F1D0ADFF900B38D /* Pods-YuMi.debug.xcconfig */; + baseConfigurationReference = ECF5B2AAE493057A3DBCCFAF /* Pods-YuMi.debug.xcconfig */; buildSettings = { APP_DISPLAY_NAME = "E-Party DEBUG"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -13583,12 +13613,8 @@ ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/YuMi/Main/RTC/Library", - "$(PROJECT_DIR)/YuMi/Modules/YMRTC/Library", - "$(PROJECT_DIR)/YuMi/Resources/Client", "$(PROJECT_DIR)/YuMi/Library", "$(PROJECT_DIR)/YuMi/Tools", - "$(PROJECT_DIR)/YuMi/Tools/TencentOpenApiSDK", ); GCC_PREFIX_HEADER = YuMi/Structure/PrefixHeader.pch; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -13830,7 +13856,7 @@ }; 189DD54426DE255600AB55B1 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B66633E061B1B34177CD011C /* Pods-YuMi.release.xcconfig */; + baseConfigurationReference = 56B26C27109C1DD2002D0624 /* Pods-YuMi.release.xcconfig */; buildSettings = { APP_DISPLAY_NAME = "E-Party"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -13844,12 +13870,8 @@ ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/YuMi/Main/RTC/Library", - "$(PROJECT_DIR)/YuMi/Modules/YMRTC/Library", - "$(PROJECT_DIR)/YuMi/Resources/Client", "$(PROJECT_DIR)/YuMi/Library", "$(PROJECT_DIR)/YuMi/Tools", - "$(PROJECT_DIR)/YuMi/Tools/TencentOpenApiSDK", ); GCC_PREFIX_HEADER = YuMi/Structure/PrefixHeader.pch; INFOPLIST_FILE = YuMi/Info.plist; diff --git a/YuMi/Appdelegate/AppDelegate.h b/YuMi/Appdelegate/AppDelegate.h index efa80bf..d4ddd22 100644 --- a/YuMi/Appdelegate/AppDelegate.h +++ b/YuMi/Appdelegate/AppDelegate.h @@ -6,15 +6,10 @@ // #import -#import + @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; -@property(nonatomic,strong,readonly)NSManagedObjectContext *managedObjectContext; -@property(nonatomic,strong,readonly)NSManagedObjectModel *managedObjectModel; -@property(nonatomic,strong,readonly)NSPersistentStoreCoordinator *persistentStoreCoordinator; -- (void)saveContext; -- (NSURL *)applicationDocumentsDirectory; @end diff --git a/YuMi/Appdelegate/AppDelegate.m b/YuMi/Appdelegate/AppDelegate.m index 62c37d0..c5da6c7 100644 --- a/YuMi/Appdelegate/AppDelegate.m +++ b/YuMi/Appdelegate/AppDelegate.m @@ -342,83 +342,4 @@ void qg_VAP_Logger_handler(VAPLogLevel level, const char* file, int line, const // } //} -#pragma mark - Core Data stack -@synthesize managedObjectContext = _managedObjectContext; -@synthesize managedObjectModel = _managedObjectModel; -@synthesize persistentStoreCoordinator = _persistentStoreCoordinator; - --(NSURL *)applicationDocumentsDirectory{ - return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; -} - -- (NSManagedObjectModel *)managedObjectModel { - // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model. - if (_managedObjectModel != nil) { - return _managedObjectModel; - } - NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"_1_______" withExtension:@"momd"]; - _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; - return _managedObjectModel; -} - -- (NSPersistentStoreCoordinator *)persistentStoreCoordinator { - // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. - if (_persistentStoreCoordinator != nil) { - return _persistentStoreCoordinator; - } - - // Create the coordinator and store - - _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; - NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"_1_______.sqlite"]; - NSError *error = nil; - NSString *failureReason = @"There was an error creating or loading the application's saved data."; - if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { - // Report any error we got. - NSMutableDictionary *dict = [NSMutableDictionary dictionary]; - dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data"; - dict[NSLocalizedFailureReasonErrorKey] = failureReason; - dict[NSUnderlyingErrorKey] = error; - error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict]; - // Replace this with code to handle the error appropriately. - // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. -// NSLog(@"Unresolved error %@, %@", error, [error userInfo]); - abort(); - } - - return _persistentStoreCoordinator; -} - - -- (NSManagedObjectContext *)managedObjectContext { - // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) - if (_managedObjectContext != nil) { - return _managedObjectContext; - } - - NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; - if (!coordinator) { - return nil; - } - _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; - [_managedObjectContext setPersistentStoreCoordinator:coordinator]; - return _managedObjectContext; -} - -#pragma mark - Core Data Saving support - -- (void)saveContext { - NSManagedObjectContext *managedObjectContext = self.managedObjectContext; - if (managedObjectContext != nil) { - NSError *error = nil; - if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) { - // Replace this implementation with code to handle the error appropriately. - // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. -// NSLog(@"Unresolved error %@, %@", error, [error userInfo]); - abort(); - } - } -} - - @end diff --git a/YuMi/E-P/NewMessage/EPBaseListViewController.swift b/YuMi/E-P/NewMessage/EPBaseListViewController.swift new file mode 100644 index 0000000..7e91aa7 --- /dev/null +++ b/YuMi/E-P/NewMessage/EPBaseListViewController.swift @@ -0,0 +1,54 @@ +// +// EPBaseListViewController.swift +// YuMi +// +// A lightweight table-view base class used by EP Message subpages. +// + +import UIKit +import SnapKit + +class EPBaseListViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { + let tableView = UITableView(frame: .zero, style: .plain) + var itemsCount: Int = 0 { didSet { tableView.reloadData() } } + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = UIColor(named: "ep.background.dark") ?? UIColor.black.withAlphaComponent(0.9) + + tableView.backgroundColor = .clear + tableView.separatorStyle = .none + tableView.showsVerticalScrollIndicator = false + tableView.dataSource = self + tableView.delegate = self + tableView.rowHeight = 72 + tableView.contentInsetAdjustmentBehavior = .never + tableView.keyboardDismissMode = .onDrag + tableView.register(Cell.self, forCellReuseIdentifier: "cell") + + view.addSubview(tableView) + tableView.snp.makeConstraints { make in + make.edges.equalTo(view.safeAreaLayoutGuide) + } + } + + // MARK: - UITableViewDataSource + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return itemsCount + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! Cell + cell.backgroundColor = .clear + return cell + } + + // MARK: - Helpers + func simulateItems(_ count: Int) { + itemsCount = count + } +} + + + + diff --git a/YuMi/E-P/NewMessage/EPFriendFollowingFans.swift b/YuMi/E-P/NewMessage/EPFriendFollowingFans.swift new file mode 100644 index 0000000..073a089 --- /dev/null +++ b/YuMi/E-P/NewMessage/EPFriendFollowingFans.swift @@ -0,0 +1,126 @@ +import UIKit + +final class EPFriendListVC: EPBaseListViewController { + override func viewDidLoad() { super.viewDidLoad(); simulateItems(6) } +} + +final class EPFollowingListVC: EPBaseListViewController { + override func viewDidLoad() { super.viewDidLoad(); simulateItems(10) } +} + +final class EPFansListVC: EPBaseListViewController { + override func viewDidLoad() { super.viewDidLoad(); simulateItems(12) } +} + +final class EPUserBriefCell: UITableViewCell { + private let avatar = UIImageView() + private let nameLabel = UILabel() + private let subtitleLabel = UILabel() + private let followButton = EPFollowButton() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + setup() + } + required init?(coder: NSCoder) { super.init(coder: coder); setup() } + + private func setup() { + selectionStyle = .none + backgroundColor = .clear + + avatar.contentMode = .scaleAspectFill + avatar.layer.cornerRadius = 24 + avatar.layer.masksToBounds = true + avatar.image = UIImage(named: "pi_login_new_logo") + + nameLabel.font = .systemFont(ofSize: 20, weight: .semibold) + nameLabel.textColor = .white + nameLabel.text = "Momoyy" + + subtitleLabel.font = .systemFont(ofSize: 16) + subtitleLabel.textColor = UIColor.white.withAlphaComponent(0.6) + subtitleLabel.text = "Welcome to play" + + contentView.addSubview(avatar) + contentView.addSubview(nameLabel) + contentView.addSubview(subtitleLabel) + contentView.addSubview(followButton) + + avatar.translatesAutoresizingMaskIntoConstraints = false + nameLabel.translatesAutoresizingMaskIntoConstraints = false + subtitleLabel.translatesAutoresizingMaskIntoConstraints = false + followButton.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + avatar.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), + avatar.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), + avatar.widthAnchor.constraint(equalToConstant: 48), + avatar.heightAnchor.constraint(equalToConstant: 48), + + nameLabel.leadingAnchor.constraint(equalTo: avatar.trailingAnchor, constant: 12), + nameLabel.topAnchor.constraint(equalTo: avatar.topAnchor, constant: -2), + + subtitleLabel.leadingAnchor.constraint(equalTo: nameLabel.leadingAnchor), + subtitleLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 6), + + followButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16), + followButton.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), + followButton.widthAnchor.constraint(equalToConstant: 120), + followButton.heightAnchor.constraint(equalToConstant: 40) + ]) + + followButton.setFollowed(false) + } +} + +final class EPFollowButton: UIButton { + private var isFollowedState: Bool = false + + override init(frame: CGRect) { super.init(frame: frame); setup() } + required init?(coder: NSCoder) { super.init(coder: coder); setup() } + + private func setup() { + titleLabel?.font = .systemFont(ofSize: 18, weight: .semibold) + layer.cornerRadius = 20 + layer.masksToBounds = true + addTarget(self, action: #selector(onTap), for: .touchUpInside) + } + + func setFollowed(_ followed: Bool) { + isFollowedState = followed + if followed { + setTitle("Followed", for: .normal) + backgroundColor = .clear + layer.borderWidth = 1 + layer.borderColor = UIColor.systemPurple.withAlphaComponent(0.6).cgColor + setTitleColor(UIColor.systemPurple.withAlphaComponent(0.9), for: .normal) + } else { + setTitle("Follow", for: .normal) + layer.borderWidth = 0 + setTitleColor(.white, for: .normal) + setGradientBackground() + } + } + + @objc private func onTap() { setFollowed(!isFollowedState) } + + private func setGradientBackground() { + let gradient = CAGradientLayer() + gradient.colors = [UIColor.systemPink.cgColor, UIColor.systemPurple.cgColor] + gradient.startPoint = CGPoint(x: 0, y: 0.5) + gradient.endPoint = CGPoint(x: 1, y: 0.5) + gradient.frame = bounds + gradient.cornerRadius = layer.cornerRadius + layer.sublayers?.removeAll(where: { $0 is CAGradientLayer }) + layer.insertSublayer(gradient, at: 0) + } + + override func layoutSubviews() { + super.layoutSubviews() + if !isFollowedState { setGradientBackground() } + } +} + + + + diff --git a/YuMi/E-P/NewMessage/EPMessageListVC.swift b/YuMi/E-P/NewMessage/EPMessageListVC.swift new file mode 100644 index 0000000..640c5aa --- /dev/null +++ b/YuMi/E-P/NewMessage/EPMessageListVC.swift @@ -0,0 +1,90 @@ +import UIKit + +final class EPMessageListVC: EPBaseListViewController { + override func viewDidLoad() { + super.viewDidLoad() + simulateItems(8) + } +} + +// MARK: - Cell +final class EPMessageCell: UITableViewCell { + private let avatar = UIImageView() + private let nameLabel = UILabel() + private let subtitleLabel = UILabel() + private let timeLabel = UILabel() + private let unreadView = UILabel() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + setup() + } + required init?(coder: NSCoder) { super.init(coder: coder); setup() } + + private func setup() { + selectionStyle = .none + backgroundColor = .clear + + avatar.contentMode = .scaleAspectFill + avatar.layer.cornerRadius = 24 + avatar.layer.masksToBounds = true + avatar.image = UIImage(named: "pi_login_new_logo") + + nameLabel.font = .systemFont(ofSize: 20, weight: .semibold) + nameLabel.textColor = .white + nameLabel.text = "Momoyy" + + subtitleLabel.font = .systemFont(ofSize: 16) + subtitleLabel.textColor = UIColor.white.withAlphaComponent(0.6) + subtitleLabel.text = "Nice to meet you" + + timeLabel.font = .systemFont(ofSize: 14) + timeLabel.textColor = UIColor.white.withAlphaComponent(0.5) + timeLabel.text = "11:03" + + unreadView.backgroundColor = UIColor.systemRed + unreadView.textColor = .white + unreadView.font = .systemFont(ofSize: 12, weight: .bold) + unreadView.textAlignment = .center + unreadView.layer.cornerRadius = 12 + unreadView.layer.masksToBounds = true + unreadView.text = "99+" + + contentView.addSubview(avatar) + contentView.addSubview(nameLabel) + contentView.addSubview(subtitleLabel) + contentView.addSubview(timeLabel) + contentView.addSubview(unreadView) + + avatar.translatesAutoresizingMaskIntoConstraints = false + nameLabel.translatesAutoresizingMaskIntoConstraints = false + subtitleLabel.translatesAutoresizingMaskIntoConstraints = false + timeLabel.translatesAutoresizingMaskIntoConstraints = false + unreadView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + avatar.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), + avatar.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), + avatar.widthAnchor.constraint(equalToConstant: 48), + avatar.heightAnchor.constraint(equalToConstant: 48), + + nameLabel.leadingAnchor.constraint(equalTo: avatar.trailingAnchor, constant: 12), + nameLabel.topAnchor.constraint(equalTo: avatar.topAnchor, constant: -2), + + subtitleLabel.leadingAnchor.constraint(equalTo: nameLabel.leadingAnchor), + subtitleLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 6), + + timeLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16), + timeLabel.topAnchor.constraint(equalTo: nameLabel.topAnchor), + + unreadView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16), + unreadView.centerYAnchor.constraint(equalTo: subtitleLabel.centerYAnchor), + unreadView.widthAnchor.constraint(greaterThanOrEqualToConstant: 40), + unreadView.heightAnchor.constraint(equalToConstant: 24) + ]) + } +} + + + + diff --git a/YuMi/E-P/NewMessage/EPMessageMainViewController.swift b/YuMi/E-P/NewMessage/EPMessageMainViewController.swift new file mode 100644 index 0000000..1c50097 --- /dev/null +++ b/YuMi/E-P/NewMessage/EPMessageMainViewController.swift @@ -0,0 +1,92 @@ +import UIKit +import SnapKit + +final class EPMessageMainViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate { + // 外部回调:未读数变化 + var unreadCountDidChange: ((Int)->Void)? + + private let segment = EPMessageSegmentView() + private let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal) + + private lazy var pages: [UIViewController] = { + return [ + EPMessageListVC(), + EPFriendListVC(), + EPFollowingListVC(), + EPFansListVC() + ] + }() + + private var currentIndex: Int = 0 + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = UIColor.black.withAlphaComponent(0.92) + title = YMLocalizedString("XPSessionMainViewController0") + + setupSegment() + setupPageVC() + + // 模拟未读变化(后续接入桥接器) + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in + self?.unreadCountDidChange?(12) + } + } + + private func setupSegment() { + view.addSubview(segment) + segment.snp.makeConstraints { make in + make.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(8) + make.leading.trailing.equalToSuperview().inset(20) + make.height.equalTo(48) + } + segment.didSelect = { [weak self] index in + self?.setPage(index: index, animated: true) + } + } + + private func setupPageVC() { + addChild(pageVC) + view.addSubview(pageVC.view) + pageVC.view.backgroundColor = .clear + pageVC.view.snp.makeConstraints { make in + make.top.equalTo(segment.snp.bottom).offset(8) + make.leading.trailing.bottom.equalTo(view.safeAreaLayoutGuide) + } + pageVC.didMove(toParent: self) + pageVC.dataSource = self + pageVC.delegate = self + pageVC.setViewControllers([pages[0]], direction: .forward, animated: false) + } + + private func setPage(index: Int, animated: Bool) { + guard index != currentIndex, index >= 0, index < pages.count else { return } + let direction: UIPageViewController.NavigationDirection = index > currentIndex ? .forward : .reverse + pageVC.setViewControllers([pages[index]], direction: direction, animated: animated) + currentIndex = index + segment.select(index: index, animated: animated) + title = segment.titles[index] + } + + // MARK: - UIPageViewControllerDataSource + func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { + guard let idx = pages.firstIndex(of: viewController), idx > 0 else { return nil } + return pages[idx - 1] + } + + func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { + guard let idx = pages.firstIndex(of: viewController), idx < pages.count - 1 else { return nil } + return pages[idx + 1] + } + + func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { + guard completed, let vc = pageViewController.viewControllers?.first, let idx = pages.firstIndex(of: vc) else { return } + currentIndex = idx + segment.select(index: idx, animated: true) + title = segment.titles[idx] + } +} + + + + diff --git a/YuMi/E-P/NewMessage/EPMessageSegmentView.swift b/YuMi/E-P/NewMessage/EPMessageSegmentView.swift new file mode 100644 index 0000000..c0196a0 --- /dev/null +++ b/YuMi/E-P/NewMessage/EPMessageSegmentView.swift @@ -0,0 +1,84 @@ +// A simple segmented control with underline indicator for four tabs +import UIKit +import SnapKit + +final class EPMessageSegmentView: UIView { + enum Segment: Int, CaseIterable { case message=0, friend, following, fans } + + var titles: [String] = [ + YMLocalizedString("XPSessionMainViewController0"), + YMLocalizedString("XPSessionMainViewController1"), + YMLocalizedString("XPSessionMainViewController2"), + YMLocalizedString("XPSessionMainViewController3") + ] + + var didSelect: ((Int)->Void)? + + private var buttons: [UIButton] = [] + private let indicator = UIView() + + override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + required init?(coder: NSCoder) { super.init(coder: coder); setup() } + + private func setup() { + backgroundColor = .clear + + let stack = UIStackView() + stack.axis = .horizontal + stack.alignment = .fill + stack.distribution = .fillEqually + stack.spacing = 0 + addSubview(stack) + stack.snp.makeConstraints { $0.edges.equalToSuperview() } + + for (idx, title) in titles.enumerated() { + let b = UIButton(type: .custom) + b.tag = idx + b.setTitle(title, for: .normal) + b.setTitleColor(UIColor.white.withAlphaComponent(0.6), for: .normal) + b.setTitleColor(.white, for: .selected) + b.titleLabel?.font = .systemFont(ofSize: 24, weight: idx == 0 ? .heavy : .regular) + b.addTarget(self, action: #selector(onTap(_:)), for: .touchUpInside) + buttons.append(b) + stack.addArrangedSubview(b) + } + + indicator.backgroundColor = UIColor.systemPink + addSubview(indicator) + layoutIfNeeded() + select(index: 0, animated: false) + } + + @objc private func onTap(_ sender: UIButton) { + select(index: sender.tag, animated: true) + didSelect?(sender.tag) + } + + func select(index: Int, animated: Bool) { + guard index >= 0 && index < buttons.count else { return } + for (i,b) in buttons.enumerated() { + b.isSelected = (i == index) + b.titleLabel?.font = .systemFont(ofSize: 24, weight: b.isSelected ? .heavy : .regular) + } + let target = buttons[index] + let width = target.bounds.width + let y = bounds.height - 4 + let frame = CGRect(x: CGFloat(index) * width + width*0.15, y: y, width: width*0.7, height: 3) + + if animated { + UIView.animate(withDuration: 0.2) { + self.indicator.frame = frame + } + } else { + indicator.frame = frame + } + } +} + + + + diff --git a/YuMi/E-P/NewMoments/Controllers/EPMomentPublishViewController.m b/YuMi/E-P/NewMoments/Controllers/EPMomentPublishViewController.m index cea1ae1..0dfb1db 100644 --- a/YuMi/E-P/NewMoments/Controllers/EPMomentPublishViewController.m +++ b/YuMi/E-P/NewMoments/Controllers/EPMomentPublishViewController.m @@ -1,14 +1,10 @@ -// -// EPMomentPublishViewController.m -// YuMi -// + + // Created by AI on 2025-10-10. -// + // NOTE: 话题选择功能未实现 -// 旧版本 XPMonentsPublishViewController 包含话题选择 UI (addTopicView) -// 但实际业务中话题功能使用率低,新版本暂不实现 -// 如需实现参考: YuMi/Modules/YMMonents/View/XPMonentsPublishTopicView + #import "EPMomentPublishViewController.h" #import @@ -20,7 +16,7 @@ #import "EPEmotionColorStorage.h" #import "UIView+GradientLayer.h" -// 发布成功通知 + NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNotification"; @interface EPMomentPublishViewController () @@ -37,10 +33,10 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot @property (nonatomic, strong) UIButton *emotionButton; @property (nonatomic, strong) UICollectionView *collectionView; @property (nonatomic, strong) NSMutableArray *images; -@property (nonatomic, strong) NSMutableArray *selectedAssets; // TZImagePicker 已选资源 -@property (nonatomic, copy) NSString *selectedEmotionColor; // 选中的情绪颜色 +@property (nonatomic, strong) NSMutableArray *selectedAssets; +@property (nonatomic, copy) NSString *selectedEmotionColor; -@property (nonatomic, assign) BOOL hasAddedGradient; // 标记是否已添加渐变背景 +@property (nonatomic, assign) BOOL hasAddedGradient; @end @@ -51,27 +47,27 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot self.view.backgroundColor = [UIColor colorWithRed:0x0C/255.0 green:0x05/255.0 blue:0x27/255.0 alpha:1.0]; [self setupUI]; - // 自动加载用户专属颜色 + [self loadUserSignatureColor]; } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; - // 添加渐变背景到发布按钮(只添加一次) + if (!self.hasAddedGradient && self.publishButton.bounds.size.width > 0) { - // 使用与登录页面相同的渐变颜色(EPLoginConfig.Colors) - // gradientStart: #F854FC, gradientEnd: #500FFF + + [self.publishButton addGradientBackgroundWithColors:@[ - [UIColor colorWithRed:0xF8/255.0 green:0x54/255.0 blue:0xFC/255.0 alpha:1.0], // #F854FC - [UIColor colorWithRed:0x50/255.0 green:0x0F/255.0 blue:0xFF/255.0 alpha:1.0] // #500FFF + [UIColor colorWithRed:0xF8/255.0 green:0x54/255.0 blue:0xFC/255.0 alpha:1.0], + [UIColor colorWithRed:0x50/255.0 green:0x0F/255.0 blue:0xFF/255.0 alpha:1.0] ] startPoint:CGPointMake(0, 0.5) endPoint:CGPointMake(1, 0.5) cornerRadius:25]; self.hasAddedGradient = YES; } } -/// 加载用户专属颜色作为默认选中 + - (void)loadUserSignatureColor { NSString *signatureColor = [EPEmotionColorStorage userSignatureColor]; if (signatureColor) { @@ -86,7 +82,7 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot [self.view addSubview:self.contentView]; [self.navView addSubview:self.backButton]; [self.navView addSubview:self.titleLabel]; - // 发布按钮移到底部 + [self.contentView addSubview:self.textView]; [self.contentView addSubview:self.limitLabel]; [self.contentView addSubview:self.lineView]; @@ -107,7 +103,7 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot make.centerX.equalTo(self.navView); make.centerY.equalTo(self.backButton); }]; - // 发布按钮约束移到底部 + [self.contentView mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.trailing.equalTo(self.view); make.top.equalTo(self.navView.mas_bottom); @@ -128,16 +124,14 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot make.height.mas_equalTo(1); }]; - // 情绪按钮 + [self.emotionButton mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.trailing.equalTo(self.contentView).inset(15); make.top.equalTo(self.lineView.mas_bottom).offset(10); make.height.mas_equalTo(44); }]; - // 计算显示3行图片所需的高度 - // itemW = (屏幕宽度 - 左右边距30 - 列间距20) / 3 - // 总高度 = 3行itemW + 2个行间距(10*2) + CGFloat itemW = (KScreenWidth - 15*2 - 10*2)/3.0; CGFloat collectionHeight = itemW * 3 + 10 * 2; @@ -147,7 +141,7 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot make.height.mas_equalTo(collectionHeight); }]; - // 底部发布按钮 + [self.publishButton mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.trailing.equalTo(self.view).inset(20); make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom).offset(-20); @@ -164,7 +158,7 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot - (void)onEmotionButtonTapped { EPEmotionColorPicker *picker = [[EPEmotionColorPicker alloc] init]; - // 预选中当前颜色(如果有) + picker.preselectedColor = self.selectedEmotionColor; __weak typeof(self) weakSelf = self; @@ -178,10 +172,10 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot - (void)updateEmotionButtonAppearance { if (self.selectedEmotionColor) { - // 显示选中的颜色 + UIColor *color = [self colorFromHex:self.selectedEmotionColor]; - // 创建色块视图 + UIView *colorDot = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)]; colorDot.backgroundColor = color; colorDot.layer.cornerRadius = 10; @@ -189,7 +183,7 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot colorDot.layer.borderWidth = 2; colorDot.layer.borderColor = [UIColor whiteColor].CGColor; - // 转换为 UIImage + UIGraphicsBeginImageContextWithOptions(colorDot.bounds.size, NO, 0); [colorDot.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *colorDotImage = UIGraphicsGetImageFromCurrentImageContext(); @@ -197,9 +191,9 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot [self.emotionButton setImage:colorDotImage forState:UIControlStateNormal]; - // 获取情绪名称 + NSString *emotionName = [EPEmotionColorStorage emotionNameForColor:self.selectedEmotionColor]; - NSString *title = emotionName + NSString *title = emotionName ? [NSString stringWithFormat:@" Selected Emotion: %@", emotionName] : @" Emotion Selected"; [self.emotionButton setTitle:title forState:UIControlStateNormal]; @@ -212,7 +206,7 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot - (UIColor *)colorFromHex:(NSString *)hexString { unsigned rgbValue = 0; NSScanner *scanner = [NSScanner scannerWithString:hexString]; - [scanner setScanLocation:1]; // 跳过 # + [scanner setScanLocation:1]; [scanner scanHexInt:&rgbValue]; return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 @@ -223,58 +217,58 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot - (void)onPublish { [self.view endEditing:YES]; - // 验证:文本或图片至少有一项 + if (self.textView.text.length == 0 && self.images.count == 0) { [EPProgressHUD showError:YMLocalizedString(@"publish.content_or_image_required")]; return; } - // 创建 Swift API Helper + EPMomentAPISwiftHelper *apiHelper = [[EPMomentAPISwiftHelper alloc] init]; - // 保存情绪颜色用于发布后关联 + NSString *emotionColorToSave = self.selectedEmotionColor; if (self.images.count > 0) { - // 有图片:上传后发布(统一入口) - [[EPSDKManager shared] uploadImages:self.images + + [[EPSDKManager shared] uploadImages:self.images progress:^(NSInteger uploaded, NSInteger total) { [EPProgressHUD showProgress:uploaded total:total]; - } + } success:^(NSArray *resList) { [EPProgressHUD dismiss]; - [apiHelper publishMomentWithType:@"2" - content:self.textView.text ?: @"" - resList:resList + [apiHelper publishMomentWithType:@"2" + content:self.textView.text ?: @"" + resList:resList completion:^{ - // 保存临时情绪颜色(等待列表刷新后匹配) + if (emotionColorToSave) { [self savePendingEmotionColor:emotionColorToSave]; } - // 发送发布成功通知 + [[NSNotificationCenter defaultCenter] postNotificationName:EPMomentPublishSuccessNotification object:nil]; [self dismissViewControllerAnimated:YES completion:nil]; } failure:^(NSInteger code, NSString *msg) { // TODO: 显示错误 Toast NSLog(@"发布失败: %ld - %@", (long)code, msg); }]; - } + } failure:^(NSString *errorMsg) { [EPProgressHUD dismiss]; // TODO: 显示错误 Toast NSLog(@"上传失败: %@", errorMsg); }]; } else { - // 纯文本:直接发布 - [apiHelper publishMomentWithType:@"0" - content:self.textView.text - resList:@[] + + [apiHelper publishMomentWithType:@"0" + content:self.textView.text + resList:@[] completion:^{ - // 保存临时情绪颜色(等待列表刷新后匹配) + if (emotionColorToSave) { [self savePendingEmotionColor:emotionColorToSave]; } - // 发送发布成功通知 + [[NSNotificationCenter defaultCenter] postNotificationName:EPMomentPublishSuccessNotification object:nil]; [self dismissViewControllerAnimated:YES completion:nil]; } failure:^(NSInteger code, NSString *msg) { @@ -284,7 +278,7 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot } } -/// 保存待处理的情绪颜色(临时存储,供列表刷新后匹配) + - (void)savePendingEmotionColor:(NSString *)color { [[NSUserDefaults standardUserDefaults] setObject:color forKey:@"EP_Pending_Emotion_Color"]; [[NSUserDefaults standardUserDefaults] setObject:@([[NSDate date] timeIntervalSince1970]) forKey:@"EP_Pending_Emotion_Timestamp"]; @@ -294,14 +288,14 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot #pragma mark - UICollectionView - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return self.images.count + 1; // 最后一个是添加按钮 + return self.images.count + 1; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"ep.publish.cell" forIndexPath:indexPath]; cell.contentView.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.06]; cell.contentView.layer.cornerRadius = 12; - // 清空复用子视图,避免加号被覆盖 + for (UIView *sub in cell.contentView.subviews) { [sub removeFromSuperview]; } BOOL showAdd = (self.images.count < 9) && (indexPath.item == self.images.count); if (showAdd) { @@ -327,15 +321,16 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot TZImagePickerController *picker = [[TZImagePickerController alloc] initWithMaxImagesCount:9 delegate:self]; picker.allowPickingVideo = NO; picker.allowTakeVideo = NO; - picker.selectedAssets = self.selectedAssets; // 预选 - picker.maxImagesCount = 9; // 总上限 + picker.allowCameraLocation = NO; // 禁止请求定位权限 + picker.selectedAssets = self.selectedAssets; + picker.maxImagesCount = 9; [self presentViewController:picker animated:YES completion:nil]; } } #pragma mark - TZImagePickerControllerDelegate - (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto infos:(NSArray *)infos { - // 合并选择:在已有基础上追加,最多 9 张 + for (NSInteger i = 0; i < assets.count; i++) { id asset = assets[i]; UIImage *img = [photos xpSafeObjectAtIndex:i] ?: photos[i]; @@ -358,73 +353,83 @@ NSString *const EPMomentPublishSuccessNotification = @"EPMomentPublishSuccessNot #pragma mark - Lazy - (UIView *)navView { if (!_navView) { _navView = [UIView new]; _navView.backgroundColor = [UIColor clearColor]; } return _navView; } -- (UIButton *)backButton { if (!_backButton) { _backButton = [UIButton buttonWithType:UIButtonTypeCustom]; [_backButton setImage:[UIImage imageNamed:@"common_nav_back"] forState:UIControlStateNormal]; [_backButton addTarget:self action:@selector(onBack) forControlEvents:UIControlEventTouchUpInside]; } return _backButton; } -- (UILabel *)titleLabel { - if (!_titleLabel) { - _titleLabel = [UILabel new]; - _titleLabel.text = YMLocalizedString(@"publish.title"); - _titleLabel.textColor = [UIColor whiteColor]; // 白色适配深色背景 - _titleLabel.font = [UIFont systemFontOfSize:17]; - } - return _titleLabel; +- (UIButton *)backButton { + if (!_backButton) { + _backButton = [UIButton buttonWithType:UIButtonTypeCustom]; + + UIImage *backImage = [UIImage systemImageNamed:@"chevron.left"]; + UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:20 weight:UIImageSymbolWeightMedium]; + backImage = [backImage imageByApplyingSymbolConfiguration:config]; + [_backButton setImage:backImage forState:UIControlStateNormal]; + [_backButton setTintColor:[UIColor whiteColor]]; + [_backButton addTarget:self action:@selector(onBack) forControlEvents:UIControlEventTouchUpInside]; + } + return _backButton; } -- (UIButton *)publishButton { - if (!_publishButton) { - _publishButton = [UIButton buttonWithType:UIButtonTypeCustom]; - [_publishButton setTitle:YMLocalizedString(@"common.publish") forState:UIControlStateNormal]; - [_publishButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; +- (UILabel *)titleLabel { + if (!_titleLabel) { + _titleLabel = [UILabel new]; + _titleLabel.text = YMLocalizedString(@"publish.title"); + _titleLabel.textColor = [UIColor whiteColor]; + _titleLabel.font = [UIFont systemFontOfSize:17]; + } + return _titleLabel; +} +- (UIButton *)publishButton { + if (!_publishButton) { + _publishButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_publishButton setTitle:YMLocalizedString(@"common.publish") forState:UIControlStateNormal]; + [_publishButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; _publishButton.titleLabel.font = [UIFont systemFontOfSize:17 weight:UIFontWeightMedium]; _publishButton.layer.cornerRadius = 25; - _publishButton.layer.masksToBounds = NO; // 改为 NO 以便渐变层正常显示 - // 渐变背景将在 viewDidLayoutSubviews 中添加(与登录页面统一) - [_publishButton addTarget:self action:@selector(onPublish) forControlEvents:UIControlEventTouchUpInside]; - } - return _publishButton; + _publishButton.layer.masksToBounds = NO; + + [_publishButton addTarget:self action:@selector(onPublish) forControlEvents:UIControlEventTouchUpInside]; + } + return _publishButton; } - (UIView *)contentView { if (!_contentView) { _contentView = [UIView new]; _contentView.backgroundColor = [UIColor clearColor]; } return _contentView; } -- (SZTextView *)textView { - if (!_textView) { - _textView = [SZTextView new]; - _textView.placeholder = @"Enter Content"; - _textView.textColor = [UIColor whiteColor]; // 白色文本适配深色背景 - _textView.placeholderTextColor = [[UIColor whiteColor] colorWithAlphaComponent:0.4]; // 半透明白色占位符 - _textView.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.08]; // 轻微背景色 +- (SZTextView *)textView { + if (!_textView) { + _textView = [SZTextView new]; + _textView.placeholder = @"Enter Content"; + _textView.textColor = [UIColor whiteColor]; + _textView.placeholderTextColor = [[UIColor whiteColor] colorWithAlphaComponent:0.4]; + _textView.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.08]; _textView.layer.cornerRadius = 12; _textView.layer.masksToBounds = YES; - _textView.font = [UIFont systemFontOfSize:15]; - _textView.delegate = self; - } - return _textView; + _textView.font = [UIFont systemFontOfSize:15]; + _textView.delegate = self; + } + return _textView; } -- (UILabel *)limitLabel { - if (!_limitLabel) { - _limitLabel = [UILabel new]; - _limitLabel.text = @"0/500"; - _limitLabel.textColor = [[UIColor whiteColor] colorWithAlphaComponent:0.6]; // 浅色适配深色背景 - _limitLabel.font = [UIFont systemFontOfSize:12]; - } - return _limitLabel; +- (UILabel *)limitLabel { + if (!_limitLabel) { + _limitLabel = [UILabel new]; + _limitLabel.text = @"0/500"; + _limitLabel.textColor = [[UIColor whiteColor] colorWithAlphaComponent:0.6]; + _limitLabel.font = [UIFont systemFontOfSize:12]; + } + return _limitLabel; } - (UIView *)lineView { if (!_lineView) { _lineView = [UIView new]; _lineView.backgroundColor = [DJDKMIMOMColor dividerColor]; } return _lineView; } -- (UIButton *)emotionButton { - if (!_emotionButton) { - _emotionButton = [UIButton buttonWithType:UIButtonTypeCustom]; - [_emotionButton setTitle:@"🎨 Add Emotion" forState:UIControlStateNormal]; - [_emotionButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; // 白色文本 +- (UIButton *)emotionButton { + if (!_emotionButton) { + _emotionButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_emotionButton setTitle:@"🎨 Add Emotion" forState:UIControlStateNormal]; + [_emotionButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; _emotionButton.titleLabel.font = [UIFont systemFontOfSize:15]; _emotionButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; _emotionButton.contentEdgeInsets = UIEdgeInsetsMake(0, 15, 0, 0); - _emotionButton.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.08]; // 稍微提亮背景 + _emotionButton.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.08]; _emotionButton.layer.cornerRadius = 8; _emotionButton.layer.masksToBounds = YES; - [_emotionButton addTarget:self action:@selector(onEmotionButtonTapped) forControlEvents:UIControlEventTouchUpInside]; - } - return _emotionButton; + [_emotionButton addTarget:self action:@selector(onEmotionButtonTapped) forControlEvents:UIControlEventTouchUpInside]; + } + return _emotionButton; } - (UICollectionView *)collectionView { if (!_collectionView) { UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; layout.minimumLineSpacing = 10; layout.minimumInteritemSpacing = 10; CGFloat itemW = (KScreenWidth - 15*2 - 10*2)/3.0; layout.itemSize = CGSizeMake(itemW, itemW); _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; _collectionView.delegate = self; _collectionView.dataSource = self; _collectionView.backgroundColor = [UIColor clearColor]; [_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"ep.publish.cell"]; } return _collectionView; } - (NSMutableArray *)images { if (!_images) { _images = [NSMutableArray array]; } return _images; } - (NSMutableArray *)selectedAssets { if (!_selectedAssets) { _selectedAssets = [NSMutableArray array]; } return _selectedAssets; } @end - - diff --git a/YuMi/E-P/NewMoments/Controllers/EPMomentViewController.m b/YuMi/E-P/NewMoments/Controllers/EPMomentViewController.m index 1ed4d36..e02ad04 100644 --- a/YuMi/E-P/NewMoments/Controllers/EPMomentViewController.m +++ b/YuMi/E-P/NewMoments/Controllers/EPMomentViewController.m @@ -1,10 +1,8 @@ -// -// EPMomentViewController.m -// YuMi -// + + // Created by AI on 2025-10-09. // Copyright © 2025 YuMi. All rights reserved. -// + #import "EPMomentViewController.h" #import @@ -18,13 +16,13 @@ // MARK: - UI Components -/// 列表视图(MVVM:View) + @property (nonatomic, strong) EPMomentListView *listView; -/// 顶部图标 + @property (nonatomic, strong) UIImageView *topIconImageView; -/// 顶部固定文案 + @property (nonatomic, strong) UILabel *topTipLabel; @end @@ -38,24 +36,33 @@ self.title = @"Enjoy your Life Time"; - // 设置 title 为白色 + [self.navigationController.navigationBar setTitleTextAttributes:@{ NSForegroundColorAttributeName: [UIColor whiteColor] }]; [self setupUI]; - [self.listView reloadFirstPage]; - // 监听发布成功通知 - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(onMomentPublishSuccess:) - name:EPMomentPublishSuccessNotification + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(onMomentPublishSuccess:) + name:EPMomentPublishSuccessNotification object:nil]; - // ✅ 新增:冷启动时延迟检查数据,如果没有数据则自动刷新一次 - [self scheduleAutoRefreshIfNeeded]; + NSLog(@"[EPMomentViewController] 页面加载完成,UI 已设置"); +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; - NSLog(@"[EPMomentViewController] 页面加载完成"); + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSLog(@"[EPMomentViewController] 首次 viewDidAppear,延迟 0.3s 后开始加载数据"); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + NSLog(@"[EPMomentViewController] 触发首次数据加载"); + [self.listView reloadFirstPage]; + }); + }); } - (void)viewWillAppear:(BOOL)animated { @@ -73,7 +80,7 @@ make.edges.mas_equalTo(self.view); }]; - // 顶部图标 + [self.view addSubview:self.topIconImageView]; [self.topIconImageView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.view); @@ -81,21 +88,21 @@ make.size.mas_equalTo(CGSizeMake(56, 41)); }]; - // 顶部固定文案 + [self.view addSubview:self.topTipLabel]; [self.topTipLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.topIconImageView.mas_bottom).offset(14); make.leading.trailing.equalTo(self.view).inset(20); }]; - // 列表视图 + [self.view addSubview:self.listView]; [self.listView mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.trailing.bottom.equalTo(self.view); make.top.equalTo(self.topTipLabel.mas_bottom).offset(8); }]; - // 右上角发布按钮 + UIImage *addIcon = [UIImage imageNamed:@"icon_moment_add"]; UIButton *publishButton = [UIButton buttonWithType:UIButtonTypeCustom]; publishButton.contentMode = UIViewContentModeScaleAspectFit; @@ -108,26 +115,6 @@ NSLog(@"[EPMomentViewController] UI 设置完成"); } -// 不再在 VC 内部直接发请求/维护分页 - -// MARK: - Auto Refresh - -/// 延迟检查数据,如果没有数据则自动刷新(解决冷启动数据未加载问题) -- (void)scheduleAutoRefreshIfNeeded { - __weak typeof(self) weakSelf = self; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - __strong typeof(weakSelf) self = weakSelf; - if (!self) return; - - // 检查是否有数据 - if (self.listView.rawList.count == 0) { - NSLog(@"[EPMomentViewController] ⚠️ 冷启动 1 秒后检测到无数据,自动刷新一次"); - [self.listView reloadFirstPage]; - } else { - NSLog(@"[EPMomentViewController] ✅ 冷启动 1 秒后检测到已有 %lu 条数据,无需刷新", (unsigned long)self.listView.rawList.count); - } - }); -} // MARK: - Actions @@ -155,17 +142,16 @@ [[NSNotificationCenter defaultCenter] removeObserver:self]; } -// 列表点击回调由 listView 暴露 // MARK: - Lazy Loading - (EPMomentListView *)listView { if (!_listView) { _listView = [[EPMomentListView alloc] initWithFrame:CGRectZero]; - __weak typeof(self) weakSelf = self; + _listView.onSelectMoment = ^(NSInteger index) { - __strong typeof(weakSelf) self = weakSelf; -// [self showAlertWithMessage:[NSString stringWithFormat:YMLocalizedString(@"moment.item_clicked"), (long)index]]; + + }; } return _listView; @@ -190,6 +176,5 @@ return _topTipLabel; } -// 无数据源属性 @end diff --git a/YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.h b/YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.h index d0d8617..2746347 100644 --- a/YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.h +++ b/YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.h @@ -1,10 +1,7 @@ -// -// EPEmotionColorStorage.h -// YuMi -// + + // Created by AI on 2025-10-14. -// 本地情绪颜色存储管理器(基于 UserDefaults) -// + #import @@ -12,47 +9,38 @@ NS_ASSUME_NONNULL_BEGIN @interface EPEmotionColorStorage : NSObject -/// 保存动态的情绪颜色 -/// @param hexColor Hex 格式颜色值,如 #FF0000 -/// @param dynamicId 动态 ID + + (void)saveColor:(NSString *)hexColor forDynamicId:(NSString *)dynamicId; -/// 获取动态关联的情绪颜色 -/// @param dynamicId 动态 ID -/// @return Hex 格式颜色值,若未设置则返回 nil + + (nullable NSString *)colorForDynamicId:(NSString *)dynamicId; -/// 删除动态的情绪颜色 -/// @param dynamicId 动态 ID + + (void)removeColorForDynamicId:(NSString *)dynamicId; -/// 获取所有预设情绪颜色(6种基础情绪) -/// @return Hex 颜色数组 + + (NSArray *)allEmotionColors; -/// 获取随机情绪颜色(不持久化) + + (NSString *)randomEmotionColor; -/// 根据颜色值获取情绪名称 -/// @param hexColor Hex 格式颜色值,如 #FFD700 -/// @return 情绪名称(如 "Joy"),若未匹配返回 nil + + (nullable NSString *)emotionNameForColor:(NSString *)hexColor; #pragma mark - User Signature Color -/// 保存用户专属颜色 + + (void)saveUserSignatureColor:(NSString *)hexColor; -/// 获取用户专属颜色 + + (nullable NSString *)userSignatureColor; -/// 是否已设置专属颜色 + + (BOOL)hasUserSignatureColor; -/// 清除专属颜色(调试用) + + (void)clearUserSignatureColor; @end NS_ASSUME_NONNULL_END - diff --git a/YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.m b/YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.m index 62fb9eb..b9f1d80 100644 --- a/YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.m +++ b/YuMi/E-P/NewMoments/Services/EPEmotionColorStorage.m @@ -1,9 +1,7 @@ -// -// EPEmotionColorStorage.m -// YuMi -// + + // Created by AI on 2025-10-14. -// + #import "EPEmotionColorStorage.h" @@ -44,14 +42,14 @@ static NSString *const kUserSignatureTimestampKey = @"EP_User_Signature_Timestam + (NSArray *)allEmotionColors { return @[ - @"#FFD700", // 喜悦 Joy(金黄)- 降低亮度,更温暖 - @"#4A90E2", // 悲伤 Sadness(天蓝)- 提高亮度,更柔和 - @"#E74C3C", // 愤怒 Anger(珊瑚红)- 降低饱和度 - @"#9B59B6", // 恐惧 Fear(紫罗兰)- 稍微提亮 - @"#FF9A3D", // 惊讶 Surprise(柔和橙)- 略微调暗 - @"#2ECC71", // 厌恶 Disgust(翡翠绿)- 大幅降低亮度 - @"#3498DB", // 信任 Trust(亮蓝)- 清新明亮 - @"#F39C12" // 期待 Anticipation(琥珀色)- 温暖期待 + @"#FFD700", + @"#4A90E2", + @"#E74C3C", + @"#9B59B6", + @"#FF9A3D", + @"#2ECC71", + @"#3498DB", + @"#F39C12" ]; } @@ -67,7 +65,7 @@ static NSString *const kUserSignatureTimestampKey = @"EP_User_Signature_Timestam NSArray *colors = [self allEmotionColors]; NSArray *emotions = @[@"Joy", @"Sadness", @"Anger", @"Fear", @"Surprise", @"Disgust", @"Trust", @"Anticipation"]; - // 大小写不敏感比较 + NSString *upperHex = [hexColor uppercaseString]; for (NSInteger i = 0; i < colors.count; i++) { if ([[colors[i] uppercaseString] isEqualToString:upperHex]) { @@ -91,7 +89,7 @@ static NSString *const kUserSignatureTimestampKey = @"EP_User_Signature_Timestam if (!hexColor) return; [[NSUserDefaults standardUserDefaults] setObject:hexColor forKey:kUserSignatureColorKey]; - [[NSUserDefaults standardUserDefaults] setObject:@([[NSDate date] timeIntervalSince1970]) + [[NSUserDefaults standardUserDefaults] setObject:@([[NSDate date] timeIntervalSince1970]) forKey:kUserSignatureTimestampKey]; [[NSUserDefaults standardUserDefaults] synchronize]; @@ -115,4 +113,3 @@ static NSString *const kUserSignatureTimestampKey = @"EP_User_Signature_Timestam } @end - diff --git a/YuMi/E-P/NewMoments/Services/EPMomentAPISwiftHelper.swift b/YuMi/E-P/NewMoments/Services/EPMomentAPISwiftHelper.swift index 8d96032..0050609 100644 --- a/YuMi/E-P/NewMoments/Services/EPMomentAPISwiftHelper.swift +++ b/YuMi/E-P/NewMoments/Services/EPMomentAPISwiftHelper.swift @@ -1,55 +1,47 @@ -// -// EPMomentAPISwiftHelper.swift -// YuMi -// + + // Created by AI on 2025-10-11. -// + import Foundation -/// 动态 API 封装(Swift 现代化版本) -/// 统一封装列表获取和发布功能,完全替代 OC 版本 + @objc class EPMomentAPISwiftHelper: NSObject { - /// 拉取最新动态列表 - /// - Parameters: - /// - nextID: 下一页 ID,首次传空字符串 - /// - completion: 成功回调 (动态列表, 下一页ID) - /// - failure: 失败回调 (错误码, 错误信息) + @objc func fetchLatestMomentsWithNextID( _ nextID: String, completion: @escaping ([MomentsInfoModel], String) -> Void, failure: @escaping (Int, String) -> Void ) { let pageSize = "20" - let types = "0,2" // 图片+文字 + let types = "0,2" + + NSLog("[EPMomentAPISwiftHelper] 🔄 开始请求动态列表,nextID=\(nextID.isEmpty ? "(首页)" : nextID)") Api.momentsLatestList({ (data, code, msg) in + NSLog("[EPMomentAPISwiftHelper] 📥 收到响应,code=\(code)") + if code == 200, let dict = data?.data as? NSDictionary { - // 使用 MomentsListInfoModel 序列化响应数据(标准化方式) - // 参考: XPMomentsLatestPresenter.m line 25 / EPLoginService.swift line 34 - // Swift 中使用 mj_object(withKeyValues:) 而不是 model(withJSON:) + NSLog("[EPMomentAPISwiftHelper] 📦 开始解析数据字典") + if let listInfo = MomentsListInfoModel.mj_object(withKeyValues: dict) { let dynamicList = listInfo.dynamicList let nextDynamicId = listInfo.nextDynamicId + NSLog("[EPMomentAPISwiftHelper] ✅ 解析成功,dynamicList.count=\(dynamicList.count), nextDynamicId=\(nextDynamicId)") completion(dynamicList, nextDynamicId) } else { - // 序列化失败时返回空数据 + NSLog("[EPMomentAPISwiftHelper] ⚠️ 解析失败,返回空数组") completion([], "") } } else { + NSLog("[EPMomentAPISwiftHelper] ❌ 请求失败,code=\(code), msg=\(msg ?? "无错误信息")") failure(Int(code), msg ?? YMLocalizedString("error.request_failed")) } }, dynamicId: nextID, pageSize: pageSize, types: types) } - /// 发布动态 - /// - Parameters: - /// - type: "0"=纯文本, "2"=图片 - /// - content: 文本内容 - /// - resList: 图片信息数组 - /// - completion: 成功回调 - /// - failure: 失败回调 (错误码, 错误信息) + @objc func publishMoment( type: String, content: String, @@ -62,10 +54,9 @@ import Foundation return } - // worldId 传空字符串(话题功能不实现) + // NOTE: 旧版本 XPMonentsPublishViewController 包含话题选择功能 - // 但实际业务中话题功能使用率低,新版本暂不实现 - // 如需实现参考: YuMi/Modules/YMMonents/View/XPMonentsPublishTopicView + Api.momentsPublish({ (data, code, msg) in if code == 200 { @@ -76,14 +67,7 @@ import Foundation }, uid: uid, type: type, worldId: "", content: content, resList: resList) } - /// 点赞/取消点赞动态 - /// - Parameters: - /// - dynamicId: 动态 ID - /// - isLike: true=点赞,false=取消点赞 - /// - likedUid: 动态发布者 UID - /// - worldId: 话题 ID - /// - completion: 成功回调 - /// - failure: 失败回调 (错误码, 错误信息) + @objc func likeMoment( dynamicId: String, isLike: Bool, @@ -109,4 +93,3 @@ import Foundation }, dynamicId: dynamicId, uid: uid, status: status, likedUid: likedUid, worldId: worldIdStr) } } - diff --git a/YuMi/E-P/NewMoments/Views/EPEmotionColorPicker.m b/YuMi/E-P/NewMoments/Views/EPEmotionColorPicker.m index 35defa9..03f82b3 100644 --- a/YuMi/E-P/NewMoments/Views/EPEmotionColorPicker.m +++ b/YuMi/E-P/NewMoments/Views/EPEmotionColorPicker.m @@ -1,9 +1,7 @@ -// -// EPEmotionColorPicker.m -// YuMi -// + + // Created by AI on 2025-10-14. -// + #import "EPEmotionColorPicker.h" #import "EPEmotionColorWheelView.h" @@ -39,27 +37,27 @@ - (void)setupUI { self.backgroundColor = [UIColor clearColor]; - // 背景遮罩 + [self addSubview:self.backgroundMask]; [self.backgroundMask mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self); }]; - // 底部卡片容器 + [self addSubview:self.containerView]; [self.containerView mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.trailing.bottom.equalTo(self); - make.height.mas_equalTo(450); // 增加高度以适应新布局 + make.height.mas_equalTo(450); }]; - // 标题 + [self.containerView addSubview:self.titleLabel]; [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.containerView).offset(20); make.centerX.equalTo(self.containerView); }]; - // Info 按钮(左上角) + [self.containerView addSubview:self.infoButton]; [self.infoButton mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.equalTo(self.containerView).offset(16); @@ -67,7 +65,7 @@ make.size.mas_equalTo(CGSizeMake(28, 28)); }]; - // OK 按钮(右上角) + [self.containerView addSubview:self.okButton]; [self.okButton mas_makeConstraints:^(MASConstraintMaker *make) { make.trailing.equalTo(self.containerView).offset(-16); @@ -75,7 +73,7 @@ make.size.mas_equalTo(CGSizeMake(60, 32)); }]; - // 选中状态显示区域 + [self.containerView addSubview:self.selectedColorView]; [self.selectedColorView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.titleLabel.mas_bottom).offset(20); @@ -84,12 +82,12 @@ make.leading.trailing.equalTo(self.containerView).inset(20); }]; - // 色轮视图(使用共享组件) + [self.containerView addSubview:self.colorWheelView]; [self.colorWheelView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.containerView); make.top.equalTo(self.selectedColorView.mas_bottom).offset(20); - make.size.mas_equalTo(CGSizeMake(280, 280)); // 调整尺寸 + make.size.mas_equalTo(CGSizeMake(280, 280)); }]; } @@ -119,11 +117,11 @@ make.edges.equalTo(parentView); }]; - // 初始状态 - self.backgroundMask.alpha = 0; - self.containerView.transform = CGAffineTransformMakeTranslation(0, 450); // 更新动画偏移量 - // 弹出动画 + self.backgroundMask.alpha = 0; + self.containerView.transform = CGAffineTransformMakeTranslation(0, 450); + + [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ self.backgroundMask.alpha = 1; self.containerView.transform = CGAffineTransformIdentity; @@ -133,7 +131,7 @@ - (void)dismiss { [UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{ self.backgroundMask.alpha = 0; - self.containerView.transform = CGAffineTransformMakeTranslation(0, 450); // 更新动画偏移量 + self.containerView.transform = CGAffineTransformMakeTranslation(0, 450); } completion:^(BOOL finished) { [self removeFromSuperview]; }]; @@ -177,12 +175,12 @@ if (!_infoButton) { _infoButton = [UIButton buttonWithType:UIButtonTypeCustom]; - // 使用系统 info.circle 图标 + UIImage *infoIcon = [UIImage systemImageNamed:@"info.circle"]; [_infoButton setImage:infoIcon forState:UIControlStateNormal]; _infoButton.tintColor = [[UIColor whiteColor] colorWithAlphaComponent:0.7]; - // 点击效果 + [_infoButton addTarget:self action:@selector(onInfoButtonTapped) forControlEvents:UIControlEventTouchUpInside]; } return _infoButton; @@ -194,11 +192,11 @@ _selectedColorView.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.1]; _selectedColorView.layer.cornerRadius = 25; _selectedColorView.layer.masksToBounds = YES; - _selectedColorView.hidden = YES; // 初始隐藏 + _selectedColorView.hidden = YES; + - // 颜色圆点 UIView *colorDot = [[UIView alloc] init]; - colorDot.tag = 100; // 用于后续查找 + colorDot.tag = 100; colorDot.layer.cornerRadius = 12; colorDot.layer.masksToBounds = YES; [_selectedColorView addSubview:colorDot]; @@ -208,7 +206,7 @@ make.size.mas_equalTo(CGSizeMake(24, 24)); }]; - // 情绪名称标签 + [_selectedColorView addSubview:self.selectedColorLabel]; [self.selectedColorLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.equalTo(colorDot.mas_trailing).offset(12); @@ -238,7 +236,7 @@ _okButton.backgroundColor = [UIColor colorWithRed:0x9B/255.0 green:0x59/255.0 blue:0xB6/255.0 alpha:1.0]; _okButton.layer.cornerRadius = 16; _okButton.layer.masksToBounds = YES; - _okButton.enabled = NO; // 初始禁用 + _okButton.enabled = NO; _okButton.alpha = 0.5; [_okButton addTarget:self action:@selector(onOkButtonTapped) forControlEvents:UIControlEventTouchUpInside]; } @@ -256,41 +254,41 @@ _colorWheelView.onColorTapped = ^(NSString *hexColor, NSInteger index) { __strong typeof(weakSelf) self = weakSelf; - // 保存当前选择 + self.currentSelectedColor = hexColor; self.currentSelectedIndex = index; - // 更新选中状态显示 + [self updateSelectedColorDisplay:hexColor index:index]; }; } return _colorWheelView; } -/// 更新选中颜色显示 + - (void)updateSelectedColorDisplay:(NSString *)hexColor index:(NSInteger)index { NSArray *emotions = @[@"Joy", @"Sadness", @"Anger", @"Fear", @"Surprise", @"Disgust", @"Trust", @"Anticipation"]; - // 显示选中状态区域 + self.selectedColorView.hidden = NO; - // 更新颜色圆点 + UIView *colorDot = [self.selectedColorView viewWithTag:100]; colorDot.backgroundColor = [self colorFromHex:hexColor]; - // 更新情绪名称 + self.selectedColorLabel.text = emotions[index]; - // 启用OK按钮 + self.okButton.enabled = YES; self.okButton.alpha = 1.0; } -/// Hex 转 UIColor + - (UIColor *)colorFromHex:(NSString *)hexString { unsigned rgbValue = 0; NSScanner *scanner = [NSScanner scannerWithString:hexString]; - [scanner setScanLocation:1]; // 跳过 # + [scanner setScanLocation:1]; [scanner scanHexInt:&rgbValue]; return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 @@ -299,4 +297,3 @@ } @end - diff --git a/YuMi/E-P/NewMoments/Views/EPEmotionColorWheelView.m b/YuMi/E-P/NewMoments/Views/EPEmotionColorWheelView.m index 2a79ecc..b5cca42 100644 --- a/YuMi/E-P/NewMoments/Views/EPEmotionColorWheelView.m +++ b/YuMi/E-P/NewMoments/Views/EPEmotionColorWheelView.m @@ -1,9 +1,7 @@ -// -// EPEmotionColorWheelView.m -// YuMi -// + + // Created by AI on 2025-10-15. -// + #import "EPEmotionColorWheelView.h" #import "EPEmotionColorStorage.h" @@ -11,7 +9,7 @@ @interface EPEmotionColorWheelView () @property (nonatomic, strong) NSMutableArray *colorButtons; -@property (nonatomic, assign) NSInteger selectedIndex; // 当前选中的索引 +@property (nonatomic, assign) NSInteger selectedIndex; @end @@ -21,7 +19,7 @@ - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { - // 默认配置 + _radius = 80.0; _buttonSize = 50.0; _colorButtons = [NSMutableArray array]; @@ -34,7 +32,7 @@ - (void)layoutSubviews { [super layoutSubviews]; - // 如果色轮还未创建,自动创建 + if (self.colorButtons.count == 0) { [self createColorButtons]; } @@ -45,13 +43,13 @@ - (void)reloadWithPreselectedColor:(NSString *)color { self.preselectedColor = color; - // 清除旧按钮 + for (UIButton *btn in self.colorButtons) { [btn removeFromSuperview]; } [self.colorButtons removeAllObjects]; - // 重新创建 + [self createColorButtons]; } @@ -66,7 +64,7 @@ CGFloat centerY = CGRectGetHeight(self.bounds) / 2.0; for (NSInteger i = 0; i < colors.count; i++) { - // 从顶部开始,顺时针排列 + CGFloat angle = angleStep * i - M_PI_2; CGFloat x = centerX + self.radius * cos(angle) - self.buttonSize / 2.0; CGFloat y = centerY + self.radius * sin(angle) - self.buttonSize / 2.0; @@ -81,13 +79,13 @@ button.tag = i; [button addTarget:self action:@selector(onButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; - // 如果是预选中颜色,添加选中态标识 + if (self.preselectedColor && [colors[i] isEqualToString:self.preselectedColor]) { - button.layer.borderWidth = 5.0; // 加粗边框 - button.transform = CGAffineTransformMakeScale(1.1, 1.1); // 稍微放大 + button.layer.borderWidth = 5.0; + button.transform = CGAffineTransformMakeScale(1.1, 1.1); } - // 添加阴影效果 + button.layer.shadowColor = [self colorFromHex:colors[i]].CGColor; button.layer.shadowOffset = CGSizeMake(0, 2); button.layer.shadowOpacity = 0.6; @@ -103,10 +101,10 @@ NSInteger index = sender.tag; self.selectedIndex = index; - // 更新选中状态 + [self updateSelectionState]; - // 执行回调(仅用于更新UI,不直接确认选择) + NSArray *colors = [EPEmotionColorStorage allEmotionColors]; NSString *selectedColor = colors[index]; if (self.onColorTapped) { @@ -114,16 +112,16 @@ } } -/// 更新选中状态 + - (void)updateSelectionState { for (NSInteger i = 0; i < self.colorButtons.count; i++) { UIButton *button = self.colorButtons[i]; if (i == self.selectedIndex) { - // 选中状态:加粗边框,稍微放大 + button.layer.borderWidth = 5.0; button.transform = CGAffineTransformMakeScale(1.1, 1.1); } else { - // 未选中状态:正常边框,正常大小 + button.layer.borderWidth = 3.0; button.transform = CGAffineTransformIdentity; } @@ -135,7 +133,7 @@ - (UIColor *)colorFromHex:(NSString *)hexString { unsigned rgbValue = 0; NSScanner *scanner = [NSScanner scannerWithString:hexString]; - [scanner setScanLocation:1]; // 跳过 # + [scanner setScanLocation:1]; [scanner scanHexInt:&rgbValue]; return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 @@ -144,4 +142,3 @@ } @end - diff --git a/YuMi/E-P/NewMoments/Views/EPEmotionInfoView.m b/YuMi/E-P/NewMoments/Views/EPEmotionInfoView.m index dc4da14..7dc2d72 100644 --- a/YuMi/E-P/NewMoments/Views/EPEmotionInfoView.m +++ b/YuMi/E-P/NewMoments/Views/EPEmotionInfoView.m @@ -1,9 +1,7 @@ -// -// EPEmotionInfoView.m -// YuMi -// + + // Created by AI on 2025-10-16. -// + #import "EPEmotionInfoView.h" #import @@ -33,13 +31,13 @@ - (void)setupUI { self.backgroundColor = [UIColor clearColor]; - // 背景遮罩 + [self addSubview:self.backgroundMask]; [self.backgroundMask mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self); }]; - // 内容容器 + [self addSubview:self.contentContainer]; [self.contentContainer mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self); @@ -47,14 +45,14 @@ make.height.mas_lessThanOrEqualTo(500); }]; - // 标题 + [self.contentContainer addSubview:self.titleLabel]; [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.contentContainer).offset(24); make.leading.trailing.equalTo(self.contentContainer).inset(20); }]; - // 滚动视图 + [self.contentContainer addSubview:self.scrollView]; [self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.titleLabel.mas_bottom).offset(16); @@ -62,14 +60,14 @@ make.height.mas_lessThanOrEqualTo(320); }]; - // 内容文本 + [self.scrollView addSubview:self.contentLabel]; [self.contentLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.scrollView); make.width.equalTo(self.scrollView); }]; - // 关闭按钮 + [self.contentContainer addSubview:self.closeButton]; [self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.scrollView.mas_bottom).offset(20); @@ -98,12 +96,12 @@ make.edges.equalTo(parentView); }]; - // 初始状态 + self.backgroundMask.alpha = 0; self.contentContainer.alpha = 0; self.contentContainer.transform = CGAffineTransformMakeScale(0.9, 0.9); - // 弹出动画 + [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ self.backgroundMask.alpha = 1; self.contentContainer.alpha = 1; @@ -170,7 +168,7 @@ _contentLabel.textColor = [[UIColor whiteColor] colorWithAlphaComponent:0.9]; _contentLabel.font = [UIFont systemFontOfSize:15]; - // 普拉奇克情绪轮说明文本 + NSString *content = @"Based on Plutchik's Wheel of Emotions, we use 8 core colors to represent fundamental human emotions:\n\n" "🟡 Joy (Gold)\n" "Represents happiness, delight, and cheerfulness. Like sunshine warming your heart.\n\n" @@ -210,4 +208,3 @@ } @end - diff --git a/YuMi/E-P/NewMoments/Views/EPMomentCell.m b/YuMi/E-P/NewMoments/Views/EPMomentCell.m index ff00a44..4d9f471 100644 --- a/YuMi/E-P/NewMoments/Views/EPMomentCell.m +++ b/YuMi/E-P/NewMoments/Views/EPMomentCell.m @@ -1,10 +1,8 @@ -// -// NewMomentCell.m -// YuMi -// + + // Created by AI on 2025-10-09. // Copyright © 2025 YuMi. All rights reserved. -// + #import "EPMomentCell.h" #import "MomentsInfoModel.h" @@ -12,52 +10,50 @@ #import "NetImageView.h" #import "EPEmotionColorStorage.h" #import "SDPhotoBrowser.h" -#import "YuMi-Swift.h" // Swift 互操作 +#import "YuMi-Swift.h" @interface EPMomentCell () // MARK: - UI Components -/// 卡片容器 + @property (nonatomic, strong) UIView *cardView; -/// 彩色背景层(毛玻璃下方) + @property (nonatomic, strong) UIView *colorBackgroundView; -/// 毛玻璃效果视图 + @property (nonatomic, strong) UIVisualEffectView *blurEffectView; -/// 头像(网络) + @property (nonatomic, strong) NetImageView *avatarImageView; -/// 用户名 + @property (nonatomic, strong) UILabel *nameLabel; -/// 时间标签 + @property (nonatomic, strong) UILabel *timeLabel; -/// 内容标签 + @property (nonatomic, strong) UILabel *contentLabel; -/// 图片容器(九宫格) + @property (nonatomic, strong) UIView *imagesContainer; @property (nonatomic, strong) NSMutableArray *imageViews; -/// 底部操作栏 + @property (nonatomic, strong) UIView *actionBar; -/// 点赞按钮 + @property (nonatomic, strong) UIButton *likeButton; -/// 评论按钮 + @property (nonatomic, strong) UIButton *commentButton; -// 分享按钮已移除 -/// 当前数据模型 @property (nonatomic, strong) MomentsInfoModel *currentModel; -/// API Helper (Swift 版本) + @property (nonatomic, strong) EPMomentAPISwiftHelper *apiHelper; @end @@ -78,7 +74,7 @@ // MARK: - Setup UI - (void)setupUI { - // 卡片容器(圆角矩形 + 阴影) + [self.contentView addSubview:self.cardView]; [self.cardView mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.trailing.equalTo(self.contentView).inset(15); @@ -86,19 +82,19 @@ make.bottom.equalTo(self.contentView).offset(-8).priority(UILayoutPriorityRequired - 1); }]; - // 彩色背景层(最底层) + [self.cardView addSubview:self.colorBackgroundView]; [self.colorBackgroundView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.cardView); }]; - // 毛玻璃效果视图(在彩色背景层之上) + [self.cardView addSubview:self.blurEffectView]; [self.blurEffectView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.cardView); }]; - // 头像( + [self.blurEffectView.contentView addSubview:self.avatarImageView]; [self.avatarImageView mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.equalTo(self.cardView).offset(15); @@ -106,7 +102,7 @@ make.size.mas_equalTo(CGSizeMake(40, 40)); }]; - // 用户名 + [self.blurEffectView.contentView addSubview:self.nameLabel]; [self.nameLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.equalTo(self.avatarImageView.mas_trailing).offset(10); @@ -114,7 +110,7 @@ make.trailing.equalTo(self.cardView).offset(-15); }]; - // 时间 + [self.blurEffectView.contentView addSubview:self.timeLabel]; [self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.equalTo(self.nameLabel); @@ -122,32 +118,32 @@ make.trailing.equalTo(self.cardView).offset(-15); }]; - // 内容 + [self.blurEffectView.contentView addSubview:self.contentLabel]; [self.contentLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.trailing.equalTo(self.cardView).inset(15); make.top.equalTo(self.avatarImageView.mas_bottom).offset(12); }]; - // 图片九宫格 + [self.blurEffectView.contentView addSubview:self.imagesContainer]; [self.imagesContainer mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.trailing.equalTo(self.cardView).inset(15); make.top.equalTo(self.contentLabel.mas_bottom).offset(12); - make.height.mas_equalTo(0); // 初始高度为0,renderImages 时会 remakeConstraints + make.height.mas_equalTo(0); }]; - // 底部操作栏 + [self.blurEffectView.contentView addSubview:self.actionBar]; [self.actionBar mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.trailing.equalTo(self.cardView); make.top.equalTo(self.imagesContainer.mas_bottom).offset(12); make.height.mas_equalTo(50); - // 设置较高优先级,确保底部约束生效 + make.bottom.equalTo(self.cardView).offset(-8).priority(UILayoutPriorityRequired - 2); }]; - // 点赞按钮(居左显示,评论功能已隐藏) + [self.actionBar addSubview:self.likeButton]; [self.likeButton mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.equalTo(self.actionBar); @@ -161,19 +157,19 @@ - (void)configureWithModel:(MomentsInfoModel *)model { self.currentModel = model; - // 配置用户名 + self.nameLabel.text = model.nick ?: YMLocalizedString(@"user.anonymous"); - // 配置时间(将时间戳转换为 MM/dd 格式) + self.timeLabel.text = [self formatTimestampToDate:model.publishTime]; - // 配置内容 + self.contentLabel.text = model.content ?: @""; - // 配置图片九宫格 + [self renderImages:model.dynamicResList]; - // 配置点赞按钮状态和数字 + NSInteger likeCnt = MAX(0, model.likeCount.integerValue); self.likeButton.selected = model.isLike; [self.likeButton setTitle:[NSString stringWithFormat:@" %ld", (long)likeCnt] forState:UIControlStateNormal]; @@ -181,16 +177,16 @@ self.avatarImageView.imageUrl = model.avatar; - // 配置情绪颜色 border 和 shadow + [self applyEmotionColorEffect:model.emotionColor]; - // 确保布局完成后 cell 高度正确 + [self setNeedsLayout]; } -/// 应用情绪颜色视觉效果(Background + Shadow) + - (void)applyEmotionColorEffect:(NSString *)emotionColorHex { - // 获取颜色(已在列表加载时处理,这里直接使用) + if (!emotionColorHex) { NSLog(@"[EPMomentCell] 警告:emotionColorHex 为 nil"); return; @@ -198,24 +194,24 @@ UIColor *color = [self colorFromHex:emotionColorHex]; - // 移除边框 + self.cardView.layer.borderWidth = 0; - // 设置彩色背景(50% 透明度,在毛玻璃下方) + self.colorBackgroundView.backgroundColor = [color colorWithAlphaComponent:0.5]; - // 设置 shadow(使用情绪颜色) + self.cardView.layer.shadowColor = color.CGColor; self.cardView.layer.shadowOffset = CGSizeMake(0, 2); self.cardView.layer.shadowOpacity = 0.5; self.cardView.layer.shadowRadius = 16.0; } -/// Hex 转 UIColor + - (UIColor *)colorFromHex:(NSString *)hexString { unsigned rgbValue = 0; NSScanner *scanner = [NSScanner scannerWithString:hexString]; - [scanner setScanLocation:1]; // 跳过 # + [scanner setScanLocation:1]; [scanner scanHexInt:&rgbValue]; return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 @@ -226,7 +222,7 @@ // MARK: - Images Grid - (void)renderImages:(NSArray *)resList { - // 清理旧视图 + for (UIView *iv in self.imageViews) { [iv removeFromSuperview]; } [self.imageViews removeAllObjects]; if (resList.count == 0) { @@ -236,14 +232,14 @@ make.height.mas_equalTo(0); }]; - // 强制触发布局更新 + [self.contentView setNeedsLayout]; [self.contentView layoutIfNeeded]; return; } NSInteger columns = 3; CGFloat spacing = 6.0; - CGFloat totalWidth = [UIScreen mainScreen].bounds.size.width - 30 - 30; // 左右各 15 内边距,再减卡片左右 15 + CGFloat totalWidth = [UIScreen mainScreen].bounds.size.width - 30 - 30; CGFloat itemW = floor((totalWidth - spacing * (columns - 1)) / columns); for (NSInteger i = 0; i < resList.count && i < 9; i++) { @@ -255,9 +251,9 @@ iv.layer.masksToBounds = YES; iv.contentMode = UIViewContentModeScaleAspectFill; iv.userInteractionEnabled = YES; - iv.tag = i; // 用于识别点击的图片索引 + iv.tag = i; + - // 添加点击手势 UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onImageTapped:)]; [iv addGestureRecognizer:tap]; @@ -270,7 +266,7 @@ make.top.equalTo(self.imagesContainer).offset((itemW + spacing) * row); make.size.mas_equalTo(CGSizeMake(itemW, itemW)); }]; - // 绑定网络图片 + NSString *url = nil; id item = resList[i]; if ([item isKindOfClass:[NSDictionary class]]) { @@ -289,18 +285,18 @@ make.height.mas_equalTo(height); }]; - // 强制触发布局更新,确保 cell 高度正确计算 + [self.contentView setNeedsLayout]; [self.contentView layoutIfNeeded]; } -/// 格式化时间戳为 MM/dd 格式 + - (NSString *)formatTimestampToDate:(NSString *)timestampString { if (!timestampString || timestampString.length == 0) { return @""; } - // 将字符串转换为时间戳(毫秒) + NSTimeInterval timestamp = [timestampString doubleValue] / 1000.0; if (timestamp <= 0) { @@ -314,7 +310,7 @@ return [formatter stringFromDate:date]; } -/// 格式化时间戳为相对时间 + - (NSString *)formatTimeInterval:(NSInteger)timestamp { if (timestamp <= 0) return YMLocalizedString(@"time.just_now"); @@ -340,20 +336,20 @@ - (void)onLikeButtonTapped { if (!self.currentModel) return; - // 如果已点赞,执行取消点赞 + if (self.currentModel.isLike) { [self performLikeAction:NO]; return; } - // 审核中的动态不可点赞 + if (self.currentModel.status == 0) { NSLog(@"[EPMomentCell] 动态审核中,无法点赞"); // TODO: 可选择显示提示 Toast return; } - // 执行点赞 + [self performLikeAction:YES]; } @@ -364,7 +360,7 @@ NSString *likedUid = self.currentModel.uid; long worldId = self.currentModel.worldId; - // 使用 Swift API Helper + @kWeakify(self); [self.apiHelper likeMomentWithDynamicId:dynamicId isLike:isLike @@ -372,14 +368,14 @@ worldId:worldId completion:^{ @kStrongify(self); - // 更新点赞状态 + self.currentModel.isLike = isLike; NSInteger likeCount = [self.currentModel.likeCount integerValue]; likeCount += isLike ? 1 : -1; - likeCount = MAX(0, likeCount); // 防止负数 + likeCount = MAX(0, likeCount); self.currentModel.likeCount = @(likeCount).stringValue; - // 更新 UI + self.likeButton.selected = self.currentModel.isLike; [self.likeButton setTitle:[NSString stringWithFormat:@" %ld", (long)likeCount] forState:UIControlStateNormal]; [self.likeButton setTitle:[NSString stringWithFormat:@" %ld", (long)likeCount] forState:UIControlStateSelected]; @@ -390,10 +386,6 @@ }]; } -// 评论功能已隐藏 -// - (void)onCommentButtonTapped { -// NSLog(@"[EPMomentCell] 评论"); -// } - (void)onImageTapped:(UITapGestureRecognizer *)gesture { if (!self.currentModel || !self.currentModel.dynamicResList.count) return; @@ -436,9 +428,9 @@ - (UIView *)cardView { if (!_cardView) { _cardView = [[UIView alloc] init]; - _cardView.backgroundColor = [UIColor clearColor]; // 透明背景,颜色由 colorBackgroundView 提供 - _cardView.layer.cornerRadius = 12; // 圆角 - // Shadow 将由 applyEmotionColorEffect 动态设置 + _cardView.backgroundColor = [UIColor clearColor]; + _cardView.layer.cornerRadius = 12; + _cardView.layer.masksToBounds = NO; } return _cardView; @@ -507,7 +499,7 @@ - (UIView *)actionBar { if (!_actionBar) { _actionBar = [[UIView alloc] init]; - _actionBar.backgroundColor = [UIColor clearColor]; // 半透明白色,与毛玻璃效果搭配 + _actionBar.backgroundColor = [UIColor clearColor]; } return _actionBar; } @@ -526,7 +518,7 @@ return _likeButton; } -// 评论按钮已移除 + - (UIButton *)commentButton { return nil; } diff --git a/YuMi/E-P/NewMoments/Views/EPMomentListView.m b/YuMi/E-P/NewMoments/Views/EPMomentListView.m index 18ff7e1..88e1248 100644 --- a/YuMi/E-P/NewMoments/Views/EPMomentListView.m +++ b/YuMi/E-P/NewMoments/Views/EPMomentListView.m @@ -1,9 +1,7 @@ -// -// EPMomentListView.m -// YuMi -// + + // Created by AI on 2025-10-10. -// + #import #import "EPMomentListView.h" @@ -34,7 +32,7 @@ _api = [[EPMomentAPISwiftHelper alloc] init]; _mutableRawList = [NSMutableArray array]; _sourceType = EPMomentListSourceTypeRecommend; - _isLocalMode = NO; // 明确初始化为网络模式 + _isLocalMode = NO; [self addSubview:self.tableView]; [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) { @@ -49,8 +47,10 @@ } - (void)reloadFirstPage { + NSLog(@"[EPMomentListView] 📄 开始刷新第一页,isLocalMode=%d", self.isLocalMode); + if (self.isLocalMode) { - // 本地模式:调用外部刷新回调 + if (self.refreshCallback) { self.refreshCallback(); } @@ -58,7 +58,7 @@ return; } - // 网络模式:重新请求第一页 + self.nextID = @""; [self.mutableRawList removeAllObjects]; [self.tableView reloadData]; @@ -76,7 +76,7 @@ [self.mutableRawList addObjectsFromArray:dynamicInfo]; } - // 隐藏加载更多 footer + self.tableView.mj_footer.hidden = YES; [self.tableView reloadData]; @@ -84,20 +84,27 @@ } - (void)requestNextPage { - if (self.isLoading) return; + if (self.isLoading) { + NSLog(@"[EPMomentListView] ⚠️ 已有加载任务进行中,跳过本次请求"); + return; + } + + NSLog(@"[EPMomentListView] 🌐 发起网络请求,nextID=%@", self.nextID.length > 0 ? self.nextID : @"(首页)"); self.isLoading = YES; @kWeakify(self); [self.api fetchLatestMomentsWithNextID:self.nextID completion:^(NSArray * _Nonnull list, NSString * _Nonnull nextMomentID) { @kStrongify(self); + NSLog(@"[EPMomentListView] ✅ 请求成功,获得 %lu 条数据", (unsigned long)list.count); [self endLoading]; if (list.count > 0) { - // 处理情绪颜色 + [self processEmotionColors:list isFirstPage:(self.nextID.length == 0)]; self.nextID = nextMomentID; [self.mutableRawList addObjectsFromArray:list]; + [self removeEmptyState]; [self.tableView reloadData]; if (nextMomentID.length > 0) { [self.tableView.mj_footer endRefreshing]; @@ -105,13 +112,21 @@ [self.tableView.mj_footer endRefreshingWithNoMoreData]; } } else { - // 返回空数据:显示 "no more data" 状态 + NSLog(@"[EPMomentListView] ⚠️ 返回数据为空"); + if (self.mutableRawList.count == 0) { + [self showEmptyStateWithMessage:YMLocalizedString(@"common.no_data")]; + } [self.tableView.mj_footer endRefreshingWithNoMoreData]; } } failure:^(NSInteger code, NSString * _Nonnull msg) { @kStrongify(self); + NSLog(@"[EPMomentListView] ❌ 请求失败,code=%ld, msg=%@", (long)code, msg); [self endLoading]; - // TODO: 完全没有数据情况下,后续补充数据异常页面 + + + if (self.mutableRawList.count == 0) { + [self showEmptyStateWithMessage:msg ?: YMLocalizedString(@"error.request_failed")]; + } [self.tableView.mj_footer endRefreshing]; }]; } @@ -121,25 +136,48 @@ [self.refreshControl endRefreshing]; } -/// 处理动态的情绪颜色(从 UserDefaults 匹配 + 处理临时颜色) + +- (void)showEmptyStateWithMessage:(NSString *)message { + UILabel *emptyLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + emptyLabel.text = [NSString stringWithFormat:@"%@\n\n%@", message, YMLocalizedString(@"common.pull_to_retry")]; + emptyLabel.textColor = [UIColor whiteColor]; + emptyLabel.textAlignment = NSTextAlignmentCenter; + emptyLabel.numberOfLines = 0; + emptyLabel.font = [UIFont systemFontOfSize:15]; + emptyLabel.tag = 9999; + + + [[self.tableView viewWithTag:9999] removeFromSuperview]; + [self.tableView addSubview:emptyLabel]; + [emptyLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.center.equalTo(self.tableView); + make.leading.trailing.equalTo(self.tableView).inset(40); + }]; +} + +- (void)removeEmptyState { + [[self.tableView viewWithTag:9999] removeFromSuperview]; +} + + - (void)processEmotionColors:(NSArray *)list isFirstPage:(BOOL)isFirstPage { - // 检查是否有待处理的临时情绪颜色 + NSString *pendingColor = [[NSUserDefaults standardUserDefaults] stringForKey:@"EP_Pending_Emotion_Color"]; NSNumber *pendingTimestamp = [[NSUserDefaults standardUserDefaults] objectForKey:@"EP_Pending_Emotion_Timestamp"]; for (NSInteger i = 0; i < list.count; i++) { MomentsInfoModel *model = list[i]; - // 优先检查临时颜色(仅第一页第一条) + if (isFirstPage && i == 0 && pendingColor && pendingTimestamp) { - // 检查时间戳(5秒内有效,避免误匹配) + NSTimeInterval now = [[NSDate date] timeIntervalSince1970]; NSTimeInterval pending = pendingTimestamp.doubleValue; if ((now - pending) < 5.0) { model.emotionColor = pendingColor; - // 保存到持久化存储 + [EPEmotionColorStorage saveColor:pendingColor forDynamicId:model.dynamicId]; - // 清除临时数据 + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"EP_Pending_Emotion_Color"]; [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"EP_Pending_Emotion_Timestamp"]; [[NSUserDefaults standardUserDefaults] synchronize]; @@ -147,12 +185,12 @@ } } - // 从持久化存储中匹配 + NSString *savedColor = [EPEmotionColorStorage colorForDynamicId:model.dynamicId]; if (savedColor) { model.emotionColor = savedColor; } else { - // 无保存颜色,生成随机颜色并立即持久化 + NSString *randomColor = [EPEmotionColorStorage randomEmotionColor]; model.emotionColor = randomColor; [EPEmotionColorStorage saveColor:randomColor forDynamicId:model.dynamicId]; @@ -190,7 +228,7 @@ } - (void)scrollViewDidScroll:(UIScrollView *)scrollView { - // 本地模式下不触发加载更多 + if (self.isLocalMode) return; CGFloat offsetY = scrollView.contentOffset.y; @@ -213,13 +251,13 @@ _tableView.estimatedRowHeight = 200; _tableView.rowHeight = UITableViewAutomaticDimension; _tableView.showsVerticalScrollIndicator = NO; - // 底部留出更高空间,避免被悬浮 TabBar 遮挡 + _tableView.contentInset = UIEdgeInsetsMake(10, 0, 120, 0); _tableView.scrollIndicatorInsets = UIEdgeInsetsMake(10, 0, 120, 0); [_tableView registerClass:[EPMomentCell class] forCellReuseIdentifier:@"NewMomentCell"]; _tableView.refreshControl = self.refreshControl; - // MJRefresh Footer - 加载更多 + __weak typeof(self) weakSelf = self; MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{ __strong typeof(weakSelf) self = weakSelf; @@ -231,7 +269,7 @@ [self.tableView.mj_footer endRefreshing]; } }]; - // 设置白色文字和指示器 + footer.stateLabel.textColor = [UIColor whiteColor]; footer.loadingView.color = [UIColor whiteColor]; _tableView.mj_footer = footer; @@ -242,12 +280,10 @@ - (UIRefreshControl *)refreshControl { if (!_refreshControl) { _refreshControl = [[UIRefreshControl alloc] init]; - _refreshControl.tintColor = [UIColor whiteColor]; // 白色加载指示器 + _refreshControl.tintColor = [UIColor whiteColor]; [_refreshControl addTarget:self action:@selector(reloadFirstPage) forControlEvents:UIControlEventValueChanged]; } return _refreshControl; } @end - - diff --git a/YuMi/E-P/NewMoments/Views/EPSignatureColorGuideView.m b/YuMi/E-P/NewMoments/Views/EPSignatureColorGuideView.m index 9785107..0f622c7 100644 --- a/YuMi/E-P/NewMoments/Views/EPSignatureColorGuideView.m +++ b/YuMi/E-P/NewMoments/Views/EPSignatureColorGuideView.m @@ -1,9 +1,7 @@ -// -// EPSignatureColorGuideView.m -// YuMi -// + + // Created by AI on 2025-10-15. -// + #import "EPSignatureColorGuideView.h" #import "EPEmotionColorWheelView.h" @@ -22,7 +20,7 @@ @property (nonatomic, strong) EPEmotionColorWheelView *colorWheelView; @property (nonatomic, strong) UIButton *confirmButton; @property (nonatomic, strong) UIButton *skipButton; -@property (nonatomic, copy) NSString *selectedColor; // 当前选中的颜色 +@property (nonatomic, copy) NSString *selectedColor; @end @@ -38,7 +36,7 @@ } - (void)setupUI { - // 渐变背景 + CAGradientLayer *gradientLayer = [CAGradientLayer layer]; gradientLayer.colors = @[ (id)[UIColor colorWithRed:0x1a/255.0 green:0x09/255.0 blue:0x33/255.0 alpha:1.0].CGColor, @@ -49,29 +47,30 @@ [self.layer insertSublayer:gradientLayer atIndex:0]; self.gradientLayer = gradientLayer; - // 内容容器 + [self addSubview:self.contentContainer]; [self.contentContainer mas_makeConstraints:^(MASConstraintMaker *make) { - make.center.equalTo(self); + make.top.equalTo(self).offset(100); make.leading.trailing.equalTo(self).inset(30); + make.bottom.lessThanOrEqualTo(self).offset(-30); }]; - // 标题 + [self.contentContainer addSubview:self.titleLabel]; [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.contentContainer); make.centerX.equalTo(self.contentContainer); }]; - // 副标题 + [self.contentContainer addSubview:self.subtitleLabel]; [self.subtitleLabel mas_makeConstraints:^(MASConstraintMaker *make) { - make.top.equalTo(self.titleLabel.mas_bottom).offset(12); + make.top.equalTo(self.titleLabel.mas_bottom).offset(8); make.centerX.equalTo(self.contentContainer); make.leading.trailing.equalTo(self.contentContainer).inset(20); }]; - // Info 按钮(左上角,与 Skip 按钮对齐) + [self addSubview:self.infoButton]; [self.infoButton mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.equalTo(self).offset(20); @@ -79,33 +78,34 @@ make.size.mas_equalTo(CGSizeMake(36, 36)); }]; - // 选中状态显示区域 + [self.contentContainer addSubview:self.selectedColorView]; [self.selectedColorView mas_makeConstraints:^(MASConstraintMaker *make) { - make.top.equalTo(self.subtitleLabel.mas_bottom).offset(30); + make.top.equalTo(self.subtitleLabel.mas_bottom).offset(20); make.centerX.equalTo(self.contentContainer); make.height.mas_equalTo(60); make.leading.trailing.equalTo(self.contentContainer).inset(40); }]; - // 色轮视图(使用共享组件) + [self.contentContainer addSubview:self.colorWheelView]; [self.colorWheelView mas_makeConstraints:^(MASConstraintMaker *make) { - make.top.equalTo(self.selectedColorView.mas_bottom).offset(30); + make.top.equalTo(self.selectedColorView.mas_bottom).offset(20); make.centerX.equalTo(self.contentContainer); - make.size.mas_equalTo(CGSizeMake(360, 360)); // 从280x280增加到360x360 + CGFloat wheelSize = MIN(300, [UIScreen mainScreen].bounds.size.width - 80); + make.size.mas_equalTo(CGSizeMake(wheelSize, wheelSize)); }]; - // 确认按钮 + [self.contentContainer addSubview:self.confirmButton]; [self.confirmButton mas_makeConstraints:^(MASConstraintMaker *make) { - make.top.equalTo(self.colorWheelView.mas_bottom).offset(50); + make.top.equalTo(self.colorWheelView.mas_bottom).offset(30); make.leading.trailing.equalTo(self.contentContainer).inset(20); make.height.mas_equalTo(56); make.bottom.equalTo(self.contentContainer); }]; - // Skip 按钮(右上角,初始隐藏) + [self addSubview:self.skipButton]; [self.skipButton mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self).offset(60); @@ -116,7 +116,7 @@ - (void)layoutSubviews { [super layoutSubviews]; - // 更新渐变层 frame + self.gradientLayer.frame = self.bounds; } @@ -125,22 +125,22 @@ - (void)onConfirmButtonTapped { if (!self.selectedColor) return; - // 执行回调 + if (self.onColorConfirmed) { self.onColorConfirmed(self.selectedColor); } - // 关闭引导页 + [self dismiss]; } - (void)onSkipButtonTapped { - // 执行 skip 回调 + if (self.onSkipTapped) { self.onSkipTapped(); } - // 关闭引导页 + [self dismiss]; } @@ -161,14 +161,14 @@ make.edges.equalTo(window); }]; - // 控制 Skip 按钮显示 + self.skipButton.hidden = !showSkip; - // 初始状态 + self.alpha = 0; self.contentContainer.transform = CGAffineTransformMakeScale(0.8, 0.8); - // 显示动画 + [UIView animateWithDuration:0.4 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ self.alpha = 1.0; self.contentContainer.transform = CGAffineTransformIdentity; @@ -223,11 +223,11 @@ _selectedColorView.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.15]; _selectedColorView.layer.cornerRadius = 30; _selectedColorView.layer.masksToBounds = YES; - _selectedColorView.hidden = YES; // 初始隐藏 + _selectedColorView.hidden = YES; + - // 颜色圆点 UIView *colorDot = [[UIView alloc] init]; - colorDot.tag = 100; // 用于后续查找 + colorDot.tag = 100; colorDot.layer.cornerRadius = 16; colorDot.layer.masksToBounds = YES; [_selectedColorView addSubview:colorDot]; @@ -237,7 +237,7 @@ make.size.mas_equalTo(CGSizeMake(32, 32)); }]; - // 情绪名称标签 + [_selectedColorView addSubview:self.selectedColorLabel]; [self.selectedColorLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.equalTo(colorDot.mas_trailing).offset(16); @@ -261,20 +261,21 @@ - (EPEmotionColorWheelView *)colorWheelView { if (!_colorWheelView) { _colorWheelView = [[EPEmotionColorWheelView alloc] init]; - _colorWheelView.radius = 100.0; - _colorWheelView.buttonSize = 54.0; + CGFloat wheelSize = MIN(300, [UIScreen mainScreen].bounds.size.width - 80); + _colorWheelView.radius = wheelSize / 3.0; + _colorWheelView.buttonSize = 48.0; __weak typeof(self) weakSelf = self; _colorWheelView.onColorTapped = ^(NSString *hexColor, NSInteger index) { __strong typeof(weakSelf) self = weakSelf; - // 保存选中的颜色 + self.selectedColor = hexColor; - // 更新选中状态显示 + [self updateSelectedColorDisplay:hexColor index:index]; - // 启用确认按钮 + self.confirmButton.enabled = YES; self.confirmButton.alpha = 1.0; }; @@ -282,26 +283,26 @@ return _colorWheelView; } -/// 更新选中颜色显示 + - (void)updateSelectedColorDisplay:(NSString *)hexColor index:(NSInteger)index { NSArray *emotions = @[@"Joy", @"Sadness", @"Anger", @"Fear", @"Surprise", @"Disgust", @"Trust", @"Anticipation"]; - // 显示选中状态区域 + self.selectedColorView.hidden = NO; - // 更新颜色圆点 + UIView *colorDot = [self.selectedColorView viewWithTag:100]; colorDot.backgroundColor = [self colorFromHex:hexColor]; - // 更新情绪名称 + self.selectedColorLabel.text = emotions[index]; } -/// Hex 转 UIColor + - (UIColor *)colorFromHex:(NSString *)hexString { unsigned rgbValue = 0; NSScanner *scanner = [NSScanner scannerWithString:hexString]; - [scanner setScanLocation:1]; // 跳过 # + [scanner setScanLocation:1]; [scanner scanHexInt:&rgbValue]; return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 @@ -318,7 +319,7 @@ _confirmButton.layer.cornerRadius = 28; _confirmButton.layer.masksToBounds = YES; - // 渐变背景 + CAGradientLayer *gradient = [CAGradientLayer layer]; gradient.colors = @[ (id)[UIColor colorWithRed:0x9B/255.0 green:0x59/255.0 blue:0xB6/255.0 alpha:1.0].CGColor, @@ -326,12 +327,12 @@ ]; gradient.startPoint = CGPointMake(0, 0); gradient.endPoint = CGPointMake(1, 0); - gradient.frame = CGRectMake(0, 0, 1000, 56); // 宽度设大一点 + gradient.frame = CGRectMake(0, 0, 1000, 56); [_confirmButton.layer insertSublayer:gradient atIndex:0]; [_confirmButton addTarget:self action:@selector(onConfirmButtonTapped) forControlEvents:UIControlEventTouchUpInside]; - // 初始禁用状态 + _confirmButton.enabled = NO; _confirmButton.alpha = 0.5; } @@ -342,12 +343,12 @@ if (!_infoButton) { _infoButton = [UIButton buttonWithType:UIButtonTypeCustom]; - // 使用系统 info.circle 图标 + UIImage *infoIcon = [UIImage systemImageNamed:@"info.circle"]; [_infoButton setImage:infoIcon forState:UIControlStateNormal]; _infoButton.tintColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8]; - // 点击效果 + [_infoButton addTarget:self action:@selector(onInfoButtonTapped) forControlEvents:UIControlEventTouchUpInside]; } return _infoButton; @@ -363,10 +364,9 @@ _skipButton.layer.cornerRadius = 18; _skipButton.layer.masksToBounds = YES; [_skipButton addTarget:self action:@selector(onSkipButtonTapped) forControlEvents:UIControlEventTouchUpInside]; - _skipButton.hidden = YES; // 默认隐藏 + _skipButton.hidden = YES; } return _skipButton; } @end - diff --git a/YuMi/E-P/NewTabBar/EPTabBarController.swift b/YuMi/E-P/NewTabBar/EPTabBarController.swift index 31366f4..89e27c3 100644 --- a/YuMi/E-P/NewTabBar/EPTabBarController.swift +++ b/YuMi/E-P/NewTabBar/EPTabBarController.swift @@ -1,33 +1,27 @@ -// -// EPTabBarController.swift -// YuMi -// + + // Created by AI on 2025-10-09. // Copyright © 2025 YuMi. All rights reserved. -// + import UIKit import SnapKit -/// EP 系列 TabBar 控制器 -/// 悬浮设计 + 液态玻璃效果,只包含 Moment 和 Mine 两个 Tab + @objc class EPTabBarController: UITabBarController { // MARK: - Properties - /// 全局事件管理器 - private var globalEventManager: GlobalEventManager? - /// 是否已登录 private var isLoggedIn: Bool = false - /// 自定义悬浮 TabBar 容器 + private var customTabBarView: UIView! - /// 毛玻璃背景视图 + private var tabBarBackgroundView: UIVisualEffectView! - /// Tab 按钮数组 + private var tabButtons: [UIButton] = [] // MARK: - Lifecycle @@ -35,49 +29,47 @@ import SnapKit override func viewDidLoad() { super.viewDidLoad() - // 测试域名配置 + #if DEBUG APIConfig.testEncryption() #endif - // 隐藏原生 TabBar + self.tabBar.isHidden = true - // 设置 delegate 以完全控制切换行为 + self.delegate = self - // ✅ 启动时验证 ticket(与 OC 版本保持一致) + performAutoLogin() setupCustomFloatingTabBar() - setupGlobalManagers() setupInitialViewControllers() NSLog("[EPTabBarController] 悬浮 TabBar 初始化完成") } deinit { - globalEventManager?.removeAllDelegates() NSLog("[EPTabBarController] 已释放") } // MARK: - Setup - /// 设置自定义悬浮 TabBar + private func setupCustomFloatingTabBar() { - // 创建悬浮容器 + customTabBarView = UIView() customTabBarView.translatesAutoresizingMaskIntoConstraints = false customTabBarView.backgroundColor = .clear view.addSubview(customTabBarView) - // 液态玻璃/毛玻璃效果 + let effect: UIVisualEffect if #available(iOS 26.0, *) { - // iOS 26+ 使用液态玻璃(Material) + effect = UIGlassEffect() } else { - // iOS 13-17 使用毛玻璃 + effect = UIBlurEffect(style: .systemMaterial) } @@ -86,13 +78,13 @@ import SnapKit tabBarBackgroundView.layer.cornerRadius = 28 tabBarBackgroundView.layer.masksToBounds = true - // 添加边框 + tabBarBackgroundView.layer.borderWidth = 0.5 tabBarBackgroundView.layer.borderColor = UIColor.white.withAlphaComponent(0.2).cgColor customTabBarView.addSubview(tabBarBackgroundView) - // 简化的布局约束(类似 Masonry 风格) + customTabBarView.snp.makeConstraints { make in make.leading.equalTo(view).offset(16) make.trailing.equalTo(view).offset(-16) @@ -104,26 +96,26 @@ import SnapKit make.edges.equalTo(customTabBarView) } - // 添加 Tab 按钮 + setupTabButtons() NSLog("[EPTabBarController] 悬浮 TabBar 设置完成") } - /// 设置 Tab 按钮 + private func setupTabButtons() { let momentButton = createTabButton( normalImage: "tab_moment_off", selectedImage: "tab_moment_on", tag: 0 ) - // 新增中间 Message 按钮 + let messageButton = createTabButton( normalImage: "tab_message_off", selectedImage: "tab_message_on", tag: 1 ) - + let mineButton = createTabButton( normalImage: "tab_mine_off", selectedImage: "tab_mine_on", @@ -146,23 +138,23 @@ import SnapKit make.bottom.equalTo(tabBarBackgroundView).offset(-8) } - // 默认选中第一个 + updateTabButtonStates(selectedIndex: 0) } - /// 创建 Tab 按钮 + private func createTabButton(normalImage: String, selectedImage: String, tag: Int) -> UIButton { let button = UIButton(type: .custom) button.tag = tag - button.adjustsImageWhenHighlighted = false // 禁用高亮效果,避免闪烁 + button.adjustsImageWhenHighlighted = false + - // 尝试设置自定义图片,如果不存在则使用 SF Symbols if let normalImg = UIImage(named: normalImage), let selectedImg = UIImage(named: selectedImage) { - // 正确设置:分别为 normal 和 selected 状态设置图片 + button.setImage(normalImg, for: .normal) button.setImage(selectedImg, for: .selected) } else { - // 使用 SF Symbols 作为备用 + let fallbackIcons = ["sparkles", "person.circle"] let iconName = fallbackIcons[tag] let imageConfig = UIImage.SymbolConfiguration(pointSize: 24, weight: .medium) @@ -173,14 +165,14 @@ import SnapKit button.tintColor = .white.withAlphaComponent(0.6) } - // 图片渲染模式 + button.imageView?.contentMode = .scaleAspectFit - // 移除标题 + button.setTitle(nil, for: .normal) button.setTitle(nil, for: .selected) - // 设置图片大小约束 + button.imageView?.snp.makeConstraints { make in make.size.equalTo(28) } @@ -189,105 +181,90 @@ import SnapKit return button } - /// Tab 按钮点击事件 + @objc private func tabButtonTapped(_ sender: UIButton) { let newIndex = sender.tag - // 如果点击的是当前已选中的 tab,不做任何操作 + if newIndex == selectedIndex { return } - // 先更新按钮状态 + updateTabButtonStates(selectedIndex: newIndex) - // 禁用 UITabBarController 的默认切换动画,避免闪烁 + UIView.performWithoutAnimation { selectedIndex = newIndex } - let tabNames = [ - YMLocalizedString("tab.moment"), - YMLocalizedString("tab.message"), - YMLocalizedString("tab.mine") - ] + let tabNames = [YMLocalizedString("tab.moment"), + YMLocalizedString("tab.message"), + YMLocalizedString("tab.mine")] NSLog("[EPTabBarController] 选中 Tab: \(tabNames[newIndex])") } - /// 更新 Tab 按钮状态 + private func updateTabButtonStates(selectedIndex: Int) { - // 禁用按钮交互,避免快速点击 + tabButtons.forEach { $0.isUserInteractionEnabled = false } for (index, button) in tabButtons.enumerated() { let isSelected = (index == selectedIndex) - // 直接设置 isSelected 属性即可,图片会自动切换 + button.isSelected = isSelected - // SF Symbols 的情况需要手动更新 tintColor + if button.currentImage?.isSymbolImage == true { button.tintColor = isSelected ? .white : .white.withAlphaComponent(0.6) } - // 选中状态缩放动画 + UIView.animate(withDuration: 0.2, delay: 0, options: [.curveEaseOut], animations: { button.transform = isSelected ? CGAffineTransform(scaleX: 1.1, y: 1.1) : .identity }) } - // 延迟恢复按钮交互 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { self.tabButtons.forEach { $0.isUserInteractionEnabled = true } } } - /// 设置全局管理器 - private func setupGlobalManagers() { - globalEventManager = GlobalEventManager.shared() - globalEventManager?.setupSDKDelegates() - - // TODO: v0.2 版本暂时禁用房间最小化视图(无房间功能) - // 后续版本可通过 Build Configuration 或版本号判断是否启用 - /* - if let containerView = view { - globalEventManager?.setupRoomMiniView(on: containerView) - } - */ - - // 注册社交分享回调 - globalEventManager?.registerSocialShareCallback() - - NSLog("[EPTabBarController] 全局管理器设置完成(v0.2 - 无 MiniRoom)") - } - /// 设置初始 ViewController(未登录状态) private func setupInitialViewControllers() { // 三栏占位(Moment | Message | Mine) - let v1 = UINavigationController(rootViewController: UIViewController()) - v1.view.backgroundColor = .white - v1.tabBarItem = createTabBarItem(title: YMLocalizedString("tab.moment"), normalImage: "tab_moment_normal", selectedImage: "tab_moment_selected") + let momentVC = UIViewController() + momentVC.view.backgroundColor = .systemBlue + momentVC.title = "Moment" + let momentNav = UINavigationController(rootViewController: momentVC) + momentNav.tabBarItem = createTabBarItem(title: YMLocalizedString("tab.moment"), normalImage: "tab_moment_normal", selectedImage: "tab_moment_selected") - let v2 = UINavigationController(rootViewController: UIViewController()) - v2.view.backgroundColor = .white - v2.tabBarItem = createTabBarItem(title: YMLocalizedString("tab.message"), normalImage: "tab_message_normal", selectedImage: "tab_message_selected") + let messageVC = EPMessageMainViewController() + messageVC.title = "Message" + let messageNav = UINavigationController(rootViewController: messageVC) + messageNav.tabBarItem = createTabBarItem(title: YMLocalizedString("tab.message"), normalImage: "tab_message_normal", selectedImage: "tab_message_selected") + + // 角标绑定 + messageVC.unreadCountDidChange = { [weak self] c in + let value: String? = c > 0 ? (c > 99 ? "99+" : "\(c)") : nil + self?.viewControllers?[1].tabBarItem.badgeValue = value + } - let v3 = UINavigationController(rootViewController: UIViewController()) - v3.view.backgroundColor = .white - v3.tabBarItem = createTabBarItem(title: YMLocalizedString("tab.mine"), normalImage: "tab_mine_normal", selectedImage: "tab_mine_selected") + let mineVC = UIViewController() + mineVC.view.backgroundColor = .systemGreen + mineVC.title = "Mine" + let mineNav = UINavigationController(rootViewController: mineVC) + mineNav.tabBarItem = createTabBarItem(title: YMLocalizedString("tab.mine"), normalImage: "tab_mine_normal", selectedImage: "tab_mine_selected") - viewControllers = [v1, v2, v3] + viewControllers = [momentNav, messageNav, mineNav] selectedIndex = 0 NSLog("[EPTabBarController] 初始 ViewControllers 设置完成") } - /// 创建 TabBarItem - /// - Parameters: - /// - title: 标题 - /// - normalImage: 未选中图标名称 - /// - selectedImage: 选中图标名称 - /// - Returns: UITabBarItem + private func createTabBarItem(title: String, normalImage: String, selectedImage: String) -> UITabBarItem { let item = UITabBarItem( title: title, @@ -299,8 +276,7 @@ import SnapKit // MARK: - Public Methods - /// 登录成功后刷新 TabBar - /// - Parameter isLogin: 是否已登录 + func refreshTabBar(isLogin: Bool) { isLoggedIn = isLogin @@ -313,63 +289,50 @@ import SnapKit NSLog("[EPTabBarController] TabBar 已刷新,登录状态: \(isLogin)") } - /// 设置登录后的 ViewControllers + private func setupLoggedInViewControllers() { - // 三栏:Moment | Message | Mine - if viewControllers?.count != 3 || - !(viewControllers?[0] is UINavigationController) || - !(viewControllers?[1] is UINavigationController) || - !(viewControllers?[2] is UINavigationController) { - // 创建动态页 - let momentVC = EPMomentViewController() - momentVC.title = YMLocalizedString("tab.moment") - let momentNav = createTransparentNavigationController( - rootViewController: momentVC, - tabTitle: YMLocalizedString("tab.moment"), - normalImage: "tab_moment_normal", - selectedImage: "tab_moment_selected" - ) - - // 创建消息页(Swift UIKit 容器) - let messageVC = EPMessageMainViewController() - let messageNav = createTransparentNavigationController( - rootViewController: messageVC, - tabTitle: YMLocalizedString("tab.message"), - normalImage: "tab_message_normal", - selectedImage: "tab_message_selected" - ) - - // 角标绑定 - messageVC.unreadCountDidChange = { [weak self] c in - let value: String? = c > 0 ? (c > 99 ? "99+" : "\(c)") : nil - self?.viewControllers?[1].tabBarItem.badgeValue = value - } - - // 创建我的页 - let mineVC = EPMineViewController() - mineVC.title = YMLocalizedString("tab.mine") - let mineNav = createTransparentNavigationController( - rootViewController: mineVC, - tabTitle: YMLocalizedString("tab.mine"), - normalImage: "tab_mine_normal", - selectedImage: "tab_mine_selected" - ) - - viewControllers = [momentNav, messageNav, mineNav] - NSLog("[EPTabBarController] 登录后 ViewControllers 创建完成 - Moment & Message & Mine") + // 创建动态页 + let momentVC = EPMomentViewController() + momentVC.title = YMLocalizedString("tab.moment") + let momentNav = createTransparentNavigationController( + rootViewController: momentVC, + tabTitle: YMLocalizedString("tab.moment"), + normalImage: "tab_moment_normal", + selectedImage: "tab_moment_selected" + ) + + // 创建消息页(Swift UIKit 容器) + let messageVC = EPMessageMainViewController() + let messageNav = createTransparentNavigationController( + rootViewController: messageVC, + tabTitle: YMLocalizedString("tab.message"), + normalImage: "tab_message_normal", + selectedImage: "tab_message_selected" + ) + // 角标绑定 + messageVC.unreadCountDidChange = { [weak self] c in + let value: String? = c > 0 ? (c > 99 ? "99+" : "\(c)") : nil + self?.viewControllers?[1].tabBarItem.badgeValue = value } + // 创建我的页 + let mineVC = EPMineViewController() + mineVC.title = YMLocalizedString("tab.mine") + let mineNav = createTransparentNavigationController( + rootViewController: mineVC, + tabTitle: YMLocalizedString("tab.mine"), + normalImage: "tab_mine_normal", + selectedImage: "tab_mine_selected" + ) + + viewControllers = [momentNav, messageNav, mineNav] + NSLog("[EPTabBarController] 登录后 ViewControllers 创建完成 - Moment & Message & Mine") + selectedIndex = 0 } - /// 创建透明导航控制器(统一配置) - /// - Parameters: - /// - rootViewController: 根视图控制器 - /// - tabTitle: TabBar 标题 - /// - normalImage: 未选中图标 - /// - selectedImage: 选中图标 - /// - Returns: 配置好的 UINavigationController + private func createTransparentNavigationController( rootViewController: UIViewController, tabTitle: String, @@ -387,7 +350,7 @@ import SnapKit selectedImage: selectedImage ) - // 设置 delegate 以监听页面切换 + nav.delegate = self return nav @@ -395,7 +358,7 @@ import SnapKit // MARK: - TabBar Visibility Control - /// 显示悬浮 TabBar + private func showCustomTabBar(animated: Bool = true) { guard customTabBarView.isHidden else { return } @@ -414,7 +377,7 @@ import SnapKit } } - /// 隐藏悬浮 TabBar + private func hideCustomTabBar(animated: Bool = true) { guard !customTabBarView.isHidden else { return } @@ -441,18 +404,18 @@ extension EPTabBarController: UITabBarControllerDelegate { NSLog("[EPTabBarController] 选中 Tab: \(item.title ?? "Unknown")") } - /// 禁用系统默认的切换动画 - func tabBarController(_ tabBarController: UITabBarController, - animationControllerForTransitionFrom fromVC: UIViewController, + + func tabBarController(_ tabBarController: UITabBarController, + animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { - // 返回 nil 表示不使用动画 + return nil } - /// 完全控制是否允许切换 - func tabBarController(_ tabBarController: UITabBarController, + + func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { - // 允许切换,但通过返回 nil 的 animationController 来禁用动画 + return true } } @@ -461,19 +424,19 @@ extension EPTabBarController: UITabBarControllerDelegate { extension EPTabBarController: UINavigationControllerDelegate { - func navigationController(_ navigationController: UINavigationController, - willShow viewController: UIViewController, + func navigationController(_ navigationController: UINavigationController, + willShow viewController: UIViewController, animated: Bool) { - // 判断是否是根页面(一级页面) + let isRootViewController = navigationController.viewControllers.count == 1 if isRootViewController { - // 一级页面:显示 TabBar + showCustomTabBar(animated: animated) NSLog("[EPTabBarController] 显示 TabBar - 根页面") } else { - // 二级及以上页面:隐藏 TabBar + hideCustomTabBar(animated: animated) NSLog("[EPTabBarController] 隐藏 TabBar - 子页面 (层级: \(navigationController.viewControllers.count))") } @@ -484,16 +447,16 @@ extension EPTabBarController: UINavigationControllerDelegate { extension EPTabBarController { - /// 自动登录:验证 ticket 有效性(与 OC MainPresenter.autoLogin 保持一致) + private func performAutoLogin() { - // 1. 检查账号信息 + guard let accountModel = AccountInfoStorage.instance().getCurrentAccountInfo() else { NSLog("[EPTabBarController] ⚠️ 账号信息不存在,跳转到登录页") handleTokenInvalid() return } - // 2. 检查 uid 和 access_token + let uid = accountModel.uid let accessToken = accountModel.access_token @@ -503,14 +466,14 @@ extension EPTabBarController { return } - // 3. 检查 ticket 是否已存在(内存缓存) + let existingTicket = AccountInfoStorage.instance().getTicket() ?? "" if !existingTicket.isEmpty { NSLog("[EPTabBarController] ✅ Ticket 已存在,自动登录成功") return } - // 4. Ticket 不存在,请求新的 ticket + NSLog("[EPTabBarController] 🔄 Ticket 不存在,正在请求...") let loginService = EPLoginService() @@ -520,28 +483,28 @@ extension EPTabBarController { } failure: { [weak self] code, msg in NSLog("[EPTabBarController] ❌ Ticket 请求失败 (\(code)): \(msg)") - // ⚠️ Ticket 失败,强制退出登录(与 OC MainPresenter 保持一致) + DispatchQueue.main.async { self?.handleTokenInvalid() } } } - /// 处理 Token 失效:清空数据并跳转到登录页 + private func handleTokenInvalid() { NSLog("[EPTabBarController] ⚠️ Token 失效,清空账号数据...") - // 1. 清空账号信息 + AccountInfoStorage.instance().saveAccountInfo(nil) AccountInfoStorage.instance().saveTicket("") - // 2. 跳转到登录页 + DispatchQueue.main.async { let loginVC = EPLoginViewController() let nav = BaseNavigationController(rootViewController: loginVC) nav.modalPresentationStyle = .fullScreen - // 获取 keyWindow(iOS 13+ 兼容) + if #available(iOS 13.0, *) { for scene in UIApplication.shared.connectedScenes { if let windowScene = scene as? UIWindowScene, @@ -568,12 +531,12 @@ extension EPTabBarController { extension EPTabBarController { - /// OC 兼容:创建实例的工厂方法 + @objc static func create() -> EPTabBarController { return EPTabBarController() } - /// OC 兼容:刷新 TabBar 方法 + @objc func refreshTabBarWithIsLogin(_ isLogin: Bool) { refreshTabBar(isLogin: isLogin) } diff --git a/YuMi/Modules/YMRoom/View/BackMusic/Presenter/XPCoreDataManager.m b/YuMi/Modules/YMRoom/View/BackMusic/Presenter/XPCoreDataManager.m index 098a01e..a72ee0b 100644 --- a/YuMi/Modules/YMRoom/View/BackMusic/Presenter/XPCoreDataManager.m +++ b/YuMi/Modules/YMRoom/View/BackMusic/Presenter/XPCoreDataManager.m @@ -65,7 +65,7 @@ static XPCoreDataManager *manager = nil; * URL:要保存的文件路径 * options:参数信息 一般无需设置 */ - NSURL *url = [[self getDocumnetUrlpath] URLByAppendingPathComponent:@"sqlit.db" isDirectory:true]; + NSURL *url = [[self getDocumnetUrlpath] URLByAppendingPathComponent:@"sqlit.db" isDirectory:NO]; [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:nil]; } return _persistentStoreCoordinator;