Browse Source

第三方库导入暂时提交

yanxuyao 2 years ago
parent
commit
aa3cf74c87
100 changed files with 2297 additions and 4 deletions
  1. 36 0
      MiMoLive/MiMoLive.xcodeproj/project.pbxproj
  2. BIN
      MiMoLive/MiMoLive.xcworkspace/xcuserdata/yanxuyao.xcuserdatad/UserInterfaceState.xcuserstate
  3. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/CatSparks.png
  4. 61 0
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/Root.plist
  5. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/en.lproj/Localizable.strings
  6. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_baixi@2x.png
  7. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_baixi@3x.png
  8. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_cwei@2x.png
  9. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_cwei@3x.png
  10. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_tianmei@2x.png
  11. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_tianmei@3x.png
  12. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_yuanqi@2x.png
  13. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_yuanqi@3x.png
  14. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_chongzhi@2x.png
  15. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_chongzhi@3x.png
  16. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_dayan@2x.png
  17. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_dayan@3x.png
  18. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_etou@2x.png
  19. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_etou@3x.png
  20. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_liangyan@2x.png
  21. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_liangyan@3x.png
  22. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_meibai@2x.png
  23. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_meibai@3x.png
  24. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_meiya@2x.png
  25. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_meiya@3x.png
  26. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_mopi@2x.png
  27. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_mopi@3x.png
  28. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_shoubi@2x.png
  29. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_shoubi@3x.png
  30. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_shoulian@2x.png
  31. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_shoulian@3x.png
  32. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_shouquangu@2x.png
  33. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_shouquangu@3x.png
  34. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_wenhao@2x.png
  35. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_wenhao@3x.png
  36. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_xiaba@2x.png
  37. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_xiaba@3x.png
  38. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_xiahegu@2x.png
  39. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_xiahegu@3x.png
  40. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_zuixing@2x.png
  41. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_zuixing@3x.png
  42. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_beihaidao@2x.png
  43. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_beihaidao@3x.png
  44. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_haibian@2x.png
  45. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_haibian@3x.png
  46. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_luolita@2x.png
  47. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_luolita@3x.png
  48. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_lvtu@2x.png
  49. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_lvtu@3x.png
  50. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_makalong@2x.png
  51. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_makalong@3x.png
  52. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_mitao@2x.png
  53. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_mitao@3x.png
  54. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_naiyou@2x.png
  55. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_naiyou@3x.png
  56. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_wuyu@2x.png
  57. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_wuyu@3x.png
  58. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_yangqi@2x.png
  59. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_yangqi@3x.png
  60. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_yinghua@2x.png
  61. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_yinghua@3x.png
  62. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/sdlu.png
  63. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_compare@2x.png
  64. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_compare@3x.png
  65. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_ic_adjust_clear.png
  66. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_ic_adjust_contrast.png
  67. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_ic_adjust_saturation.png
  68. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_ic_adjust_sharp.png
  69. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_none@2x.png
  70. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_none@3x.png
  71. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_selected@2x.png
  72. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_selected@3x.png
  73. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_bg_blur@2x.png
  74. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_bg_blur@3x.png
  75. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_bg_peach@2x.png
  76. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_bg_peach@3x.png
  77. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/xueqiu_lm_fu.png
  78. BIN
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/zh-Hans.lproj/Localizable.strings
  79. 216 0
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautySetVC/BeautyBaseVC.swift
  80. 75 0
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautySetVC/BeautyMenuItemCell.swift
  81. 178 0
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautySetVC/BeautyMenuItemVC.swift
  82. 42 0
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautySetVC/UIColor+Hex.swift
  83. 73 0
      MiMoLive/MiMoLive/3rdParty/SenseRender/BeautySetVC/UIImage+Show.swift
  84. 32 0
      MiMoLive/MiMoLive/3rdParty/SenseRender/String+Localizable.swift
  85. 14 2
      MiMoLive/MiMoLive/Classes/Live/MOCreateLiveVC.m
  86. 6 2
      MiMoLive/Podfile.lock
  87. 21 0
      MiMoLive/Pods/JXCategoryView/LICENSE
  88. 296 0
      MiMoLive/Pods/JXCategoryView/README.md
  89. 29 0
      MiMoLive/Pods/JXCategoryView/Sources/Base/JXCategoryBaseCell.h
  90. 98 0
      MiMoLive/Pods/JXCategoryView/Sources/Base/JXCategoryBaseCell.m
  91. 31 0
      MiMoLive/Pods/JXCategoryView/Sources/Base/JXCategoryBaseCellModel.h
  92. 13 0
      MiMoLive/Pods/JXCategoryView/Sources/Base/JXCategoryBaseCellModel.m
  93. 222 0
      MiMoLive/Pods/JXCategoryView/Sources/Base/JXCategoryBaseView.h
  94. 687 0
      MiMoLive/Pods/JXCategoryView/Sources/Base/JXCategoryBaseView.m
  95. 24 0
      MiMoLive/Pods/JXCategoryView/Sources/Common/JXCategoryCollectionView.h
  96. 52 0
      MiMoLive/Pods/JXCategoryView/Sources/Common/JXCategoryCollectionView.m
  97. 18 0
      MiMoLive/Pods/JXCategoryView/Sources/Common/JXCategoryFactory.h
  98. 29 0
      MiMoLive/Pods/JXCategoryView/Sources/Common/JXCategoryFactory.m
  99. 31 0
      MiMoLive/Pods/JXCategoryView/Sources/Common/JXCategoryIndicatorParamsModel.h
  100. 13 0
      MiMoLive/Pods/JXCategoryView/Sources/Common/JXCategoryIndicatorParamsModel.m

+ 36 - 0
MiMoLive/MiMoLive.xcodeproj/project.pbxproj

@@ -91,6 +91,13 @@
 		A62478052AD55523000C2E10 /* UITableViewCell+POAccessory.m in Sources */ = {isa = PBXBuildFile; fileRef = A62478032AD55523000C2E10 /* UITableViewCell+POAccessory.m */; };
 		A62478082AD55D05000C2E10 /* MOImageBaseTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A62478072AD55D05000C2E10 /* MOImageBaseTableViewCell.m */; };
 		A62CC8792AFA2F6A00FAB3AA /* MOCreateLiveView.xib in Resources */ = {isa = PBXBuildFile; fileRef = A62CC8782AFA2F6A00FAB3AA /* MOCreateLiveView.xib */; };
+		A62CC87B2AFA51A100FAB3AA /* BeautyBaseVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A62CC87A2AFA51A000FAB3AA /* BeautyBaseVC.swift */; };
+		A62CC87D2AFA51B400FAB3AA /* String+Localizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A62CC87C2AFA51B400FAB3AA /* String+Localizable.swift */; };
+		A62CC87F2AFA52B700FAB3AA /* UIColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A62CC87E2AFA52B700FAB3AA /* UIColor+Hex.swift */; };
+		A62CC8822AFA52DE00FAB3AA /* BeautyMenuItemVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A62CC8802AFA52DE00FAB3AA /* BeautyMenuItemVC.swift */; };
+		A62CC8832AFA52DE00FAB3AA /* BeautyMenuItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A62CC8812AFA52DE00FAB3AA /* BeautyMenuItemCell.swift */; };
+		A62CC8852AFA531700FAB3AA /* UIImage+Show.swift in Sources */ = {isa = PBXBuildFile; fileRef = A62CC8842AFA531700FAB3AA /* UIImage+Show.swift */; };
+		A62CC88A2AFA561900FAB3AA /* BeautyResource.bundle in Resources */ = {isa = PBXBuildFile; fileRef = A62CC8892AFA561900FAB3AA /* BeautyResource.bundle */; };
 		A6320ED52ADBC29300F2F846 /* MOLoginChooseVC.m in Sources */ = {isa = PBXBuildFile; fileRef = A6320ED32ADBC29300F2F846 /* MOLoginChooseVC.m */; };
 		A6320ED62ADBC29300F2F846 /* MOLoginChooseVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = A6320ED42ADBC29300F2F846 /* MOLoginChooseVC.xib */; };
 		A6320EDA2ADBCEE200F2F846 /* MOPwdAndSMSLoginVC.m in Sources */ = {isa = PBXBuildFile; fileRef = A6320ED82ADBCEE200F2F846 /* MOPwdAndSMSLoginVC.m */; };
@@ -458,6 +465,13 @@
 		A62478062AD55D05000C2E10 /* MOImageBaseTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MOImageBaseTableViewCell.h; sourceTree = "<group>"; };
 		A62478072AD55D05000C2E10 /* MOImageBaseTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MOImageBaseTableViewCell.m; sourceTree = "<group>"; };
 		A62CC8782AFA2F6A00FAB3AA /* MOCreateLiveView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MOCreateLiveView.xib; sourceTree = "<group>"; };
+		A62CC87A2AFA51A000FAB3AA /* BeautyBaseVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BeautyBaseVC.swift; sourceTree = "<group>"; };
+		A62CC87C2AFA51B400FAB3AA /* String+Localizable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Localizable.swift"; sourceTree = "<group>"; };
+		A62CC87E2AFA52B700FAB3AA /* UIColor+Hex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Hex.swift"; sourceTree = "<group>"; };
+		A62CC8802AFA52DE00FAB3AA /* BeautyMenuItemVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BeautyMenuItemVC.swift; sourceTree = "<group>"; };
+		A62CC8812AFA52DE00FAB3AA /* BeautyMenuItemCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BeautyMenuItemCell.swift; sourceTree = "<group>"; };
+		A62CC8842AFA531700FAB3AA /* UIImage+Show.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Show.swift"; sourceTree = "<group>"; };
+		A62CC8892AFA561900FAB3AA /* BeautyResource.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = BeautyResource.bundle; sourceTree = "<group>"; };
 		A6320ED22ADBC29300F2F846 /* MOLoginChooseVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MOLoginChooseVC.h; sourceTree = "<group>"; };
 		A6320ED32ADBC29300F2F846 /* MOLoginChooseVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MOLoginChooseVC.m; sourceTree = "<group>"; };
 		A6320ED42ADBC29300F2F846 /* MOLoginChooseVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MOLoginChooseVC.xib; sourceTree = "<group>"; };
@@ -1290,6 +1304,18 @@
 			path = Controller;
 			sourceTree = "<group>";
 		};
+		A62CC8862AFA532900FAB3AA /* BeautySetVC */ = {
+			isa = PBXGroup;
+			children = (
+				A62CC87A2AFA51A000FAB3AA /* BeautyBaseVC.swift */,
+				A62CC8802AFA52DE00FAB3AA /* BeautyMenuItemVC.swift */,
+				A62CC8812AFA52DE00FAB3AA /* BeautyMenuItemCell.swift */,
+				A62CC8842AFA531700FAB3AA /* UIImage+Show.swift */,
+				A62CC87E2AFA52B700FAB3AA /* UIColor+Hex.swift */,
+			);
+			path = BeautySetVC;
+			sourceTree = "<group>";
+		};
 		A6320ED12ADBC25B00F2F846 /* ViewController */ = {
 			isa = PBXGroup;
 			children = (
@@ -1977,6 +2003,8 @@
 		A6D753962AD68F0D00553091 /* SenseRender */ = {
 			isa = PBXGroup;
 			children = (
+				A62CC8892AFA561900FAB3AA /* BeautyResource.bundle */,
+				A62CC8862AFA532900FAB3AA /* BeautySetVC */,
 				A6D753982AD68F2800553091 /* BundleUtil.h */,
 				A6D753972AD68F2800553091 /* BundleUtil.m */,
 				A6D753932AD6893000553091 /* BeautyAPI.h */,
@@ -1986,6 +2014,7 @@
 				A65D42EC2AFA186F006523ED /* BeautyModel.swift */,
 				A65D42EE2AFA1AA9006523ED /* NSString+Show.swift */,
 				A65D42F02AFA1AC8006523ED /* AppContext.swift */,
+				A62CC87C2AFA51B400FAB3AA /* String+Localizable.swift */,
 				A6D7538F2AD6892800553091 /* SenseRender */,
 			);
 			path = SenseRender;
@@ -2160,6 +2189,7 @@
 				A6D751A22AD67D5F00553091 /* model.bundle in Resources */,
 				A6D753832AD688DC00553091 /* README.zh.md in Resources */,
 				A63664B52AF1374C002C0BFC /* MOFamilyPrivilegeView.xib in Resources */,
+				A62CC88A2AFA561900FAB3AA /* BeautyResource.bundle in Resources */,
 				A601BAF12AD80C130021CF03 /* MOPersonlProfileVC.xib in Resources */,
 				A68C053F2AE926E90080F6EC /* MOGuildApplyView.xib in Resources */,
 				A61944072AE53B4D00D2878F /* MOModifyNickNameVC.xib in Resources */,
@@ -2298,6 +2328,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				A62CC8852AFA531700FAB3AA /* UIImage+Show.swift in Sources */,
 				A63934C02AE0115E00D0EA84 /* MOLiveBase.m in Sources */,
 				A6C299952AEA510500D76DA7 /* MOGuildApplyVC.m in Sources */,
 				A68860B02AE8F53400B87861 /* MOPersonData.m in Sources */,
@@ -2309,6 +2340,7 @@
 				A608EBC02AE66EEC0092317D /* MOLanguageBaseCell.m in Sources */,
 				A6C1FB5B2AEE3BD8000908C9 /* MONameplateCell.m in Sources */,
 				A69D8A6B2AE10836004FCE66 /* MOCountryList.m in Sources */,
+				A62CC87D2AFA51B400FAB3AA /* String+Localizable.swift in Sources */,
 				A601BEB72AD829050021CF03 /* HzUUIDTools.m in Sources */,
 				A69D8A6C2AE10836004FCE66 /* MOCountryBaseData.m in Sources */,
 				A601BAE12AD7EE1A0021CF03 /* MOEditInfoNormalCell.m in Sources */,
@@ -2403,8 +2435,10 @@
 				A618DC3B2AEA9FD000976BB8 /* MOFanClubBeginVC.m in Sources */,
 				A6733DAD2AF27B1F00F57852 /* MOIntroductionCell.m in Sources */,
 				A601BAEC2AD8001B0021CF03 /* MONetAPIClient.m in Sources */,
+				A62CC87B2AFA51A100FAB3AA /* BeautyBaseVC.swift in Sources */,
 				A6C1FB832AEE921D000908C9 /* MOAnchorApplyData.m in Sources */,
 				A6D7538C2AD688DC00553091 /* EffectsLicense.m in Sources */,
+				A62CC8822AFA52DE00FAB3AA /* BeautyMenuItemVC.swift in Sources */,
 				A63DDCA02AD17BF900569758 /* main.m in Sources */,
 				A6803B472ADFB69D00D4AB96 /* MOScrollMenuView.m in Sources */,
 				A68C053D2AE9265E0080F6EC /* MOGuildApplyView.m in Sources */,
@@ -2429,6 +2463,7 @@
 				A636646B2AF0CFB5002C0BFC /* YCXMenu.m in Sources */,
 				A6C2999B2AEA53D300D76DA7 /* MOGuildInputBaseCell.m in Sources */,
 				A6A63E9B2AF3927C009ED032 /* MOFanClubSetVC.m in Sources */,
+				A62CC87F2AFA52B700FAB3AA /* UIColor+Hex.swift in Sources */,
 				A6D753952AD6893100553091 /* BeautyAPI.m in Sources */,
 				A6C299A02AEA541800D76DA7 /* MOGuildImageOrTextBaseCell.m in Sources */,
 				A601BACE2AD7C7F60021CF03 /* MoFile.swift in Sources */,
@@ -2469,6 +2504,7 @@
 				A60CAEC42ADD65D4007F73C7 /* MOSelectPartitionCell.m in Sources */,
 				A6D7539C2AD69E0700553091 /* MOMineMenuCollectionCell.m in Sources */,
 				A636647D2AF0DBB1002C0BFC /* MOCountDownButton.m in Sources */,
+				A62CC8832AFA52DE00FAB3AA /* BeautyMenuItemCell.swift in Sources */,
 				A63664872AF0ED9A002C0BFC /* MOAboutUsVC.m in Sources */,
 				A63664952AF109D6002C0BFC /* MOFamilyMainVC.m in Sources */,
 				A6D753A32AD6A6F500553091 /* MOMineItemModel.m in Sources */,

BIN
MiMoLive/MiMoLive.xcworkspace/xcuserdata/yanxuyao.xcuserdatad/UserInterfaceState.xcuserstate


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/CatSparks.png


+ 61 - 0
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/Root.plist

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>StringsTable</key>
+	<string>Root</string>
+	<key>PreferenceSpecifiers</key>
+	<array>
+		<dict>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+			<key>Title</key>
+			<string>Group</string>
+		</dict>
+		<dict>
+			<key>Type</key>
+			<string>PSTextFieldSpecifier</string>
+			<key>Title</key>
+			<string>Name</string>
+			<key>Key</key>
+			<string>name_preference</string>
+			<key>DefaultValue</key>
+			<string></string>
+			<key>IsSecure</key>
+			<false/>
+			<key>KeyboardType</key>
+			<string>Alphabet</string>
+			<key>AutocapitalizationType</key>
+			<string>None</string>
+			<key>AutocorrectionType</key>
+			<string>No</string>
+		</dict>
+		<dict>
+			<key>Type</key>
+			<string>PSToggleSwitchSpecifier</string>
+			<key>Title</key>
+			<string>Enabled</string>
+			<key>Key</key>
+			<string>enabled_preference</string>
+			<key>DefaultValue</key>
+			<true/>
+		</dict>
+		<dict>
+			<key>Type</key>
+			<string>PSSliderSpecifier</string>
+			<key>Key</key>
+			<string>slider_preference</string>
+			<key>DefaultValue</key>
+			<real>0.5</real>
+			<key>MinimumValue</key>
+			<integer>0</integer>
+			<key>MaximumValue</key>
+			<integer>1</integer>
+			<key>MinimumValueImage</key>
+			<string></string>
+			<key>MaximumValueImage</key>
+			<string></string>
+		</dict>
+	</array>
+</dict>
+</plist>

BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/en.lproj/Localizable.strings


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_baixi@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_baixi@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_cwei@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_cwei@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_tianmei@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_tianmei@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_yuanqi@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_fgz_yuanqi@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_chongzhi@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_chongzhi@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_dayan@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_dayan@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_etou@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_etou@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_liangyan@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_liangyan@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_meibai@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_meibai@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_meiya@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_meiya@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_mopi@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_mopi@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_shoubi@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_shoubi@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_shoulian@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_shoulian@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_shouquangu@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_shouquangu@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_wenhao@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_wenhao@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_xiaba@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_xiaba@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_xiahegu@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_xiahegu@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_zuixing@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_icon_zuixing@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_beihaidao@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_beihaidao@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_haibian@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_haibian@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_luolita@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_luolita@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_lvtu@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_lvtu@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_makalong@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_makalong@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_mitao@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_mitao@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_naiyou@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_naiyou@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_wuyu@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_wuyu@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_yangqi@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_yangqi@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_yinghua@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/meiyan_lj_yinghua@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/sdlu.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_compare@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_compare@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_ic_adjust_clear.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_ic_adjust_contrast.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_ic_adjust_saturation.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_ic_adjust_sharp.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_none@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_none@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_selected@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_beauty_selected@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_bg_blur@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_bg_blur@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_bg_peach@2x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/show_bg_peach@3x.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/xueqiu_lm_fu.png


BIN
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautyResource.bundle/zh-Hans.lproj/Localizable.strings


+ 216 - 0
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautySetVC/BeautyBaseVC.swift

@@ -0,0 +1,216 @@
+//
+//  BeautyBaseVC.swift
+//  BeautyAPi
+//
+//  Created by zhaoyongqaing on 2022/11/4.
+//
+
+import UIKit
+import JXCategoryView
+
+enum BeautyMenuType: CaseIterable {
+    case beauty
+    case style
+    case adjust
+    case sticker
+    
+    var title: String {
+        switch self {
+        case .beauty: return "create_beauty_setting_beauty_face".beauty_localized
+        case .style: return "create_beauty_setting_special_effects".beauty_localized
+        case .adjust: return "create_beauty_setting_special_adjust".beauty_localized
+        case .sticker: return "create_beauty_setting_sticker".beauty_localized
+        }
+    }
+}
+
+@objcMembers class BeautyBaseVC: UIViewController {
+    
+    var selectedItem: ((_ item: String)->())?
+    var dismissed: (()->())?
+    
+    private lazy var slider: UISlider = {
+        let slider = UISlider()
+        slider.minimumTrackTintColor = UIColor(hex: "7A59FB")
+        slider.maximumTrackTintColor = UIColor(hex: "FFFFFF", alpha: 0.7)
+        slider.addTarget(self, action: #selector(onTapSliderHandler(sender:)), for: .valueChanged)
+        return slider
+    }()
+    private let titles = BeautyMenuType.allCases.filter({
+        if BeautyModel.beautyType != .sense {
+            return $0 != .adjust
+        }
+        return true
+    }).map({ $0.title })
+    private let vcs = BeautyMenuType.allCases.filter({
+        if BeautyModel.beautyType != .sense {
+            return $0 != .adjust
+        }
+        return true
+    }).map({ BeautyMenuItemVC(type: $0) })
+    
+    // 背景
+    private lazy var bgView: UIView = {
+        let bgView = UIView()
+        bgView.backgroundColor = UIColor(hex: "#151325")
+        bgView.layer.cornerRadius = 20
+        bgView.layer.masksToBounds = true
+        bgView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
+        return bgView
+    }()
+    
+    // 对比按钮
+    private lazy var compareButton: UIButton = {
+        let compareButton = UIButton(type: .custom)
+        compareButton.setImage(UIImage.show_beautyImage(name: "show_beauty_compare"), for: .selected)
+        if #available(iOS 13.0, *) {
+            compareButton.setImage(UIImage.show_beautyImage(name: "show_beauty_compare")?
+                .withTintColor(UIColor(hex: "#7A59FB") ?? .clear,
+                               renderingMode: .alwaysOriginal), for: .normal)
+        }
+        compareButton.addTarget(self, action: #selector(didClickCompareButton(sender:)), for: .touchUpInside)
+        compareButton.backgroundColor = UIColor(hex: "#000000", alpha: 0.25)
+        compareButton.isSelected = true
+        compareButton.layer.cornerRadius = 18
+        compareButton.layer.masksToBounds = true
+        return compareButton
+    }()
+    
+    // 指示条
+    private lazy var indicator: JXCategoryIndicatorLineView = {
+        let indicator = JXCategoryIndicatorLineView()
+        indicator.indicatorWidth = 30
+        indicator.indicatorHeight = 2
+        indicator.indicatorColor = .white
+        return indicator
+    }()
+    
+    // 分类
+    private lazy var segmentedView: JXCategoryTitleView = {
+        let segmentedView = JXCategoryTitleView()
+        segmentedView.isTitleColorGradientEnabled = true
+        segmentedView.titles = titles
+        segmentedView.titleFont = .systemFont(ofSize: 14)
+        segmentedView.titleSelectedFont = .systemFont(ofSize: 15)
+        segmentedView.titleColor = UIColor(hex: "989DBA")
+        segmentedView.titleSelectedColor = .white
+        segmentedView.backgroundColor = .clear
+        segmentedView.defaultSelectedIndex = 0
+        segmentedView.delegate = self
+        segmentedView.indicators = [self.indicator]
+        return segmentedView
+    }()
+        
+    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
+        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
+        modalPresentationStyle = .overCurrentContext
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    private var beautyFaceVC: BeautyMenuItemVC? {
+        didSet {
+            beautyFaceVC?.selectedItemClosure = { [weak self] value, isHiddenValue in
+                guard let self = self else { return }
+                self.slider.isHidden = isHiddenValue
+                self.compareButton.isHidden = isHiddenValue
+                self.slider.setValue(Float(value), animated: true)
+            }
+            beautyFaceVC?.reloadData()
+        }
+    }
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        setupUI()
+        beautyFaceVC = vcs.first
+    }
+
+    private func setupUI(){
+        view.backgroundColor = .clear
+        
+        // slider
+        view.addSubview(slider)
+        slider.translatesAutoresizingMaskIntoConstraints = false
+        slider.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 22).isActive = true
+        slider.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -83).isActive = true
+        slider.heightAnchor.constraint(equalToConstant: 30).isActive = true
+        slider.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -214).isActive = true
+        
+        // 对比按钮
+        view.addSubview(compareButton)
+        compareButton.translatesAutoresizingMaskIntoConstraints = false
+        compareButton.centerYAnchor.constraint(equalTo: slider.centerYAnchor).isActive = true
+        compareButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
+        compareButton.widthAnchor.constraint(equalToConstant: 36).isActive = true
+        compareButton.heightAnchor.constraint(equalToConstant: 36).isActive = true
+        
+        view.addSubview(bgView)
+        bgView.translatesAutoresizingMaskIntoConstraints = false
+        bgView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
+        bgView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
+        bgView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
+        bgView.heightAnchor.constraint(equalToConstant: 203).isActive = true
+       
+        bgView.addSubview(segmentedView)
+        segmentedView.translatesAutoresizingMaskIntoConstraints = false
+        segmentedView.leadingAnchor.constraint(equalTo: bgView.leadingAnchor).isActive = true
+        segmentedView.trailingAnchor.constraint(equalTo: bgView.trailingAnchor).isActive = true
+        segmentedView.topAnchor.constraint(equalTo: bgView.topAnchor, constant: 10).isActive = true
+        segmentedView.heightAnchor.constraint(equalToConstant: 30).isActive = true
+        
+        if let listContainerView = JXCategoryListContainerView(type: .scrollView, delegate: self) {
+            segmentedView.listContainer = listContainerView
+            bgView.addSubview(listContainerView)
+            listContainerView.translatesAutoresizingMaskIntoConstraints = false
+            listContainerView.leadingAnchor.constraint(equalTo: bgView.leadingAnchor).isActive = true
+            listContainerView.trailingAnchor.constraint(equalTo: bgView.trailingAnchor).isActive = true
+            listContainerView.topAnchor.constraint(equalTo: segmentedView.bottomAnchor, constant: 25).isActive = true
+            listContainerView.heightAnchor.constraint(equalToConstant: 70).isActive = true
+        }
+    }
+    
+
+    @objc
+    private func onTapSliderHandler(sender: UISlider) {
+        beautyFaceVC?.changeValueHandler(value: CGFloat(sender.value))
+    }
+}
+
+extension BeautyBaseVC {
+    
+    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
+        dismiss(animated: true)
+        dismissed?()
+    }
+    
+    // 点击对比按钮
+    @objc private func didClickCompareButton(sender: UIButton){
+        sender.isSelected = !sender.isSelected
+        BeautyManager.shareManager.isEnableBeauty = sender.isSelected
+    }
+}
+
+
+extension BeautyBaseVC: JXCategoryViewDelegate {
+    func categoryView(_ categoryView: JXCategoryBaseView!, didSelectedItemAt index: Int) {
+        beautyFaceVC = vcs[index]
+        if index == vcs.count - 1 {
+            compareButton.isHidden = true
+        }
+    }
+}
+
+extension BeautyBaseVC: JXCategoryListContainerViewDelegate {
+    
+    func number(ofListsInlistContainerView listContainerView: JXCategoryListContainerView?) -> Int {
+        titles.count
+    }
+    
+    func listContainerView(_ listContainerView: JXCategoryListContainerView?,
+                           initListFor index: Int) -> JXCategoryListContentViewDelegate? {
+        vcs[index]
+    }
+}

+ 75 - 0
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautySetVC/BeautyMenuItemCell.swift

@@ -0,0 +1,75 @@
+//
+//  BeautyMenuItemCell.swift
+//  BeautyAPi
+//
+//  Created by zhaoyongqaing on 2022/11/4.
+//
+
+import UIKit
+
+class BeautyMenuItemCell: UICollectionViewCell {
+    // 图
+    private lazy var imageView: UIImageView = {
+        let imageView = UIImageView()
+        imageView.image = UIImage.show_beautyImage(name: "show_beauty_none")
+        imageView.contentMode = .scaleAspectFit
+        return imageView
+    }()
+    // 名称
+    private lazy var nameLabel: UILabel = {
+        let nameLabel = UILabel()
+        nameLabel.font = .systemFont(ofSize: 11)
+        nameLabel.textColor = UIColor(hex: "989DBA")
+        nameLabel.numberOfLines = 2
+        nameLabel.text = "show_beauty_item_beauty_whiten".beauty_localized
+        return nameLabel
+    }()
+    // 选中标识
+    private lazy var indicatorImgView: UIImageView = {
+        let indicatorImgView = UIImageView()
+        indicatorImgView.isHidden = true
+        indicatorImgView.image = UIImage.show_beautyImage(name: "show_beauty_selected")
+        return indicatorImgView
+    }()
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        createSubviews()
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    func setupModel(model: BeautyModel) {
+        nameLabel.text = model.name
+        imageView.image = UIImage.show_beautyImage(name: model.icon)
+        indicatorImgView.isHidden = !model.isSelected
+        nameLabel.font = model.isSelected ? .systemFont(ofSize: 12) : .systemFont(ofSize: 11)
+        nameLabel.textColor = model.isSelected ? .white : UIColor(hex: "989DBA")
+    }
+    
+    private func createSubviews(){
+        // 图
+        contentView.addSubview(imageView)
+        imageView.translatesAutoresizingMaskIntoConstraints = false
+        
+        // 选中标识
+        contentView.addSubview(indicatorImgView)
+        indicatorImgView.translatesAutoresizingMaskIntoConstraints = false
+    
+        imageView.centerXAnchor.constraint(equalTo: indicatorImgView.centerXAnchor).isActive = true
+        imageView.centerYAnchor.constraint(equalTo: indicatorImgView.centerYAnchor).isActive = true
+        
+        indicatorImgView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
+        indicatorImgView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
+        indicatorImgView.widthAnchor.constraint(equalToConstant: 52).isActive = true
+        indicatorImgView.heightAnchor.constraint(equalToConstant: 52).isActive = true
+        
+        // 名称
+        contentView.addSubview(nameLabel)
+        nameLabel.translatesAutoresizingMaskIntoConstraints = false
+        nameLabel.centerXAnchor.constraint(equalTo: indicatorImgView.centerXAnchor).isActive = true
+        nameLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
+    }
+}

+ 178 - 0
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautySetVC/BeautyMenuItemVC.swift

@@ -0,0 +1,178 @@
+//
+//  BeautyMenuItemVC.swift
+//  BeautyAPi
+//
+//  Created by zhaoyongqaing on 2022/11/4.
+//
+
+import UIKit
+import JXCategoryView
+
+class BeautyMenuItemVC: UIViewController {
+    
+    var selectedItemClosure: ((_ value: CGFloat, _ isHiddenSldier: Bool) -> Void)?
+    
+    private lazy var collectionView: UICollectionView = {
+        let layout = UICollectionViewFlowLayout()
+        layout.scrollDirection = .horizontal
+        layout.minimumInteritemSpacing = 15
+        layout.sectionInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
+        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
+        collectionView.backgroundColor = .clear
+        collectionView.register(BeautyMenuItemCell.self, forCellWithReuseIdentifier: NSStringFromClass(BeautyMenuItemCell.self))
+        collectionView.delegate = self
+        collectionView.dataSource = self
+        collectionView.showsHorizontalScrollIndicator = false
+        return collectionView
+    }()
+    private lazy var beautyData = BeautyModel.createBeautyData()
+    private lazy var styleData = BeautyModel.createStyleData()
+    private lazy var adjustData = BeautyModel.createAdjustData()
+    private lazy var filterData = BeautyModel.createFilterData()
+    private lazy var stickerData = BeautyModel.createStickerData()
+     
+    private lazy var dataArray: [BeautyModel] = {
+        switch type {
+        case .beauty: return beautyData
+        case .style: return styleData
+        case .adjust: return adjustData
+        case .sticker: return stickerData
+        }
+    }()
+    
+    private var type: BeautyMenuType = .beauty
+    private var defalutSelectIndex = 0
+    
+    init(type: BeautyMenuType) {
+        super.init(nibName: nil, bundle: nil)
+        self.type = type
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        setUpUI()
+        configDefaultSelect()
+    }
+    
+    func changeValueHandler(value: CGFloat) {
+        guard value > 0 else { return }
+        setBeautyHandler(value: value, isReset: false)
+    }
+    
+    func reloadData() {
+        collectionView.reloadData()
+    }
+    
+    private func setBeautyHandler(value: CGFloat, isReset: Bool) {
+        let model = dataArray[defalutSelectIndex]
+        model.value = value
+        switch type {
+        case .beauty, .adjust:
+            if isReset {
+                BeautyManager.shareManager.reset(datas: dataArray)
+                return
+            }
+            BeautyManager.shareManager.setBeauty(path: model.path,
+                                                     key: model.key,
+                                                     value: model.value)
+        case .style:
+            if isReset {
+                BeautyManager.shareManager.resetStyle(datas: dataArray)
+                return
+            }
+            BeautyManager.shareManager.setStyle(path: model.path,
+                                                    key: model.key,
+                                                    value: model.value)
+            
+        case .sticker:
+            BeautyManager.shareManager.setSticker(path: model.path, datas: stickerData)
+        }
+    }
+    
+    private func setUpUI(){
+        // 列表
+        view.addSubview(collectionView)
+        collectionView.translatesAutoresizingMaskIntoConstraints = false
+        collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
+        collectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
+        collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
+        collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
+    }
+    
+    // 默认选中
+    private func configDefaultSelect(){
+        CATransaction.begin()
+        CATransaction.setCompletionBlock {
+            let indexPath = IndexPath(item: self.defalutSelectIndex, section: 0)
+            if self.collectionView.numberOfItems(inSection: 0)  > self.defalutSelectIndex {
+                self.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .left)
+            }
+        }
+        collectionView.reloadData()
+        CATransaction.commit()
+    }
+}
+
+extension BeautyMenuItemVC: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
+    
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        dataArray.count
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        let cell: BeautyMenuItemCell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(BeautyMenuItemCell.self),
+                                                                          for: indexPath) as! BeautyMenuItemCell
+        let model = dataArray[indexPath.item]
+        cell.setupModel(model: model)
+        if model.isSelected {
+            selectedItemClosure?(model.value, model.key == nil)
+            defalutSelectIndex = indexPath.item
+        }
+        return cell
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        let preModel = dataArray[defalutSelectIndex]
+        preModel.isSelected = false
+        dataArray[defalutSelectIndex] = preModel
+        collectionView.reloadItems(at: [IndexPath(item: defalutSelectIndex, section: 0)])
+        
+        defalutSelectIndex = indexPath.item
+        let model = dataArray[indexPath.item]
+        setBeautyHandler(value: model.value, isReset: model.key == nil)
+        model.isSelected = true
+        dataArray[indexPath.item] = model
+        collectionView.reloadItems(at: [IndexPath(item: indexPath.item, section: 0)])
+        
+        if type == .sticker {
+            selectedItemClosure?(0, true)
+            return
+        }
+        selectedItemClosure?(model.value, model.path == nil)
+    }
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
+        switch type {
+        case .beauty:
+            let model = dataArray[indexPath.item]
+            let nsString = (model.name ?? "") as NSString
+            let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 11)]
+            let size = nsString.size(withAttributes: attributes as [NSAttributedString.Key : Any])
+            let w = size.width < 52 ? 52 : size.width
+            return CGSize(width: w, height: 70)
+
+        default:
+            return CGSize(width: 52, height: 70)
+        }
+    }
+}
+
+extension BeautyMenuItemVC : JXCategoryListContentViewDelegate {
+    
+    func listView() -> UIView! {
+        return view
+    }
+}

+ 42 - 0
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautySetVC/UIColor+Hex.swift

@@ -0,0 +1,42 @@
+//
+//  UIColor+Hex.swift
+//  BeautyAPi
+//
+//  Created by zhaoyongqiang on 2023/8/14.
+//
+
+import Foundation
+extension UIColor {
+    convenience init?(hex string: String) {
+        self.init(hex: string, alpha: 1.0)
+    }
+    convenience init?(hex string: String, alpha: CGFloat = 1.0) {
+        
+        var hex = string.hasPrefix("#") ? String(string.dropFirst()) : string
+        hex = hex.hasPrefix("0x") ? String(hex.dropFirst(2)) : hex
+        guard hex.count == 3 || hex.count == 6  else {
+            self.init(white: 1.0, alpha: 0.0)
+            return
+        }
+        
+        if hex.count == 3 {
+            for (indec, char) in hex.enumerated() {
+                hex.insert(char, at: hex.index(hex.startIndex, offsetBy: indec * 2))
+            }
+        }
+        
+        self.init(
+            red: CGFloat((Int(hex, radix: 16)! >> 16) & 0xFF) / 255.0,
+            green: CGFloat((Int(hex, radix: 16)! >> 8) & 0xFF) / 255.0,
+            blue: CGFloat((Int(hex, radix: 16)!) & 0xFF) / 255.0,
+            alpha: alpha
+        )
+    }
+    
+    var randomColor: UIColor {
+        UIColor(red: CGFloat.random(in: 0...255) / 255.0,
+                green: CGFloat.random(in: 0...255) / 255.0,
+                blue: CGFloat.random(in: 0...255) / 255.0,
+                alpha: 1)
+    }
+}

+ 73 - 0
MiMoLive/MiMoLive/3rdParty/SenseRender/BeautySetVC/UIImage+Show.swift

@@ -0,0 +1,73 @@
+//
+//  UIImage+Show.swift
+//  AgoraEntScenarios
+//
+//  Created by FanPengpeng on 2022/11/2.
+//
+
+import UIKit
+
+extension UIImage {
+    @objc
+    static func show_beautyImage(name: String?) -> UIImage? {
+        guard let imageName = name else { return nil }
+        return sceneImage(name: imageName, bundleName: "BeautyResource")
+    }
+}
+
+
+extension UIImage {
+    @objc
+    static func sceneImage(name: String) -> UIImage? {
+        return sceneImage(name: name, bundleName: nil)
+    }
+
+    @objc
+    static func sceneImage(name: String, bundleName: String?) -> UIImage? {
+        guard let bundleName = bundleName else {
+            assertionFailure("sceneImageBundleName == nil")
+            return nil
+        }
+        
+        guard let bundlePath = Bundle.main.path(forResource: bundleName, ofType: "bundle"),
+              let bundle = Bundle(path: bundlePath)
+        else {
+            assertionFailure("image bundle == nil")
+            return nil
+        }
+
+        let components = name.components(separatedBy: ".")
+        let pureName = components.first ?? name
+        let suffix = (components.count == 2 ? components.last : nil) ?? "png"
+        let scale = Int(UIScreen.main.scale)
+        var scales = [1, 2, 3].filter { value in
+            return value != scale
+        }
+        scales.insert(scale, at: 0)
+        
+        let lang = getLang()
+        for value in scales {
+            let imageName1 = value > 1 ? "\(pureName)-\(lang)@\(value)x" : pureName
+            let imageName2 = value > 1 ? "\(pureName)@\(value)x" : pureName
+            if let path = bundle.path(forResource: imageName1, ofType: suffix) {
+                let image = UIImage(contentsOfFile: path)
+                return image
+            }
+            if let path = bundle.path(forResource: imageName2, ofType: suffix) {
+                let image = UIImage(contentsOfFile: path)
+                return image
+            }
+        }
+        return nil
+    }
+    
+    private static func getLang() -> String {
+        guard let lang = NSLocale.preferredLanguages.first else {
+            return "en"
+        }
+        if lang.contains("zh") {
+            return "zh-Hans"
+        }
+        return "en"
+    }
+}

+ 32 - 0
MiMoLive/MiMoLive/3rdParty/SenseRender/String+Localizable.swift

@@ -0,0 +1,32 @@
+//
+//  String+Localizable.swift
+//  BeautyAPi
+//
+//  Created by zhaoyongqiang on 2023/8/14.
+//
+
+import Foundation
+extension String {
+    var beauty_localized: String {
+        guard let bundlePath = Bundle.main.path(forResource: "BeautyResource", ofType: "bundle"),
+              let bundle = Bundle(path: bundlePath)
+        else {
+            return self
+        }
+        
+        guard var lang = NSLocale.preferredLanguages.first else {
+            return self
+        }
+        if lang.contains("zh") {
+            lang = "zh-Hans"
+        } else {
+            lang = "en"
+        }
+        
+        guard let langPath = bundle.path(forResource: lang, ofType: "lproj") , let detailBundle = Bundle(path: langPath) else {
+            return self
+        }
+        let retStr = NSLocalizedString(self,tableName: "Localizable", bundle:detailBundle ,comment: "")
+        return retStr
+    }
+}

+ 14 - 2
MiMoLive/MiMoLive/Classes/Live/MOCreateLiveVC.m

@@ -22,6 +22,8 @@
 @property (nonatomic, strong) UIView *localView;
 /** 创建房间的菜单view */
 @property (nonatomic, strong) MOCreateLiveView *createBgView;
+/** 美颜设置VC */
+@property (nonatomic, strong) BeautyBaseVC *beautySettingVC;
 
 @end
 
@@ -44,6 +46,8 @@
 }
 
 - (void)setBgActionBlock{
+    WEAKSELF
+    
     //关闭窗口
     self.createBgView.closeLiveBlock = ^{
         
@@ -51,12 +55,12 @@
     
     //翻转摄像头
     self.createBgView.cameraBtnBlock = ^{
-        
+        [weakSelf.beautyAPI switchCamera];
     };
     
     //美化
     self.createBgView.beautyBtnBlock = ^{
-        
+        [weakSelf presentViewController:weakSelf.beautySettingVC animated:YES completion:nil];
     };
     
     //通知是否打开
@@ -152,4 +156,12 @@
     return _createBgView;
 }
 
+- (BeautyBaseVC *)beautySettingVC{
+    if(!_beautySettingVC){
+        _beautySettingVC = [[BeautyBaseVC alloc] init];
+    }
+    
+    return _beautySettingVC;
+}
+
 @end

+ 6 - 2
MiMoLive/Podfile.lock

@@ -52,6 +52,7 @@ PODS:
   - FMDB/standard (2.7.5)
   - HyphenateChat (4.1.1)
   - IQKeyboardManager (6.5.15)
+  - JXCategoryView (1.6.1)
   - Masonry (1.1.0)
   - MBProgressHUD (1.1.0)
   - MJRefresh (3.7.5)
@@ -96,6 +97,7 @@ DEPENDENCIES:
   - FMDB
   - HyphenateChat (~> 4.1.1)
   - IQKeyboardManager (~> 6.5.15)
+  - JXCategoryView (= 1.6.1)
   - Masonry (~> 1.1.0)
   - MBProgressHUD (~> 1.1.0)
   - MJRefresh (~> 3.7.5)
@@ -119,6 +121,7 @@ SPEC REPOS:
     - FMDB
     - HyphenateChat
     - IQKeyboardManager
+    - JXCategoryView
     - Masonry
     - MBProgressHUD
     - MJRefresh
@@ -141,6 +144,7 @@ SPEC CHECKSUMS:
   FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
   HyphenateChat: 95aa4abbad0e763f513acbe21ff9543f50b886cd
   IQKeyboardManager: 22ffab9bd300ad493485a390a095f5db0c841776
+  JXCategoryView: 7b1ee69ede4843c581688afe84d0f047723262f2
   Masonry: 678fab65091a9290e40e2832a55e7ab731aad201
   MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9
   MJRefresh: fdf5e979eb406a0341468932d1dfc8b7f9fce961
@@ -152,6 +156,6 @@ SPEC CHECKSUMS:
   TZImagePickerController: e9909edbadf7381140efc5b5c9f5bdbfd630f7d4
   YYCache: 8105b6638f5e849296c71f331ff83891a4942952
 
-PODFILE CHECKSUM: 3e5aeb1227b3a4c458c5f6b9d04113b2b673ff5f
+PODFILE CHECKSUM: 6e71a6887198d745bc7e3e62b46dc314e7315059
 
-COCOAPODS: 1.12.0
+COCOAPODS: 1.12.1

+ 21 - 0
MiMoLive/Pods/JXCategoryView/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 暴走的鑫鑫
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 296 - 0
MiMoLive/Pods/JXCategoryView/README.md

@@ -0,0 +1,296 @@
+<div align=center><img src="Example/Example/Images/JXCategoryView.png" width="405" height="63" /></div>
+
+[![platform](https://img.shields.io/badge/platform-iOS-blue.svg?style=plastic)](#)
+[![languages](https://img.shields.io/badge/language-objective--c-blue.svg)](#) 
+[![cocoapods](https://img.shields.io/badge/cocoapods-supported-4BC51D.svg?style=plastic)](https://cocoapods.org/pods/JXCategoryView)
+[![support](https://img.shields.io/badge/support-ios%208%2B-orange.svg)](#) 
+
+A powerful and easy to use category view (segmentedcontrol, segmentview, pagingview, pagerview, pagecontrol) (腾讯新闻、今日头条、QQ 音乐、网易云音乐、京东、爱奇艺、腾讯视频、淘宝、天猫、简书、微博等所有主流 APP 分类切换滚动视图)
+
+与其他的同类三方库对比的优点:
+- 使用协议封装指示器逻辑,可以随心所欲地自定义指示器效果;
+- 提供更加全面丰富、高度自定义的效果;
+- 使用子类化管理 cell 样式,逻辑更清晰,扩展更简单;
+- 高度封装列表容器,使用便捷,完美支持列表的生命周期调用;
+
+## Swift版本
+
+如果你在找 Swift 版本,请点击查看 [JXSegmentedView](https://github.com/pujiaxin33/JXSegmentedView)。
+
+## 效果预览
+
+### 指示器效果预览
+
+说明 | Gif |
+----|------|
+LineView  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/LineView.gif" width="343" height="80"> |
+LineView延长  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/JDLineStyle.gif" width="343" height="80"> |
+LineView延长+偏移  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/IQIYILineStyle.gif" width="343" height="80"> |
+LineView🌈彩虹风格  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/RainbowLineView.gif" width="343" height="80"> |
+DotLineView点线效果 |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/IndicatorCustomizeGuide.gif" width="334" height="88"> |
+BallView QQ黏性红点  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/QQBall.gif" width="343" height="84"> |
+TriangleView 三角形底部  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TriangleBottom.gif" width="343" height="80"> |
+TriangleView 三角形顶部  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TriangleTop.gif" width="343" height="80"> |
+BackgroundView椭圆形  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/BackgroundEllipseLayer.gif" width="343" height="80"> |
+BackgroundView椭圆形+阴影  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/BackgroundViewShadow.gif" width="343" height="80"> |
+BackgroundView长方形  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/Rectangle.gif" width="343" height="80"> |
+BackgroundView遮罩有背景  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleMask.gif" width="343" height="80"> |
+BackgroundView遮罩无背景  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleMaskNoBackgroundView.gif" width="343" height="80"> |
+BackgroundView渐变色  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/GradientBGIndicatorView.gif" width="350" height="80"> |
+ImageView底部(小船)  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/IndicatorImageView.gif" width="343" height="137"> |
+ImageView背景(最佳男歌手)  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/BackgroundImageView.gif" width="343" height="80"> |
+ImageView滚动效果(足球)  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/Football.gif" width="343" height="135"> |
+混合使用 |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/Mixed.gif" width="343" height="80"> |
+
+以下均支持上下位置切换:
+`JXCategoryIndicatorLineView`、`JXCategoryIndicatorImageView`、`JXCategoryIndicatorBallView`、`JXCategoryIndicatorTriangleView`
+
+### Cell样式效果预览
+
+说明 | Gif |
+----|------|
+颜色渐变  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleColorGradient.gif" width="343" height="80"> |
+大小缩放  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleZoom.gif" width="350" height="80"> |
+大小缩放+底部锚点  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleLabelAnchorBottom.gif" width="350" height="80"> |
+大小缩放+顶部锚点  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleLabelAnchorTop.gif" width="350" height="80"> |
+大小缩放+字体粗细  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleZoomStrokeWidth.gif" width="350" height="80"> |
+大小缩放+点击动画  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleZoomSelectedAnimation.gif" width="350" height="80"> |
+大小缩放+cell宽度缩放  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleZoomCellWidth.gif" width="350" height="80"> |
+TitleImage_Top |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleImageTop.gif" width="343" height="80"> |
+TitleImage_Left |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleImageLeft.gif" width="343" height="80"> |
+TitleImage_Bottom |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleImageBottom.gif" width="343" height="80"> |
+TitleImage_Right |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleImageRight.gif" width="343" height="80"> |
+cell图文混用 |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/CellMixed.gif" width="343" height="90"> |
+Image |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/CellImage.gif" width="343" height="80"> |
+数字 |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/Number.gif" width="343" height="80"> |
+红点 |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/CellRedDot.gif" width="343" height="80"> |
+多行文本 |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/MultiLineText.gif" width="350" height="80"> |
+多行富文本 |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/AttributeView.gif" width="343" height="80"> |
+Cell背景色渐变  |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/CellBackgroundColorGradient.gif" width="343" height="80"> |
+分割线 |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/SeparatorLine.gif" width="343" height="80"> |
+
+### 特殊效果预览
+
+说明 | Gif |
+----|------|
+数据源过少<br/> averageCellSpacingEnabled默认为YES |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/averageCellSpacingEnabledYES.gif" width="343" height="80"> |
+数据源过少<br/> averageCellSpacingEnabled为NO |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/averageCellSpacingEnabledNO.gif" width="343" height="80"> |
+SegmentedControl<br/>参考[`SegmentedControlViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/SegmentedControl/SegmentedControlViewController.m)类 |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/SegmentedControl.gif" width="343" height="80"> |
+导航栏使用<br/>参考[`NaviSegmentedControlViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/SegmentedControl/NaviSegmentedControlViewController.m)类 |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/SegmentedControlNavi.gif" width="343" height="80"> |
+嵌套使用<br/>参考[`NestViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/Nest/NestViewController.m)类 |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/Nest.gif" width="343" height="272"> |
+个人主页(上下左右滚动、header悬浮)<br/>参考[`PagingViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/PagingView/Example/PagingViewController.m)类<br/> 更多样式请点击查看[JXPagingView库](https://github.com/pujiaxin33/JXPagingView) |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/UserProfile.gif" width="343" height="562"> |
+垂直列表滚动<br/>参考[`VerticalListViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/VerticalListView/VerticalListViewController.m)类<br/> 高仿腾讯视频<br/>支持UITableView,参考[`VerticalListTableViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/VerticalListView/VerticalListTableViewController.m)<br/>(背景色异常是录屏软件bug) |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/VerticalList.gif" width="343" height="607"> |
+| 垂直缩放(仿网易圈圈、脉脉首页)<br/>参考[`ScrollZoomViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/ScrollZoom/ScrollZoomViewController.m)类 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/VerticalZoomTitle.gif" width="350" height="306"> |
+数据源刷新&列表数据加载<br/>参考[`LoadDataListContainerViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/LoadData/LoadDataListContainerViewController.m)类 |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/LoadData.gif" width="343" height="619"> |
+上下滚动隐藏导航栏 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/ScrollUp.gif" width="336" height="354"> |
+京东首页-滚动渐变变小 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/jingdo_scroll_small.gif" width="305" height="599"> |
+
+### 自定义效果预览
+
+收录来自其他使用者的自定义示例,这些自定义类只在 Demo 项目里面,Pod 库并没有这些文件。所以,如果你需要使用这些自定义效果,请通过文件导入的方式。
+
+目的:
+- 参考学习如何自定义;
+- 直接修改自定义示例类以快速实现自己的需求。
+
+欢迎提 PullRequest 进行收录你的自定义效果。
+
+| 说明 | Gif |
+| ----|------|
+| Spring动画指示器 |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/SpringIndicator.gif" width="336" height="70"> |
+| 富文本数量cell |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/CountCell.gif" width="336" height="70"> |
+| 左右对齐指示器 |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/AlignmentIndicator.gif" width="336" height="70"> |
+| 秒杀时间线cell |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/timeline.gif" width="336" height="70"> |
+| 京东商品排序cell |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/JDProductSort.gif" width="336" height="70"> |
+| title背景块cell |  <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleBackgroundExample.gif" width="336" height="70"> |
+
+## 要求
+
+- iOS 9.0+
+- Xcode 9+
+- Objective-C
+
+## 安装
+
+### 手动
+
+Clone 代码,把 Sources 文件夹拖入项目,`#import "JXCategoryView.h"` 就可以使用了。
+
+### CocoaPods
+
+```ruby
+target '<Your Target Name>' do
+    pod 'JXCategoryView'
+end
+```
+先执行 `pod repo update`,再执行 `pod install`。
+
+## 结构图
+
+<img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/JXCategoryViewStructure.png" width="933" height="482">
+
+## 使用
+
+### JXCategoryTitleView 使用示例
+
+1. 初始化 `JXCategoryTitleView`:
+```Objective-C
+self.categoryView = [[JXCategoryTitleView alloc] initWithFrame:CGRectMake(0, 0, WindowsSize.width, 50)];
+self.categoryView.delegate = self;
+[self.view addSubview:self.categoryView];
+```
+
+2. 配置 `JXCategoryTitleView` 的属性:
+```Objective-C
+self.categoryView.titles = @[@"螃蟹", @"麻辣小龙虾", @"苹果"...];
+self.categoryView.titleColorGradientEnabled = YES;
+```
+
+3. 添加指示器:
+```Objective-C
+JXCategoryIndicatorLineView *lineView = [[JXCategoryIndicatorLineView alloc] init];
+lineView.indicatorColor = [UIColor redColor];
+lineView.indicatorWidth = JXCategoryViewAutomaticDimension;
+self.categoryView.indicators = @[lineView];
+```
+
+4. 实现 `JXCategoryViewDelegate` 代理(可选)
+```Objective-C
+// 点击选中或者滚动选中都会调用该方法。适用于只关心选中事件,不关心具体是点击还是滚动选中的。
+- (void)categoryView:(JXCategoryBaseView *)categoryView didSelectedItemAtIndex:(NSInteger)index;
+
+// 点击选中的情况才会调用该方法
+- (void)categoryView:(JXCategoryBaseView *)categoryView didClickSelectedItemAtIndex:(NSInteger)index;
+
+// 滚动选中的情况才会调用该方法
+- (void)categoryView:(JXCategoryBaseView *)categoryView didScrollSelectedItemAtIndex:(NSInteger)index;
+
+// 正在滚动中的回调
+- (void)categoryView:(JXCategoryBaseView *)categoryView scrollingFromLeftIndex:(NSInteger)leftIndex toRightIndex:(NSInteger)rightIndex ratio:(CGFloat)ratio;
+```
+
+### 列表容器使用示例
+
+#### `JXCategoryListContainerView` 封装类使用示例
+
+`JXCategoryListContainerView` 是对列表视图高度封装的类,具有以下优点:
+
+- 相对于直接使用 `UIScrollView` 自定义,封装度高、代码集中、使用简单;
+- 列表懒加载:当显示某个列表的时候,才进行列表初始化。而不是一次性加载全部列表,性能更优;
+- 支持列表的 `willAppear`、`didAppear`、`willDisappear`、`didDisappear` 生命周期方法调用;
+
+1. 初始化 `JXCategoryListContainerView` 并关联到 `categoryView`:
+```Objective-C
+self.listContainerView = [[JXCategoryListContainerView alloc] initWithType:JXCategoryListContainerType_ScrollView delegate:self];
+[self.view addSubview:self.listContainerView];
+// 关联到 categoryView
+self.categoryView.listContainer = self.listContainerView;
+```
+
+2. 实现 `JXCategoryListContainerViewDelegate` 代理方法:
+```Objective-C
+// 返回列表的数量
+- (NSInteger)numberOfListsInlistContainerView:(JXCategoryListContainerView *)listContainerView {
+    return self.titles.count;
+}
+// 根据下标 index 返回对应遵守并实现 `JXCategoryListContentViewDelegate` 协议的列表实例
+- (id<JXCategoryListContentViewDelegate>)listContainerView:(JXCategoryListContainerView *)listContainerView initListForIndex:(NSInteger)index {
+    return [[ListViewController alloc] init];
+}
+```
+
+3. 列表实现 `JXCategoryListContentViewDelegate` 代理方法
+不管列表是 `UIView` 还是 `UIViewController` 都可以,提高使用灵活性,更便于现有的业务接入。
+```Objective-C
+// 返回列表视图
+// 如果列表是 VC,就返回 VC.view
+// 如果列表是 View,就返回 View 自己
+- (UIView *)listView {
+    return self.view;
+}
+```
+
+
+具体点击 [LoadDataListContainerViewController](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/LoadData/LoadDataListContainerViewController.m) 查看源代码了解
+
+#### 直接使用 UIScrollView 自定义
+
+因为代码量较多且分散,所有不推荐使用该方法。要正确使用需要注意的地方比较多,尤其对于刚接触 iOS 的同学来说不太友好。
+
+不直接贴代码了,具体点击 [LoadDataListCustomViewController](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/LoadData/LoadDataListCustomViewController.m) 查看源代码了解。
+
+
+## 常见问题和答案
+
+❗️❗️❗️这里面包含了许多常见问题和答案,使用之前请务必浏览此文档,或者遇到问题先看此文档❗️❗️❗️
+
+[常见问题和答案总文档](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md)
+
+- [个人主页效果更丰富的示例:JXPagingView](https://github.com/pujiaxin33/JXPagingView)
+- [侧滑手势处理](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BE%A7%E6%BB%91%E6%89%8B%E5%8A%BF%E5%A4%84%E7%90%86.md)
+- [列表的生命周期方法处理](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E5%88%97%E8%A1%A8%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E6%96%B9%E6%B3%95%E5%A4%84%E7%90%86.md)
+- [`JXCategoryListContainerType`的`scrollView`和`collectionView`对比](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#jxcategorylistcontainertype%E7%9A%84scrollview%E5%92%8Ccollectionview%E5%AF%B9%E6%AF%94)
+- [cell左滑删除](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#cell%E5%B7%A6%E6%BB%91%E5%88%A0%E9%99%A4)
+- [`FDFullscreenPopGesture`等全屏手势处理](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E5%85%A8%E5%B1%8F%E6%89%8B%E5%8A%BF%E5%A4%84%E7%90%86.md)
+- [JXCategoryView数据源刷新](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#jxcategoryview%E6%95%B0%E6%8D%AE%E6%BA%90%E5%88%B7%E6%96%B0)
+- [`reloadDataWithoutListContainer`方法使用说明](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#reloaddatawithoutlistcontainer%E6%96%B9%E6%B3%95%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E)
+- [listContainer或contentScrollView关联说明](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#listcontainer%E6%88%96contentscrollview%E5%85%B3%E8%81%94%E8%AF%B4%E6%98%8E)
+- [点击切换列表的动画控制](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E7%82%B9%E5%87%BB%E5%88%87%E6%8D%A2%E5%88%97%E8%A1%A8%E7%9A%84%E5%8A%A8%E7%94%BB%E6%8E%A7%E5%88%B6)
+- [列表cell点击跳转示例](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E5%88%97%E8%A1%A8cell%E7%82%B9%E5%87%BB%E8%B7%B3%E8%BD%AC%E7%A4%BA%E4%BE%8B)
+- [列表调用`presentViewController`方法](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E5%88%97%E8%A1%A8%E8%B0%83%E7%94%A8presentviewcontroller%E6%96%B9%E6%B3%95)
+- [代码选中指定index](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E4%BB%A3%E7%A0%81%E9%80%89%E4%B8%AD%E6%8C%87%E5%AE%9Aindex)
+- [JXCategoryView.collectionView高度取整说明](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#jxcategoryviewcollectionview%E9%AB%98%E5%BA%A6%E5%8F%96%E6%95%B4%E8%AF%B4%E6%98%8E)
+- [对父VC的automaticallyAdjustsScrollViewInsets属性设置为NO](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E5%AF%B9%E7%88%B6vc%E7%9A%84automaticallyadjustsscrollviewinsets%E5%B1%9E%E6%80%A7%E8%AE%BE%E7%BD%AE%E4%B8%BAno)
+- [`JXCategoryListContainerView`内部使用`UIViewController`当做列表容器使用说明](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#jxcategorylistcontainerview%E5%86%85%E9%83%A8%E4%BD%BF%E7%94%A8uiviewcontroller%E5%BD%93%E5%81%9A%E5%88%97%E8%A1%A8%E5%AE%B9%E5%99%A8%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E)
+- [使用多行文本](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E4%BD%BF%E7%94%A8%E5%A4%9A%E8%A1%8C%E6%96%87%E6%9C%AC)
+- [列表容器禁止左右滑动](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E5%88%97%E8%A1%A8%E5%AE%B9%E5%99%A8%E7%A6%81%E6%AD%A2%E5%B7%A6%E5%8F%B3%E6%BB%91%E5%8A%A8)
+- [单个cell刷新 ](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E5%8D%95%E4%B8%AAcell%E5%88%B7%E6%96%B0)
+- [点击item时指示器和列表滚动时效果一致](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E7%82%B9%E5%87%BBitem%E6%97%B6%E6%8C%87%E7%A4%BA%E5%99%A8%E5%92%8C%E5%88%97%E8%A1%A8%E6%BB%9A%E5%8A%A8%E6%97%B6%E6%95%88%E6%9E%9C%E4%B8%80%E8%87%B4)
+- [自定义建议](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BB%BA%E8%AE%AE)
+
+## 指示器样式自定义
+
+- 需要继承 `JXCategoryIndicatorProtocol` 协议,点击参看 [JXCategoryIndicatorProtocol](https://github.com/pujiaxin33/JXCategoryView/blob/master/Sources/Common/JXCategoryIndicatorProtocol.h);
+- 提供了继承 `JXCategoryIndicatorProtocol` 协议的基类 `JXCategoryIndicatorComponentView`,里面提供了许多基础属性。点击参看 [JXCategoryIndicatorComponentView](https://github.com/pujiaxin33/JXCategoryView/blob/master/Sources/Indicator/IndicatorViews/JXCategoryIndicatorComponentView.m);
+- 自定义指示器,请参考已实现的指示器视图,多尝试、多思考,再有问题请提 Issue 或加入反馈 QQ 群。
+
+## Cell 自定义
+
+- 任何子类化需求,view、cell、cellModel 三个都要子类化,即使某个子类 cell 什么事情都不做。用于维护继承链,以免以后子类化都不知道要继承谁了;
+- 如果你想完全自定义 cell 里面的内容,那就继承 `JXCategoryIndicatorView`、`JXCategoryIndicatorCell、`JXCategoryIndicatorCellModel`,就像`JXCategoryTitleView`、`JXCategoryTitleCell、`JXCategoryTitleCellModel`那样去做;
+- 如果你只是在父类进行一些微调,那就继承目标 view、cell、cellModel,对 cell 原有控件微调、或者加入新的控件皆可。就像 `JXCategoryTitleImageView` 系列、`JXCategoryTitleAttributeView` 系列那样去做;
+- Cell 自定义,请参考已实现的 cell 样式,多尝试、多思考,再有问题请提 Issue 或加入反馈 QQ 群
+
+## 常用属性说明
+
+[常用属性说明文档地址](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E5%B8%B8%E7%94%A8%E5%B1%9E%E6%80%A7%E8%AF%B4%E6%98%8E.md)
+
+## 更新记录
+
+- 2018.8.21 发布1.0.0版本,更新内容:使用POP(面向协议编程)重构指示器视图;[迁移指南](https://github.com/pujiaxin33/JXCategoryView/blob/master/Migration/1.0.0.md)
+- 2018.8.22 发布1.0.1版本,更新内容:删除zoomEnabled,新增titleLabelZoomEnabled、imageZoomEnabled;
+- 2018.8.23 发布1.0.2版本,更新内容:添加cellWidthZoomEnabled实现腾讯视频效果;
+- 2018.8.24 发布1.0.3版本,更新内容:添加垂直列表滚动效果、指示器添加verticalMargin属性、JXCategoryViewDelegate代理方法优化;
+- 2018.9.4 发布1.0.4版本,更新内容:修复bug、添加cell图文混用示例;
+- 2018.12.19 发布1.1.7版本,更新内容:添加`JXCategoryListContainerView`,高度封装列表逻辑,支持懒加载列表,提升初始化性能;
+- 2019.1.24 发布1.2.2版本,更新内容:非兼容更新接口`- (BOOL)selectCellAtIndex:(NSInteger)index selectedType:(JXCategoryCellSelectedType)selectedType`,自定义有用到该接口的请及时更新。
+- 2019.6.21 发布1.3.13版本,更新内容:将`JXCategoryListCollectionContainerView.dataSource`移动到m实现文件,添加`- (instancetype)initWithDataSource:(id<JXCategoryListCollectionContainerViewDataSource>)dataSource`初始化方法。
+- 2019.7.20 发布1.3.16版本,删除代理方法`- (void)categoryView:(JXCategoryBaseView *)categoryView didClickedItemContentScrollViewTransitionToIndex:(NSInteger)index;`,请使用`contentScrollViewClickTransitionAnimationEnabled`属性。`JXCategoryTitleVerticalZoomView`进行了重构,内容左边距只需要使用`contentEdgeLeft`属性即可。
+- 2019.9.11 发布1.4.0版本,删除一波被标记为弃用的属性和方法;完善列表的生命周期方法的调用;`JXCategoryListCollectionContainerView`类新增和必须要调用`- (void)scrollingFromLeftIndex:(NSInteger)leftIndex toRightIndex:(NSInteger)rightIndex ratio:(CGFloat)ratio selectedIndex:(NSInteger)selectedIndex`和`- (void)didClickSelectedItemAtIndex:(NSInteger)index`两个方法。
+- 2019.9.19 发布1.5.0版本,重构列表容器,具体修改请参考[1.5.0版本迁移指南](https://github.com/pujiaxin33/JXCategoryView/blob/master/Migration/1.5.0%E7%89%88%E6%9C%AC%E8%BF%81%E7%A7%BB%E6%8C%87%E5%8D%97.md)
+
+## 补充
+
+如果刚开始使用`JXCategoryView`,当开发过程中需要支持某种特性时,请务必先搜索使用文档或者源代码。确认是否已经实现支持了想要的特性。请别不要文档和源代码都没有看,就直接提问,这对于大家都是一种时间浪费。如果没有支持想要的特性,欢迎提Issue讨论,或者自己实现提一个PullRequest。
+
+该仓库保持随时更新,对于主流新的分类选择效果会第一时间支持。使用过程中,有任何建议或问题,可以通过以下方式联系我:</br>
+邮箱:317437084@qq.com </br>
+QQ群: 112440473
+
+<img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/JXCategoryViewQQGroupTwo.JPG" width="300" height="411">
+
+喜欢就star❤️一下吧
+
+## License
+
+JXCategoryView is released under the MIT license.

+ 29 - 0
MiMoLive/Pods/JXCategoryView/Sources/Base/JXCategoryBaseCell.h

@@ -0,0 +1,29 @@
+//
+//  JXCategoryBaseCell.h
+//  UI系列测试
+//
+//  Created by jiaxin on 2018/3/15.
+//  Copyright © 2018年 jiaxin. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "JXCategoryBaseCellModel.h"
+#import "JXCategoryViewAnimator.h"
+#import "JXCategoryViewDefines.h"
+
+@interface JXCategoryBaseCell : UICollectionViewCell
+
+@property (nonatomic, strong, readonly) JXCategoryBaseCellModel *cellModel;
+@property (nonatomic, strong, readonly) JXCategoryViewAnimator *animator;
+
+- (void)initializeViews NS_REQUIRES_SUPER;
+
+- (void)reloadData:(JXCategoryBaseCellModel *)cellModel NS_REQUIRES_SUPER;
+
+- (BOOL)checkCanStartSelectedAnimation:(JXCategoryBaseCellModel *)cellModel;
+
+- (void)addSelectedAnimationBlock:(JXCategoryCellSelectedAnimationBlock)block;
+
+- (void)startSelectedAnimationIfNeeded:(JXCategoryBaseCellModel *)cellModel;
+
+@end

+ 98 - 0
MiMoLive/Pods/JXCategoryView/Sources/Base/JXCategoryBaseCell.m

@@ -0,0 +1,98 @@
+//
+//  JXCategoryBaseCell.m
+//  UI系列测试
+//
+//  Created by jiaxin on 2018/3/15.
+//  Copyright © 2018年 jiaxin. All rights reserved.
+//
+
+#import "JXCategoryBaseCell.h"
+#import "RTLManager.h"
+
+@interface JXCategoryBaseCell ()
+@property (nonatomic, strong) JXCategoryBaseCellModel *cellModel;
+@property (nonatomic, strong) JXCategoryViewAnimator *animator;
+@property (nonatomic, strong) NSMutableArray <JXCategoryCellSelectedAnimationBlock> *animationBlockArray;
+@end
+
+@implementation JXCategoryBaseCell
+
+#pragma mark - Initialize
+
+- (void)dealloc {
+    [self.animator stop];
+}
+
+- (void)prepareForReuse {
+    [super prepareForReuse];
+
+    [self.animator stop];
+}
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    self = [super initWithFrame:frame];
+    if (self) {
+        [self initializeViews];
+    }
+    return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+    self = [super initWithCoder:coder];
+    if (self) {
+        [self initializeViews];
+    }
+    return self;
+}
+
+#pragma mark - Public
+
+- (void)initializeViews {
+    _animationBlockArray = [NSMutableArray array];
+
+    [RTLManager horizontalFlipViewIfNeeded:self];
+    [RTLManager horizontalFlipViewIfNeeded:self.contentView];
+}
+
+- (void)reloadData:(JXCategoryBaseCellModel *)cellModel {
+    self.cellModel = cellModel;
+
+    if (cellModel.isSelectedAnimationEnabled) {
+        [self.animationBlockArray removeLastObject];
+        if ([self checkCanStartSelectedAnimation:cellModel]) {
+            self.animator = [[JXCategoryViewAnimator alloc] init];
+            self.animator.duration = cellModel.selectedAnimationDuration;
+        } else {
+            [self.animator stop];
+        }
+    }
+}
+
+- (BOOL)checkCanStartSelectedAnimation:(JXCategoryBaseCellModel *)cellModel {
+    BOOL canStartSelectedAnimation = ((cellModel.selectedType == JXCategoryCellSelectedTypeCode) || (cellModel.selectedType == JXCategoryCellSelectedTypeClick));
+    return canStartSelectedAnimation;
+}
+
+- (void)addSelectedAnimationBlock:(JXCategoryCellSelectedAnimationBlock)block {
+    [self.animationBlockArray addObject:block];
+}
+
+- (void)startSelectedAnimationIfNeeded:(JXCategoryBaseCellModel *)cellModel {
+    if (cellModel.isSelectedAnimationEnabled && [self checkCanStartSelectedAnimation:cellModel]) {
+        // 需要更新 isTransitionAnimating,用于处理在过滤时,禁止响应点击,避免界面异常。
+        cellModel.transitionAnimating = YES;
+        __weak typeof(self)weakSelf = self;
+        self.animator.progressCallback = ^(CGFloat percent) {
+            for (JXCategoryCellSelectedAnimationBlock block in weakSelf.animationBlockArray) {
+                block(percent);
+            }
+        };
+        self.animator.completeCallback = ^{
+            cellModel.transitionAnimating = NO;
+            [weakSelf.animationBlockArray removeAllObjects];
+        };
+        [self.animator start];
+    }
+}
+
+@end

+ 31 - 0
MiMoLive/Pods/JXCategoryView/Sources/Base/JXCategoryBaseCellModel.h

@@ -0,0 +1,31 @@
+//
+//  JXCategoryBaseCellModel.h
+//  UI系列测试
+//
+//  Created by jiaxin on 2018/3/15.
+//  Copyright © 2018年 jiaxin. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+#import "JXCategoryViewDefines.h"
+
+@interface JXCategoryBaseCellModel : NSObject
+
+@property (nonatomic, assign) NSUInteger index;
+@property (nonatomic, assign) CGFloat cellWidth;
+@property (nonatomic, assign) CGFloat cellSpacing;
+@property (nonatomic, assign, getter=isSelected) BOOL selected;
+
+@property (nonatomic, assign, getter=isCellWidthZoomEnabled) BOOL cellWidthZoomEnabled;
+@property (nonatomic, assign) CGFloat cellWidthNormalZoomScale;
+@property (nonatomic, assign) CGFloat cellWidthCurrentZoomScale;
+@property (nonatomic, assign) CGFloat cellWidthSelectedZoomScale;
+
+@property (nonatomic, assign, getter=isSelectedAnimationEnabled) BOOL selectedAnimationEnabled;
+@property (nonatomic, assign) NSTimeInterval selectedAnimationDuration;
+@property (nonatomic, assign) JXCategoryCellSelectedType selectedType;
+
+@property (nonatomic, assign, getter=isTransitionAnimating) BOOL transitionAnimating;
+
+@end

+ 13 - 0
MiMoLive/Pods/JXCategoryView/Sources/Base/JXCategoryBaseCellModel.m

@@ -0,0 +1,13 @@
+//
+//  JXCategoryBaseCellModel.m
+//  UI系列测试
+//
+//  Created by jiaxin on 2018/3/15.
+//  Copyright © 2018年 jiaxin. All rights reserved.
+//
+
+#import "JXCategoryBaseCellModel.h"
+
+@implementation JXCategoryBaseCellModel
+
+@end

+ 222 - 0
MiMoLive/Pods/JXCategoryView/Sources/Base/JXCategoryBaseView.h

@@ -0,0 +1,222 @@
+//
+//  JXCategoryView.h
+//  UI系列测试
+//
+//  Created by jiaxin on 2018/3/15.
+//  Copyright © 2018年 jiaxin. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "JXCategoryBaseCell.h"
+#import "JXCategoryBaseCellModel.h"
+#import "JXCategoryCollectionView.h"
+#import "JXCategoryViewDefines.h"
+
+@class JXCategoryBaseView;
+
+@protocol JXCategoryViewListContainer <NSObject>
+- (void)setDefaultSelectedIndex:(NSInteger)index;
+- (UIScrollView *)contentScrollView;
+- (void)reloadData;
+- (void)didClickSelectedItemAtIndex:(NSInteger)index;
+@end
+
+@protocol JXCategoryViewDelegate <NSObject>
+
+@optional
+
+//为什么会把选中代理分为三个,因为有时候只关心点击选中的,有时候只关心滚动选中的,有时候只关心选中。所以具体情况,使用对应方法。
+/**
+ 点击选中或者滚动选中都会调用该方法。适用于只关心选中事件,不关心具体是点击还是滚动选中的。
+
+ @param categoryView categoryView对象
+ @param index 选中的index
+ */
+- (void)categoryView:(JXCategoryBaseView *)categoryView didSelectedItemAtIndex:(NSInteger)index;
+
+/**
+ 点击选中的情况才会调用该方法
+
+ @param categoryView categoryView对象
+ @param index 选中的index
+ */
+- (void)categoryView:(JXCategoryBaseView *)categoryView didClickSelectedItemAtIndex:(NSInteger)index;
+
+/**
+ 滚动选中的情况才会调用该方法
+
+ @param categoryView categoryView对象
+ @param index 选中的index
+ */
+- (void)categoryView:(JXCategoryBaseView *)categoryView didScrollSelectedItemAtIndex:(NSInteger)index;
+
+/**
+ 控制能否点击Item
+
+ @param categoryView categoryView对象
+ @param index 准备点击的index
+ @return 能否点击
+ */
+- (BOOL)categoryView:(JXCategoryBaseView *)categoryView canClickItemAtIndex:(NSInteger)index;
+
+/**
+ 正在滚动中的回调
+
+ @param categoryView categoryView对象
+ @param leftIndex 正在滚动中,相对位置处于左边的index
+ @param rightIndex 正在滚动中,相对位置处于右边的index
+ @param ratio 从左往右计算的百分比
+ */
+- (void)categoryView:(JXCategoryBaseView *)categoryView scrollingFromLeftIndex:(NSInteger)leftIndex toRightIndex:(NSInteger)rightIndex ratio:(CGFloat)ratio;
+
+@end
+
+@interface JXCategoryBaseView : UIView
+
+@property (nonatomic, strong, readonly) JXCategoryCollectionView *collectionView;
+
+@property (nonatomic, strong) NSArray <JXCategoryBaseCellModel *> *dataSource;
+
+@property (nonatomic, weak) id<JXCategoryViewDelegate> delegate;
+
+/**
+ 高封装度的列表容器,使用该类可以让列表拥有完成的生命周期、自动同步defaultSelectedIndex、自动调用reloadData。
+ */
+@property (nonatomic, weak) id<JXCategoryViewListContainer> listContainer;
+
+/**
+ 推荐使用封装度更高的listContainer属性。如果使用contentScrollView请参考`LoadDataListCustomViewController`使用示例。
+ */
+@property (nonatomic, strong) UIScrollView *contentScrollView;
+
+@property (nonatomic, assign) NSInteger defaultSelectedIndex;   //修改初始化的时候默认选择的index
+
+@property (nonatomic, assign, readonly) NSInteger selectedIndex;
+
+@property (nonatomic, assign, getter=isContentScrollViewClickTransitionAnimationEnabled) BOOL contentScrollViewClickTransitionAnimationEnabled;    //点击cell进行contentScrollView切换时是否需要动画。默认为YES
+
+@property (nonatomic, assign) CGFloat contentEdgeInsetLeft;     //整体内容的左边距,默认JXCategoryViewAutomaticDimension(等于cellSpacing)
+
+@property (nonatomic, assign) CGFloat contentEdgeInsetRight;    //整体内容的右边距,默认JXCategoryViewAutomaticDimension(等于cellSpacing)
+
+@property (nonatomic, assign) CGFloat cellWidth;    //默认JXCategoryViewAutomaticDimension
+
+@property (nonatomic, assign) CGFloat cellWidthIncrement;    //cell宽度补偿。默认:0
+
+@property (nonatomic, assign) CGFloat cellSpacing;    //cell之间的间距,默认20
+
+@property (nonatomic, assign, getter=isAverageCellSpacingEnabled) BOOL averageCellSpacingEnabled;     //当collectionView.contentSize.width小于JXCategoryBaseView的宽度,是否将cellSpacing均分。默认为YES。
+
+//cell宽度是否缩放
+@property (nonatomic, assign, getter=isCellWidthZoomEnabled) BOOL cellWidthZoomEnabled;     //默认为NO
+
+@property (nonatomic, assign, getter=isCellWidthZoomScrollGradientEnabled) BOOL cellWidthZoomScrollGradientEnabled;     //手势滚动过程中,是否需要更新cell的宽度。默认为YES
+
+@property (nonatomic, assign) CGFloat cellWidthZoomScale;    //默认1.2,cellWidthZoomEnabled为YES才生效
+
+@property (nonatomic, assign, getter=isSelectedAnimationEnabled) BOOL selectedAnimationEnabled;    //是否开启点击或代码选中动画。默认为NO。自定义的cell选中动画需要自己实现。(仅点击或调用selectItemAtIndex选中才有效,滚动选中无效)
+
+@property (nonatomic, assign) NSTimeInterval selectedAnimationDuration;     //cell选中动画的时间。默认0.25
+
+/**
+ 选中目标index的item
+
+ @param index 目标index
+ */
+- (void)selectItemAtIndex:(NSInteger)index;
+
+/**
+ 初始化的时候无需调用。比如页面初始化之后,根据网络接口异步回调回来数据,重新配置categoryView,需要调用该方法进行刷新。
+ */
+- (void)reloadData;
+
+/**
+ 重新配置categoryView但是不需要reload listContainer。特殊情况是该方法。
+ */
+- (void)reloadDataWithoutListContainer;
+
+/**
+ 刷新指定的index的cell
+ 内部会触发`- (void)refreshCellModel:(JXCategoryBaseCellModel *)cellModel index:(NSInteger)index`方法进行cellModel刷新
+
+ @param index 指定cell的index
+ */
+- (void)reloadCellAtIndex:(NSInteger)index;
+
+@end
+
+
+
+@interface JXCategoryBaseView (UISubclassingBaseHooks)
+
+/**
+ 获取目标cell当前的frame,反应当前真实的frame受到cellWidthSelectedZoomScale的影响。
+ */
+- (CGRect)getTargetCellFrame:(NSInteger)targetIndex;
+
+/**
+ 获取目标cell的选中时的frame,其他cell的状态都当做普通状态处理。
+ */
+- (CGRect)getTargetSelectedCellFrame:(NSInteger)targetIndex selectedType:(JXCategoryCellSelectedType)selectedType;
+- (void)initializeData NS_REQUIRES_SUPER;
+- (void)initializeViews NS_REQUIRES_SUPER;
+
+/**
+ reloadData方法调用,重新生成数据源赋值到self.dataSource
+ */
+- (void)refreshDataSource;
+
+/**
+ reloadData方法调用,根据数据源重新刷新状态;
+ */
+- (void)refreshState NS_REQUIRES_SUPER;
+
+/**
+ 选中某个item时,刷新将要选中与取消选中的cellModel
+
+ @param selectedCellModel 将要选中的cellModel
+ @param unselectedCellModel 取消选中的cellModel
+ */
+- (void)refreshSelectedCellModel:(JXCategoryBaseCellModel *)selectedCellModel unselectedCellModel:(JXCategoryBaseCellModel *)unselectedCellModel NS_REQUIRES_SUPER;
+
+/**
+ 关联的contentScrollView的contentOffset发生了改变
+
+ @param contentOffset 偏移量
+ */
+- (void)contentOffsetOfContentScrollViewDidChanged:(CGPoint)contentOffset NS_REQUIRES_SUPER;
+
+/**
+ 选中某一个item的时候调用,该方法用于子类重载。
+ 如果外部要选中某个index,请使用`- (void)selectItemAtIndex:(NSUInteger)index;`
+
+ @param index 选中的index
+ @param selectedType JXCategoryCellSelectedType
+ @return 返回值为NO,表示触发内部某些判断(点击了同一个cell),子类无需后续操作。
+ */
+- (BOOL)selectCellAtIndex:(NSInteger)index selectedType:(JXCategoryCellSelectedType)selectedType NS_REQUIRES_SUPER;
+
+/**
+ reloadData时,返回每个cell的宽度
+
+ @param index 目标index
+ @return cellWidth
+ */
+- (CGFloat)preferredCellWidthAtIndex:(NSInteger)index;
+
+/**
+ 返回自定义cell的class
+
+ @return cell class
+ */
+- (Class)preferredCellClass;
+
+/**
+ refreshState时调用,重置cellModel的状态
+
+ @param cellModel 待重置的cellModel
+ @param index cellModel在数组中的index
+ */
+- (void)refreshCellModel:(JXCategoryBaseCellModel *)cellModel index:(NSInteger)index NS_REQUIRES_SUPER;
+
+@end

+ 687 - 0
MiMoLive/Pods/JXCategoryView/Sources/Base/JXCategoryBaseView.m

@@ -0,0 +1,687 @@
+//
+//  JXCategoryBaseView.m
+//  UI系列测试
+//
+//  Created by jiaxin on 2018/3/15.
+//  Copyright © 2018年 jiaxin. All rights reserved.
+//
+
+#import "JXCategoryBaseView.h"
+#import "JXCategoryFactory.h"
+#import "JXCategoryViewAnimator.h"
+#import "RTLManager.h"
+
+struct DelegateFlags {
+    unsigned int didSelectedItemAtIndexFlag : 1;
+    unsigned int didClickSelectedItemAtIndexFlag : 1;
+    unsigned int didScrollSelectedItemAtIndexFlag : 1;
+    unsigned int canClickItemAtIndexFlag : 1;
+    unsigned int scrollingFromLeftIndexToRightIndexFlag : 1;
+};
+
+@interface JXCategoryBaseView () <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
+
+@property (nonatomic, strong) JXCategoryCollectionView *collectionView;
+@property (nonatomic, assign) struct DelegateFlags delegateFlags;
+@property (nonatomic, assign) NSInteger selectedIndex;
+@property (nonatomic, assign) CGFloat innerCellSpacing;
+@property (nonatomic, assign) CGPoint lastContentViewContentOffset;
+@property (nonatomic, strong) JXCategoryViewAnimator *animator;
+// 正在滚动中的目标index。用于处理正在滚动列表的时候,立即点击item,会导致界面显示异常。
+@property (nonatomic, assign) NSInteger scrollingTargetIndex;
+@property (nonatomic, assign, getter=isNeedReloadByBecomeActive) BOOL needReloadByBecomeActive;
+@property (nonatomic, assign, getter=isFirstLayoutSubviews) BOOL firstLayoutSubviews;
+@property (nonatomic, assign, getter=isNeedConfigAutomaticallyAdjustsScrollViewInsets) BOOL needConfigAutomaticallyAdjustsScrollViewInsets;
+
+@end
+
+@implementation JXCategoryBaseView
+
+- (void)dealloc {
+    if (self.contentScrollView) {
+        [self.contentScrollView removeObserver:self forKeyPath:@"contentOffset"];
+    }
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
+    [self.animator stop];
+}
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    self = [super initWithFrame:frame];
+    if (self) {
+        [self initializeData];
+        [self initializeViews];
+    }
+    return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+    self = [super initWithCoder:coder];
+    if (self) {
+        [self initializeData];
+        [self initializeViews];
+    }
+    return self;
+}
+
+- (void)willMoveToSuperview:(UIView *)newSuperview {
+    [super willMoveToSuperview:newSuperview];
+
+    [self configAutomaticallyAdjustsScrollViewInsets:newSuperview];
+}
+
+- (void)reloadData {
+    [self reloadDataWithoutListContainer];
+    [self.listContainer reloadData];
+}
+
+- (void)reloadDataWithoutListContainer {
+    [self refreshDataSource];
+    [self refreshState];
+    [self.collectionView.collectionViewLayout invalidateLayout];
+    [self.collectionView reloadData];
+}
+
+- (void)reloadCellAtIndex:(NSInteger)index {
+    if (index < 0 || index >= self.dataSource.count) {
+        return;
+    }
+    JXCategoryBaseCellModel *cellModel = self.dataSource[index];
+    cellModel.selectedType = JXCategoryCellSelectedTypeUnknown;
+    [self refreshCellModel:cellModel index:index];
+    JXCategoryBaseCell *cell = (JXCategoryBaseCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
+    [cell reloadData:cellModel];
+}
+
+- (void)selectItemAtIndex:(NSInteger)index {
+    [self selectCellAtIndex:index selectedType:JXCategoryCellSelectedTypeCode];
+}
+
+- (void)layoutSubviews {
+    [super layoutSubviews];
+
+    //部分使用者为了适配不同的手机屏幕尺寸,JXCategoryView的宽高比要求保持一样,所以它的高度就会因为不同宽度的屏幕而不一样。计算出来的高度,有时候会是位数很长的浮点数,如果把这个高度设置给UICollectionView就会触发内部的一个错误。所以,为了规避这个问题,在这里对高度统一向下取整。
+    //如果向下取整导致了你的页面异常,请自己重新设置JXCategoryView的高度,保证为整数即可。
+    CGRect targetFrame = CGRectMake(0, 0, self.bounds.size.width, floor(self.bounds.size.height));
+    if (self.isFirstLayoutSubviews) {
+        if (self.bounds.size.width == 0 || self.bounds.size.height == 0) {
+            return;
+        }
+        if (self.isNeedConfigAutomaticallyAdjustsScrollViewInsets) {
+            [self configAutomaticallyAdjustsScrollViewInsets:self.superview];
+        }
+        self.firstLayoutSubviews = NO;
+        self.collectionView.frame = targetFrame;
+        [self reloadDataWithoutListContainer];
+    }else {
+        if (!CGRectEqualToRect(self.collectionView.frame, targetFrame)) {
+            self.collectionView.frame = targetFrame;
+            [self refreshState];
+            [self.collectionView.collectionViewLayout invalidateLayout];
+            [self.collectionView reloadData];
+        }
+    }
+}
+
+#pragma mark - Setter
+
+- (void)setDelegate:(id<JXCategoryViewDelegate>)delegate {
+    _delegate = delegate;
+
+    _delegateFlags.didSelectedItemAtIndexFlag = [delegate respondsToSelector:@selector(categoryView:didSelectedItemAtIndex:)];
+    _delegateFlags.didClickSelectedItemAtIndexFlag = [delegate respondsToSelector:@selector(categoryView:didClickSelectedItemAtIndex:)];
+    _delegateFlags.didScrollSelectedItemAtIndexFlag = [delegate respondsToSelector:@selector(categoryView:didScrollSelectedItemAtIndex:)];
+    _delegateFlags.canClickItemAtIndexFlag = [delegate respondsToSelector:@selector(categoryView:canClickItemAtIndex:)];
+    _delegateFlags.scrollingFromLeftIndexToRightIndexFlag = [delegate respondsToSelector:@selector(categoryView:scrollingFromLeftIndex:toRightIndex:ratio:)];
+}
+
+- (void)setDefaultSelectedIndex:(NSInteger)defaultSelectedIndex {
+    _defaultSelectedIndex = defaultSelectedIndex;
+
+    self.selectedIndex = defaultSelectedIndex;
+    [self.listContainer setDefaultSelectedIndex:defaultSelectedIndex];
+}
+
+- (void)setContentScrollView:(UIScrollView *)contentScrollView {
+    if (_contentScrollView != nil) {
+        [_contentScrollView removeObserver:self forKeyPath:@"contentOffset"];
+    }
+    _contentScrollView = contentScrollView;
+
+    self.contentScrollView.scrollsToTop = NO;
+    [self.contentScrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
+}
+
+- (void)setListContainer:(id<JXCategoryViewListContainer>)listContainer {
+    _listContainer = listContainer;
+    [listContainer setDefaultSelectedIndex:self.defaultSelectedIndex];
+    self.contentScrollView = [listContainer contentScrollView];
+}
+
+#pragma mark - <UICollectionViewDataSource, UICollectionViewDelegate>
+
+- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
+    return 1;
+}
+
+- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
+    return self.dataSource.count;
+}
+
+- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
+    return [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([self preferredCellClass]) forIndexPath:indexPath];
+}
+
+- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
+    JXCategoryBaseCellModel *cellModel = self.dataSource[indexPath.item];
+    cellModel.selectedType = JXCategoryCellSelectedTypeUnknown;
+    [(JXCategoryBaseCell *)cell reloadData:cellModel];
+}
+
+- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
+    BOOL isTransitionAnimating = NO;
+    for (JXCategoryBaseCellModel *cellModel in self.dataSource) {
+        if (cellModel.isTransitionAnimating) {
+            isTransitionAnimating = YES;
+            break;
+        }
+    }
+    if (!isTransitionAnimating) {
+        //当前没有正在过渡的item,才允许点击选中
+        [self clickSelectItemAtIndex:indexPath.row];
+    }
+}
+
+#pragma mark - <UICollectionViewDelegateFlowLayout>
+
+- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
+    return UIEdgeInsetsMake(0, [self getContentEdgeInsetLeft], 0, [self getContentEdgeInsetRight]);
+}
+
+- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
+    return CGSizeMake(self.dataSource[indexPath.item].cellWidth, self.collectionView.bounds.size.height);
+}
+
+- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
+    return self.innerCellSpacing;
+}
+
+- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
+    return self.innerCellSpacing;
+}
+
+#pragma mark - KVO
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
+    if ([keyPath isEqualToString:@"contentOffset"]) {
+        CGPoint contentOffset = [change[NSKeyValueChangeNewKey] CGPointValue];
+        if ((self.contentScrollView.isTracking || self.contentScrollView.isDecelerating)) {
+            //只处理用户滚动的情况
+            [self contentOffsetOfContentScrollViewDidChanged:contentOffset];
+        }
+        self.lastContentViewContentOffset = contentOffset;
+    }
+}
+
+#pragma mark - Private
+
+- (void)configAutomaticallyAdjustsScrollViewInsets:(UIView *)view {
+    UIResponder *next = view;
+    while (next != nil) {
+        if ([next isKindOfClass:[UIViewController class]]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+            ((UIViewController *)next).automaticallyAdjustsScrollViewInsets = NO;
+#pragma clang diagnostic pop
+            self.needConfigAutomaticallyAdjustsScrollViewInsets = NO;
+            break;
+        }
+        next = next.nextResponder;
+    }
+}
+
+- (CGFloat)getContentEdgeInsetLeft {
+    if (self.contentEdgeInsetLeft == JXCategoryViewAutomaticDimension) {
+        return self.innerCellSpacing;
+    }
+    return self.contentEdgeInsetLeft;
+}
+
+- (CGFloat)getContentEdgeInsetRight {
+    if (self.contentEdgeInsetRight == JXCategoryViewAutomaticDimension) {
+        return self.innerCellSpacing;
+    }
+    return self.contentEdgeInsetRight;
+}
+
+- (CGFloat)getCellWidthAtIndex:(NSInteger)index {
+    return [self preferredCellWidthAtIndex:index] + self.cellWidthIncrement;
+}
+
+- (void)clickSelectItemAtIndex:(NSInteger)index {
+    if (self.delegateFlags.canClickItemAtIndexFlag && ![self.delegate categoryView:self canClickItemAtIndex:index]) {
+        return;
+    }
+
+    [self selectCellAtIndex:index selectedType:JXCategoryCellSelectedTypeClick];
+}
+
+- (void)scrollSelectItemAtIndex:(NSInteger)index {
+    [self selectCellAtIndex:index selectedType:JXCategoryCellSelectedTypeScroll];
+}
+
+- (void)applicationDidBecomeActive:(NSNotification *)notification {
+    if (self.isNeedReloadByBecomeActive) {
+        self.needReloadByBecomeActive = NO;
+        [self reloadData];
+    }
+}
+
+@end
+
+@implementation JXCategoryBaseView (UISubclassingBaseHooks)
+
+- (CGRect)getTargetCellFrame:(NSInteger)targetIndex {
+    CGFloat x = [self getContentEdgeInsetLeft];
+    for (int i = 0; i < targetIndex; i ++) {
+        JXCategoryBaseCellModel *cellModel = self.dataSource[i];
+        CGFloat cellWidth;
+        if (cellModel.isTransitionAnimating && cellModel.isCellWidthZoomEnabled) {
+            //正在进行动画的时候,cellWidthCurrentZoomScale是随着动画渐变的,而没有立即更新到目标值
+            if (cellModel.isSelected) {
+                cellWidth = [self getCellWidthAtIndex:cellModel.index]*cellModel.cellWidthSelectedZoomScale;
+            }else {
+                cellWidth = [self getCellWidthAtIndex:cellModel.index]*cellModel.cellWidthNormalZoomScale;
+            }
+        }else {
+            cellWidth = cellModel.cellWidth;
+        }
+        x += cellWidth + self.innerCellSpacing;
+    }
+    CGFloat width;
+    JXCategoryBaseCellModel *selectedCellModel = self.dataSource[targetIndex];
+    if (selectedCellModel.isTransitionAnimating && selectedCellModel.isCellWidthZoomEnabled) {
+        width = [self getCellWidthAtIndex:selectedCellModel.index]*selectedCellModel.cellWidthSelectedZoomScale;
+    }else {
+        width = selectedCellModel.cellWidth;
+    }
+    return CGRectMake(x, 0, width, self.bounds.size.height);
+}
+
+- (CGRect)getTargetSelectedCellFrame:(NSInteger)targetIndex selectedType:(JXCategoryCellSelectedType)selectedType {
+    CGFloat x = [self getContentEdgeInsetLeft];
+    for (int i = 0; i < targetIndex; i ++) {
+        JXCategoryBaseCellModel *cellModel = self.dataSource[i];
+        x += [self getCellWidthAtIndex:cellModel.index] + self.innerCellSpacing;
+    }
+    CGFloat cellWidth = 0;
+    JXCategoryBaseCellModel *selectedCellModel = self.dataSource[targetIndex];
+    if (selectedCellModel.cellWidthZoomEnabled) {
+        cellWidth = [self getCellWidthAtIndex:targetIndex]*selectedCellModel.cellWidthSelectedZoomScale;
+    }else {
+        cellWidth = [self getCellWidthAtIndex:targetIndex];
+    }
+    return CGRectMake(x, 0, cellWidth, self.bounds.size.height);
+}
+
+- (void)initializeData {
+    _firstLayoutSubviews = YES;
+    _dataSource = [NSMutableArray array];
+    _selectedIndex = 0;
+    _cellWidth = JXCategoryViewAutomaticDimension;
+    _cellWidthIncrement = 0;
+    _cellSpacing = 20;
+    _averageCellSpacingEnabled = YES;
+    _cellWidthZoomEnabled = NO;
+    _cellWidthZoomScale = 1.2;
+    _cellWidthZoomScrollGradientEnabled = YES;
+    _contentEdgeInsetLeft = JXCategoryViewAutomaticDimension;
+    _contentEdgeInsetRight = JXCategoryViewAutomaticDimension;
+    _lastContentViewContentOffset = CGPointZero;
+    _selectedAnimationEnabled = NO;
+    _selectedAnimationDuration = 0.25;
+    _scrollingTargetIndex = -1;
+    _contentScrollViewClickTransitionAnimationEnabled = YES;
+    _needReloadByBecomeActive = NO;
+}
+
+- (void)initializeViews {
+    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
+    layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
+    _collectionView = [[JXCategoryCollectionView alloc] initWithFrame:self.bounds collectionViewLayout:layout];
+    self.collectionView.backgroundColor = [UIColor clearColor];
+    self.collectionView.showsHorizontalScrollIndicator = NO;
+    self.collectionView.showsVerticalScrollIndicator = NO;
+    self.collectionView.scrollsToTop = NO;
+    self.collectionView.dataSource = self;
+    self.collectionView.delegate = self;
+    [self.collectionView registerClass:[self preferredCellClass] forCellWithReuseIdentifier:NSStringFromClass([self preferredCellClass])];
+    if (@available(iOS 10.0, *)) {
+        self.collectionView.prefetchingEnabled = NO;
+    }
+    if (@available(iOS 11.0, *)) {
+        if ([self.collectionView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
+            self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
+        }
+    }
+    if ([RTLManager supportRTL]) {
+        self.collectionView.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight;
+        [RTLManager horizontalFlipView:self.collectionView];
+    }
+    [self addSubview:self.collectionView];
+
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
+}
+
+- (void)refreshDataSource {}
+
+- (void)refreshState {
+    if (self.selectedIndex < 0 || self.selectedIndex >= self.dataSource.count) {
+        self.defaultSelectedIndex = 0;
+    }
+
+    self.innerCellSpacing = self.cellSpacing;
+    //总的内容宽度(左边距+cell总宽度+总cellSpacing+右边距)
+    __block CGFloat totalItemWidth = [self getContentEdgeInsetLeft];
+    //总的cell宽度
+    CGFloat totalCellWidth = 0;
+    for (int i = 0; i < self.dataSource.count; i++) {
+        JXCategoryBaseCellModel *cellModel = self.dataSource[i];
+        cellModel.index = i;
+        cellModel.cellWidthZoomEnabled = self.cellWidthZoomEnabled;
+        cellModel.cellWidthNormalZoomScale = 1;
+        cellModel.cellWidthSelectedZoomScale = self.cellWidthZoomScale;
+        cellModel.selectedAnimationEnabled = self.selectedAnimationEnabled;
+        cellModel.selectedAnimationDuration = self.selectedAnimationDuration;
+        cellModel.cellSpacing = self.innerCellSpacing;
+        if (i == self.selectedIndex) {
+            cellModel.selected = YES;
+            cellModel.cellWidthCurrentZoomScale = cellModel.cellWidthSelectedZoomScale;
+        }else {
+            cellModel.selected = NO;
+            cellModel.cellWidthCurrentZoomScale = cellModel.cellWidthNormalZoomScale;
+        }
+        if (self.isCellWidthZoomEnabled) {
+            cellModel.cellWidth = [self getCellWidthAtIndex:i]*cellModel.cellWidthCurrentZoomScale;
+        }else {
+            cellModel.cellWidth = [self getCellWidthAtIndex:i];
+        }
+        totalCellWidth += cellModel.cellWidth;
+        if (i == self.dataSource.count - 1) {
+            totalItemWidth += cellModel.cellWidth + [self getContentEdgeInsetRight];
+        }else {
+            totalItemWidth += cellModel.cellWidth + self.innerCellSpacing;
+        }
+        [self refreshCellModel:cellModel index:i];
+    }
+
+    if (self.isAverageCellSpacingEnabled && totalItemWidth < self.bounds.size.width) {
+        //如果总的内容宽度都没有超过视图宽度,就将cellSpacing等分
+        NSInteger cellSpacingItemCount = self.dataSource.count - 1;
+        CGFloat totalCellSpacingWidth = self.bounds.size.width - totalCellWidth;
+        //如果内容左边距是Automatic,就加1
+        if (self.contentEdgeInsetLeft == JXCategoryViewAutomaticDimension) {
+            cellSpacingItemCount += 1;
+        }else {
+            totalCellSpacingWidth -= self.contentEdgeInsetLeft;
+        }
+        //如果内容右边距是Automatic,就加1
+        if (self.contentEdgeInsetRight == JXCategoryViewAutomaticDimension) {
+            cellSpacingItemCount += 1;
+        }else {
+            totalCellSpacingWidth -= self.contentEdgeInsetRight;
+        }
+
+        CGFloat cellSpacing = 0;
+        if (cellSpacingItemCount > 0) {
+            cellSpacing = totalCellSpacingWidth/cellSpacingItemCount;
+        }
+        self.innerCellSpacing = cellSpacing;
+        [self.dataSource enumerateObjectsUsingBlock:^(JXCategoryBaseCellModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+            obj.cellSpacing = self.innerCellSpacing;
+        }];
+    }
+
+    //---------------------定位collectionView到当前选中的位置----------------------
+    //因为初始化的时候,collectionView并没有初始化完,cell都没有被加载出来。只有自己手动计算当前选中的index的位置,然后更新到contentOffset
+    __block CGFloat frameXOfSelectedCell = [self getContentEdgeInsetLeft];
+    __block CGFloat selectedCellWidth = 0;
+    totalItemWidth = [self getContentEdgeInsetLeft];
+    [self.dataSource enumerateObjectsUsingBlock:^(JXCategoryBaseCellModel * cellModel, NSUInteger idx, BOOL * _Nonnull stop) {
+        if (idx < self.selectedIndex) {
+            frameXOfSelectedCell += cellModel.cellWidth + self.innerCellSpacing;
+        }else if (idx == self.selectedIndex) {
+            selectedCellWidth = cellModel.cellWidth;
+        }
+        if (idx == self.dataSource.count - 1) {
+            totalItemWidth += cellModel.cellWidth + [self getContentEdgeInsetRight];
+        }else {
+            totalItemWidth += cellModel.cellWidth + self.innerCellSpacing;
+        }
+    }];
+
+    CGFloat minX = 0;
+    CGFloat maxX = totalItemWidth - self.bounds.size.width;
+    CGFloat targetX = frameXOfSelectedCell - self.bounds.size.width/2.0 + selectedCellWidth/2.0;
+    [self.collectionView setContentOffset:CGPointMake(MAX(MIN(maxX, targetX), minX), 0) animated:NO];
+    //---------------------定位collectionView到当前选中的位置----------------------
+
+    if (CGRectEqualToRect(self.contentScrollView.frame, CGRectZero) && self.contentScrollView.superview != nil) {
+        //某些情况系统会出现JXCategoryView先布局,contentScrollView后布局。就会导致下面指定defaultSelectedIndex失效,所以发现contentScrollView的frame为zero时,强行触发其父视图链里面已经有frame的一个父视图的layoutSubviews方法。
+        //比如JXSegmentedListContainerView会将contentScrollView包裹起来使用,该情况需要JXSegmentedListContainerView.superView触发布局更新
+        UIView *parentView = self.contentScrollView.superview;
+        while (parentView != nil && CGRectEqualToRect(parentView.frame, CGRectZero)) {
+            parentView = parentView.superview;
+        }
+        [parentView setNeedsLayout];
+        [parentView layoutIfNeeded];
+    }
+    //将contentScrollView的contentOffset定位到当前选中index的位置
+    [self.contentScrollView setContentOffset:CGPointMake(self.selectedIndex*self.contentScrollView.bounds.size.width, 0) animated:NO];
+}
+
+- (BOOL)selectCellAtIndex:(NSInteger)targetIndex selectedType:(JXCategoryCellSelectedType)selectedType {
+    if (targetIndex < 0 || targetIndex >= self.dataSource.count) {
+        return NO;
+    }
+
+    self.needReloadByBecomeActive = NO;
+    if (self.selectedIndex == targetIndex) {
+        //目标index和当前选中的index相等,就不需要处理后续的选中更新逻辑,只需要回调代理方法即可。
+        if (selectedType == JXCategoryCellSelectedTypeCode) {
+            [self.listContainer didClickSelectedItemAtIndex:targetIndex];
+        }else if (selectedType == JXCategoryCellSelectedTypeClick) {
+            [self.listContainer didClickSelectedItemAtIndex:targetIndex];
+            if (self.delegateFlags.didClickSelectedItemAtIndexFlag) {
+                [self.delegate categoryView:self didClickSelectedItemAtIndex:targetIndex];
+            }
+        }else if (selectedType == JXCategoryCellSelectedTypeScroll) {
+            if (self.delegateFlags.didScrollSelectedItemAtIndexFlag) {
+                [self.delegate categoryView:self didScrollSelectedItemAtIndex:targetIndex];
+            }
+        }
+        if (self.delegateFlags.didSelectedItemAtIndexFlag) {
+            [self.delegate categoryView:self didSelectedItemAtIndex:targetIndex];
+        }
+        self.scrollingTargetIndex = -1;
+        return NO;
+    }
+
+    //通知子类刷新当前选中的和将要选中的cellModel
+    JXCategoryBaseCellModel *lastCellModel = self.dataSource[self.selectedIndex];
+    lastCellModel.selectedType = selectedType;
+    JXCategoryBaseCellModel *selectedCellModel = self.dataSource[targetIndex];
+    selectedCellModel.selectedType = selectedType;
+    [self refreshSelectedCellModel:selectedCellModel unselectedCellModel:lastCellModel];
+
+    //刷新当前选中的和将要选中的cell
+    JXCategoryBaseCell *lastCell = (JXCategoryBaseCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:self.selectedIndex inSection:0]];
+    [lastCell reloadData:lastCellModel];
+    JXCategoryBaseCell *selectedCell = (JXCategoryBaseCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:targetIndex inSection:0]];
+    [selectedCell reloadData:selectedCellModel];
+
+    if (self.scrollingTargetIndex != -1 && self.scrollingTargetIndex != targetIndex) {
+        JXCategoryBaseCellModel *scrollingTargetCellModel = self.dataSource[self.scrollingTargetIndex];
+        scrollingTargetCellModel.selected = NO;
+        scrollingTargetCellModel.selectedType = selectedType;
+        [self refreshSelectedCellModel:selectedCellModel unselectedCellModel:scrollingTargetCellModel];
+        JXCategoryBaseCell *scrollingTargetCell = (JXCategoryBaseCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:self.scrollingTargetIndex inSection:0]];
+        [scrollingTargetCell reloadData:scrollingTargetCellModel];
+    }
+
+    if (self.isCellWidthZoomEnabled) {
+        [self.collectionView.collectionViewLayout invalidateLayout];
+        //延时为了解决cellwidth变化,点击最后几个cell,scrollToItem会出现位置偏移bu。需要等cellWidth动画渐变结束后再滚动到index的cell位置。
+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.selectedAnimationDuration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+            [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:targetIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
+        });
+    } else {
+        [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:targetIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
+    }
+
+    if (selectedType == JXCategoryCellSelectedTypeClick ||
+        selectedType == JXCategoryCellSelectedTypeCode) {
+        [self.contentScrollView setContentOffset:CGPointMake(targetIndex*self.contentScrollView.bounds.size.width, 0) animated:self.isContentScrollViewClickTransitionAnimationEnabled];
+    }
+
+    self.selectedIndex = targetIndex;
+    if (selectedType == JXCategoryCellSelectedTypeCode) {
+        [self.listContainer didClickSelectedItemAtIndex:targetIndex];
+    } else if (selectedType == JXCategoryCellSelectedTypeClick) {
+        [self.listContainer didClickSelectedItemAtIndex:targetIndex];
+        if (self.delegateFlags.didClickSelectedItemAtIndexFlag) {
+            [self.delegate categoryView:self didClickSelectedItemAtIndex:targetIndex];
+        }
+    } else if(selectedType == JXCategoryCellSelectedTypeScroll) {
+        if (self.delegateFlags.didScrollSelectedItemAtIndexFlag) {
+            [self.delegate categoryView:self didScrollSelectedItemAtIndex:targetIndex];
+        }
+    }
+    if (self.delegateFlags.didSelectedItemAtIndexFlag) {
+        [self.delegate categoryView:self didSelectedItemAtIndex:targetIndex];
+    }
+    self.scrollingTargetIndex = -1;
+
+    return YES;
+}
+
+
+- (void)refreshSelectedCellModel:(JXCategoryBaseCellModel *)selectedCellModel unselectedCellModel:(JXCategoryBaseCellModel *)unselectedCellModel {
+    selectedCellModel.selected = YES;
+    unselectedCellModel.selected = NO;
+
+    if (self.isCellWidthZoomEnabled) {
+        if (selectedCellModel.selectedType == JXCategoryCellSelectedTypeCode ||
+            selectedCellModel.selectedType == JXCategoryCellSelectedTypeClick) {
+            self.animator = [[JXCategoryViewAnimator alloc] init];
+            self.animator.duration = self.selectedAnimationDuration;
+            __weak typeof(self) weakSelf = self;
+            self.animator.progressCallback = ^(CGFloat percent) {
+                selectedCellModel.transitionAnimating = YES;
+                unselectedCellModel.transitionAnimating = YES;
+                selectedCellModel.cellWidthCurrentZoomScale = [JXCategoryFactory interpolationFrom:selectedCellModel.cellWidthNormalZoomScale to:selectedCellModel.cellWidthSelectedZoomScale percent:percent];
+                selectedCellModel.cellWidth = [weakSelf getCellWidthAtIndex:selectedCellModel.index] * selectedCellModel.cellWidthCurrentZoomScale;
+                unselectedCellModel.cellWidthCurrentZoomScale = [JXCategoryFactory interpolationFrom:unselectedCellModel.cellWidthSelectedZoomScale to:unselectedCellModel.cellWidthNormalZoomScale percent:percent];
+                unselectedCellModel.cellWidth = [weakSelf getCellWidthAtIndex:unselectedCellModel.index] * unselectedCellModel.cellWidthCurrentZoomScale;
+                [weakSelf.collectionView.collectionViewLayout invalidateLayout];
+            };
+            self.animator.completeCallback = ^{
+                selectedCellModel.transitionAnimating = NO;
+                unselectedCellModel.transitionAnimating = NO;
+            };
+            [self.animator start];
+        } else {
+            selectedCellModel.cellWidthCurrentZoomScale = selectedCellModel.cellWidthSelectedZoomScale;
+            selectedCellModel.cellWidth = [self getCellWidthAtIndex:selectedCellModel.index] * selectedCellModel.cellWidthCurrentZoomScale;
+            unselectedCellModel.cellWidthCurrentZoomScale = unselectedCellModel.cellWidthNormalZoomScale;
+            unselectedCellModel.cellWidth = [self getCellWidthAtIndex:unselectedCellModel.index] * unselectedCellModel.cellWidthCurrentZoomScale;
+        }
+    }
+}
+
+- (void)contentOffsetOfContentScrollViewDidChanged:(CGPoint)contentOffset {
+    if (self.dataSource.count == 0) {
+        return;
+    }
+    CGFloat ratio = contentOffset.x/self.contentScrollView.bounds.size.width;
+    if (ratio > self.dataSource.count - 1 || ratio < 0) {
+        //超过了边界,不需要处理
+        return;
+    }
+    if (contentOffset.x == 0 && self.selectedIndex == 0 && self.lastContentViewContentOffset.x == 0) {
+        //滚动到了最左边,且已经选中了第一个,且之前的contentOffset.x为0
+        return;
+    }
+    CGFloat maxContentOffsetX = self.contentScrollView.contentSize.width - self.contentScrollView.bounds.size.width;
+    if (contentOffset.x == maxContentOffsetX && self.selectedIndex == self.dataSource.count - 1 && self.lastContentViewContentOffset.x == maxContentOffsetX) {
+        //滚动到了最右边,且已经选中了最后一个,且之前的contentOffset.x为maxContentOffsetX
+        return;
+    }
+    ratio = MAX(0, MIN(self.dataSource.count - 1, ratio));
+    NSInteger baseIndex = floorf(ratio);
+    CGFloat remainderRatio = ratio - baseIndex;
+
+    if (remainderRatio == 0) {
+        //快速滑动翻页,用户一直在拖拽contentScrollView,需要更新选中状态
+        //滑动一小段距离,然后放开回到原位,contentOffset同样的值会回调多次。例如在index为1的情况,滑动放开回到原位,contentOffset会多次回调CGPoint(width, 0)
+        if (!(self.lastContentViewContentOffset.x == contentOffset.x && self.selectedIndex == baseIndex)) {
+            [self scrollSelectItemAtIndex:baseIndex];
+        }
+    } else {
+        self.needReloadByBecomeActive = YES;
+        if (self.animator.isExecuting) {
+            [self.animator invalid];
+            //需要重置之前animator.progessCallback为处理完的状态
+            for (JXCategoryBaseCellModel *model in self.dataSource) {
+                if (model.isSelected) {
+                    model.cellWidthCurrentZoomScale = model.cellWidthSelectedZoomScale;
+                    model.cellWidth = [self getCellWidthAtIndex:model.index] * model.cellWidthCurrentZoomScale;
+                }else {
+                    model.cellWidthCurrentZoomScale = model.cellWidthNormalZoomScale;
+                    model.cellWidth = [self getCellWidthAtIndex:model.index] * model.cellWidthCurrentZoomScale;
+                }
+            }
+        }
+        //快速滑动翻页,当remainderRatio没有变成0,但是已经翻页了,需要通过下面的判断,触发选中
+        if (fabs(ratio - self.selectedIndex) > 1) {
+            NSInteger targetIndex = baseIndex;
+            if (ratio < self.selectedIndex) {
+                targetIndex = baseIndex + 1;
+            }
+            [self scrollSelectItemAtIndex:targetIndex];
+        }
+
+        if (self.selectedIndex == baseIndex) {
+            self.scrollingTargetIndex = baseIndex + 1;
+        } else {
+            self.scrollingTargetIndex = baseIndex;
+        }
+
+        if (self.isCellWidthZoomEnabled && self.isCellWidthZoomScrollGradientEnabled) {
+            JXCategoryBaseCellModel *leftCellModel = (JXCategoryBaseCellModel *)self.dataSource[baseIndex];
+            JXCategoryBaseCellModel *rightCellModel = (JXCategoryBaseCellModel *)self.dataSource[baseIndex + 1];
+            leftCellModel.cellWidthCurrentZoomScale = [JXCategoryFactory interpolationFrom:leftCellModel.cellWidthSelectedZoomScale to:leftCellModel.cellWidthNormalZoomScale percent:remainderRatio];
+            leftCellModel.cellWidth = [self getCellWidthAtIndex:leftCellModel.index] * leftCellModel.cellWidthCurrentZoomScale;
+            rightCellModel.cellWidthCurrentZoomScale = [JXCategoryFactory interpolationFrom:rightCellModel.cellWidthNormalZoomScale to:rightCellModel.cellWidthSelectedZoomScale percent:remainderRatio];
+            rightCellModel.cellWidth = [self getCellWidthAtIndex:rightCellModel.index] * rightCellModel.cellWidthCurrentZoomScale;
+            [self.collectionView.collectionViewLayout invalidateLayout];
+        }
+
+        if (self.delegateFlags.scrollingFromLeftIndexToRightIndexFlag) {
+            [self.delegate categoryView:self scrollingFromLeftIndex:baseIndex toRightIndex:baseIndex + 1 ratio:remainderRatio];
+        }
+    }
+}
+
+- (CGFloat)preferredCellWidthAtIndex:(NSInteger)index {
+    return 0;
+}
+
+- (Class)preferredCellClass {
+    return JXCategoryBaseCell.class;
+}
+
+- (void)refreshCellModel:(JXCategoryBaseCellModel *)cellModel index:(NSInteger)index {
+
+}
+
+@end

+ 24 - 0
MiMoLive/Pods/JXCategoryView/Sources/Common/JXCategoryCollectionView.h

@@ -0,0 +1,24 @@
+//
+//  JXCategoryCollectionView.h
+//  UI系列测试
+//
+//  Created by jiaxin on 2018/3/21.
+//  Copyright © 2018年 jiaxin. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "JXCategoryIndicatorProtocol.h"
+@class JXCategoryCollectionView;
+
+@protocol JXCategoryCollectionViewGestureDelegate <NSObject>
+@optional
+- (BOOL)categoryCollectionView:(JXCategoryCollectionView *)collectionView gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;
+- (BOOL)categoryCollectionView:(JXCategoryCollectionView *)collectionView gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
+@end
+
+@interface JXCategoryCollectionView : UICollectionView
+
+@property (nonatomic, strong) NSArray <UIView<JXCategoryIndicatorProtocol> *> *indicators;
+@property (nonatomic, weak) id<JXCategoryCollectionViewGestureDelegate> gestureDelegate;
+
+@end

+ 52 - 0
MiMoLive/Pods/JXCategoryView/Sources/Common/JXCategoryCollectionView.m

@@ -0,0 +1,52 @@
+//
+//  JXCategoryCollectionView.m
+//  UI系列测试
+//
+//  Created by jiaxin on 2018/3/21.
+//  Copyright © 2018年 jiaxin. All rights reserved.
+//
+
+#import "JXCategoryCollectionView.h"
+
+@interface JXCategoryCollectionView ()<UIGestureRecognizerDelegate>
+@end
+
+@implementation JXCategoryCollectionView
+
+- (void)setIndicators:(NSArray<UIView<JXCategoryIndicatorProtocol> *> *)indicators {
+    for (UIView *indicator in _indicators) {
+        //先移除之前的indicator
+        [indicator removeFromSuperview];
+    }
+
+    _indicators = indicators;
+
+    for (UIView *indicator in indicators) {
+        [self addSubview:indicator];
+    }
+}
+
+- (void)layoutSubviews
+{
+    [super layoutSubviews];
+
+    for (UIView<JXCategoryIndicatorProtocol> *view in self.indicators) {
+        [self sendSubviewToBack:view];
+    }
+}
+
+- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
+    if (self.gestureDelegate && [self.gestureDelegate respondsToSelector:@selector(categoryCollectionView:gestureRecognizerShouldBegin:)]) {
+        return [self.gestureDelegate categoryCollectionView:self gestureRecognizerShouldBegin:gestureRecognizer];
+    }
+    return YES;
+}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
+    if (self.gestureDelegate && [self.gestureDelegate respondsToSelector:@selector(categoryCollectionView:gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]) {
+        return [self.gestureDelegate categoryCollectionView:self gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
+    }
+    return NO;
+}
+
+@end

+ 18 - 0
MiMoLive/Pods/JXCategoryView/Sources/Common/JXCategoryFactory.h

@@ -0,0 +1,18 @@
+//
+//  JXCategoryFactory.h
+//  JXCategoryView
+//
+//  Created by jiaxin on 2018/8/17.
+//  Copyright © 2018年 jiaxin. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+@interface JXCategoryFactory : NSObject
+
++ (CGFloat)interpolationFrom:(CGFloat)from to:(CGFloat)to percent:(CGFloat)percent;
+
++ (UIColor *)interpolationColorFrom:(UIColor *)fromColor to:(UIColor *)toColor percent:(CGFloat)percent;
+
+@end

+ 29 - 0
MiMoLive/Pods/JXCategoryView/Sources/Common/JXCategoryFactory.m

@@ -0,0 +1,29 @@
+//
+//  JXCategoryFactory.m
+//  JXCategoryView
+//
+//  Created by jiaxin on 2018/8/17.
+//  Copyright © 2018年 jiaxin. All rights reserved.
+//
+
+#import "JXCategoryFactory.h"
+#import "UIColor+JXAdd.h"
+
+@implementation JXCategoryFactory
+
++ (CGFloat)interpolationFrom:(CGFloat)from to:(CGFloat)to percent:(CGFloat)percent
+{
+    percent = MAX(0, MIN(1, percent));
+    return from + (to - from)*percent;
+}
+
++ (UIColor *)interpolationColorFrom:(UIColor *)fromColor to:(UIColor *)toColor percent:(CGFloat)percent
+{
+    CGFloat red = [self interpolationFrom:fromColor.jx_red to:toColor.jx_red percent:percent];
+    CGFloat green = [self interpolationFrom:fromColor.jx_green to:toColor.jx_green percent:percent];
+    CGFloat blue = [self interpolationFrom:fromColor.jx_blue to:toColor.jx_blue percent:percent];
+    CGFloat alpha = [self interpolationFrom:fromColor.jx_alpha to:toColor.jx_alpha percent:percent];
+    return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
+}
+
+@end

+ 31 - 0
MiMoLive/Pods/JXCategoryView/Sources/Common/JXCategoryIndicatorParamsModel.h

@@ -0,0 +1,31 @@
+//
+//  JXCategoryIndicatorParamsModel.h
+//  JXCategoryView
+//
+//  Created by jiaxin on 2018/12/13.
+//  Copyright © 2018 jiaxin. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+#import "JXCategoryViewDefines.h"
+
+/**
+ 指示器不同情况处理时传递的数据模型,不同情况会对不同的属性赋值,根据不同情况的 api 说明确认。
+ 
+ FAQ: 为什么会通过 model 传递数据?
+ 因为指示器处理逻辑以后会扩展不同的使用场景,会新增参数,如果不通过 model 传递,就会在 api 新增参数,一旦修改 api 改的地方就特别多了,而且会影响到之前自定义实现的开发者。
+ */
+@interface JXCategoryIndicatorParamsModel : NSObject
+
+@property (nonatomic, assign) NSInteger selectedIndex;      // 当前选中的 index
+@property (nonatomic, assign) CGRect selectedCellFrame;     // 当前选中的 cellFrame
+@property (nonatomic, assign) NSInteger leftIndex;          // 正在过渡中的两个 cell,相对位置在左边的 cell 的 index
+@property (nonatomic, assign) CGRect leftCellFrame;         // 正在过渡中的两个 cell,相对位置在左边的 cell 的 frame
+@property (nonatomic, assign) NSInteger rightIndex;         // 正在过渡中的两个 cell,相对位置在右边的 cell 的 index
+@property (nonatomic, assign) CGRect rightCellFrame;        // 正在过渡中的两个 cell,相对位置在右边的 cell 的 frame
+@property (nonatomic, assign) CGFloat percent;              // 正在过渡中的两个 cell,从左到右的百分比
+@property (nonatomic, assign) NSInteger lastSelectedIndex;  // 之前选中的 index
+@property (nonatomic, assign) JXCategoryCellSelectedType selectedType;  //cell 被选中类型
+
+@end

+ 13 - 0
MiMoLive/Pods/JXCategoryView/Sources/Common/JXCategoryIndicatorParamsModel.m

@@ -0,0 +1,13 @@
+//
+//  JXCategoryIndicatorParamsModel.m
+//  JXCategoryView
+//
+//  Created by jiaxin on 2018/12/13.
+//  Copyright © 2018 jiaxin. All rights reserved.
+//
+
+#import "JXCategoryIndicatorParamsModel.h"
+
+@implementation JXCategoryIndicatorParamsModel
+
+@end

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