Browse Source

1.升级IM SDK
2.单聊输入中......

Steven 1 year ago
parent
commit
d29a54ca28
100 changed files with 1173 additions and 419 deletions
  1. 0 8
      KulexiuForTeacher/KulexiuForTeacher.xcodeproj/project.pbxproj
  2. 1 1
      KulexiuForTeacher/KulexiuForTeacher/Common/Tools/GifRefresh/KSNewGifRefreshFooter.m
  3. 16 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Chat/Controller/KSChatConversationViewController.m
  4. 120 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Chat/Controller/TXCustom/KSTXC2CChatViewController.m
  5. 1 1
      KulexiuForTeacher/KulexiuForTeacher/Module/Chat/Controller/TXCustom/KSTXGroupChatViewController.m
  6. 3 3
      KulexiuForTeacher/Podfile
  7. 141 145
      KulexiuForTeacher/Podfile.lock
  8. 1 0
      KulexiuForTeacher/Pods/Headers/Private/MJRefresh/MJRefreshNormalTrailer.h
  9. 1 0
      KulexiuForTeacher/Pods/Headers/Private/MJRefresh/MJRefreshStateTrailer.h
  10. 1 0
      KulexiuForTeacher/Pods/Headers/Private/MJRefresh/MJRefreshTrailer.h
  11. 1 0
      KulexiuForTeacher/Pods/Headers/Private/MJRefresh/UICollectionViewLayout+MJRefresh.h
  12. 1 0
      KulexiuForTeacher/Pods/Headers/Private/SDWebImage/UIView+WebCacheState.h
  13. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TIMCommonMediator.h
  14. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TIMRTLUtil.h
  15. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TUIEmojiMeditorProtocol.h
  16. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TUIRelationUserModel.h
  17. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TUISecurityStrikeView.h
  18. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TUITagsCell.h
  19. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TUITagsModel.h
  20. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TUITagsView.h
  21. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/EMVoiceConverter.h
  22. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIChatContextEmojiDetailController.h
  23. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIChatPopContextExtionView.h
  24. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIChatPopContextRecentView.h
  25. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIChatPopEmojiView.h
  26. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIChatPopMenuDefine.h
  27. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIChatPopRecentView.h
  28. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIEmojiCell.h
  29. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIEmojiCellData.h
  30. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIEmojiConfig.h
  31. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIEmojiMeditorProtocolProvider.h
  32. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIFaceSegementScrollView.h
  33. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIFaceVerticalView.h
  34. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIGroupPinCell.h
  35. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIGroupPinPageViewController.h
  36. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/amrFileCodec.h
  37. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/dec_if.h
  38. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/if_rom.h
  39. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/interf_dec.h
  40. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TUIChat/interf_enc.h
  41. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TUICore/OfflinePushExtBusinessInfo.h
  42. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TUICore/OfflinePushExtConfigInfo.h
  43. 1 0
      KulexiuForTeacher/Pods/Headers/Private/TUICore/OfflinePushExtInfo.h
  44. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TUIGroup/TUIAddCell.h
  45. 0 1
      KulexiuForTeacher/Pods/Headers/Private/TUIGroup/TUIAddCellData.h
  46. 1 0
      KulexiuForTeacher/Pods/Headers/Public/MJRefresh/MJRefreshNormalTrailer.h
  47. 1 0
      KulexiuForTeacher/Pods/Headers/Public/MJRefresh/MJRefreshStateTrailer.h
  48. 1 0
      KulexiuForTeacher/Pods/Headers/Public/MJRefresh/MJRefreshTrailer.h
  49. 1 0
      KulexiuForTeacher/Pods/Headers/Public/MJRefresh/UICollectionViewLayout+MJRefresh.h
  50. 1 0
      KulexiuForTeacher/Pods/Headers/Public/SDWebImage/UIView+WebCacheState.h
  51. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TIMCommonMediator.h
  52. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TIMRTLUtil.h
  53. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TUIEmojiMeditorProtocol.h
  54. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TUIRelationUserModel.h
  55. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TUISecurityStrikeView.h
  56. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TUITagsCell.h
  57. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TUITagsModel.h
  58. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TUITagsView.h
  59. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/EMVoiceConverter.h
  60. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIChatContextEmojiDetailController.h
  61. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIChatPopContextExtionView.h
  62. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIChatPopContextRecentView.h
  63. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIChatPopEmojiView.h
  64. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIChatPopMenuDefine.h
  65. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIChatPopRecentView.h
  66. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIEmojiCell.h
  67. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIEmojiCellData.h
  68. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIEmojiConfig.h
  69. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIEmojiMeditorProtocolProvider.h
  70. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIFaceSegementScrollView.h
  71. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIFaceVerticalView.h
  72. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIGroupPinCell.h
  73. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIGroupPinPageViewController.h
  74. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/amrFileCodec.h
  75. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/dec_if.h
  76. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/if_rom.h
  77. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/interf_dec.h
  78. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TUIChat/interf_enc.h
  79. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TUICore/OfflinePushExtBusinessInfo.h
  80. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TUICore/OfflinePushExtConfigInfo.h
  81. 1 0
      KulexiuForTeacher/Pods/Headers/Public/TUICore/OfflinePushExtInfo.h
  82. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TUIGroup/TUIAddCell.h
  83. 0 1
      KulexiuForTeacher/Pods/Headers/Public/TUIGroup/TUIAddCellData.h
  84. 13 2
      KulexiuForTeacher/Pods/MJExtension/MJExtension/NSObject+MJCoding.h
  85. 6 1
      KulexiuForTeacher/Pods/MJExtension/MJExtension/NSObject+MJCoding.m
  86. 1 1
      KulexiuForTeacher/Pods/MJExtension/MJExtension/NSObject+MJKeyValue.m
  87. 14 0
      KulexiuForTeacher/Pods/MJExtension/MJExtension/PrivacyInfo.xcprivacy
  88. 135 80
      KulexiuForTeacher/Pods/MJExtension/README.md
  89. 14 3
      KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h
  90. 82 20
      KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.m
  91. 9 1
      KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h
  92. 19 7
      KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.m
  93. 61 17
      KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.h
  94. 83 32
      KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.m
  95. 10 3
      KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.h
  96. 10 19
      KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.m
  97. 14 4
      KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.h
  98. 169 37
      KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.m
  99. 30 0
      KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.h
  100. 179 0
      KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.m

+ 0 - 8
KulexiuForTeacher/KulexiuForTeacher.xcodeproj/project.pbxproj

@@ -8863,8 +8863,6 @@
 					"-l\"jcore-noidfa-ios-2.7.1\"",
 					"-l\"jpush-ios-4.3.0\"",
 					"-l\"lottie-ios\"",
-					"-l\"opencore-amrnb\"",
-					"-l\"opencore-amrwb\"",
 					"-l\"resolv\"",
 					"-l\"sqlite3\"",
 					"-l\"stdc++\"",
@@ -9043,8 +9041,6 @@
 					"-l\"jcore-noidfa-ios-2.7.1\"",
 					"-l\"jpush-ios-4.3.0\"",
 					"-l\"lottie-ios\"",
-					"-l\"opencore-amrnb\"",
-					"-l\"opencore-amrwb\"",
 					"-l\"resolv\"",
 					"-l\"sqlite3\"",
 					"-l\"stdc++\"",
@@ -9351,8 +9347,6 @@
 					"-l\"jcore-noidfa-ios-2.7.1\"",
 					"-l\"jpush-ios-4.3.0\"",
 					"-l\"lottie-ios\"",
-					"-l\"opencore-amrnb\"",
-					"-l\"opencore-amrwb\"",
 					"-l\"resolv\"",
 					"-l\"sqlite3\"",
 					"-l\"stdc++\"",
@@ -9626,8 +9620,6 @@
 					"-l\"jcore-noidfa-ios-2.7.1\"",
 					"-l\"jpush-ios-4.3.0\"",
 					"-l\"lottie-ios\"",
-					"-l\"opencore-amrnb\"",
-					"-l\"opencore-amrwb\"",
 					"-l\"resolv\"",
 					"-l\"sqlite3\"",
 					"-l\"stdc++\"",

+ 1 - 1
KulexiuForTeacher/KulexiuForTeacher/Common/Tools/GifRefresh/KSNewGifRefreshFooter.m

@@ -46,7 +46,7 @@
         self.gifView.contentMode = UIViewContentModeCenter;
     } else {
         self.gifView.contentMode = UIViewContentModeRight;
-        self.gifView.mj_w = self.mj_w * 0.5 - self.labelLeftInset - self.stateLabel.mj_textWith * 0.5;
+        self.gifView.mj_w = self.mj_w * 0.5 - self.labelLeftInset - self.stateLabel.mj_textWidth * 0.5;
     }
 }
 

+ 16 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Chat/Controller/KSChatConversationViewController.m

@@ -35,6 +35,22 @@
     self.vc.conversationData = self.conversation;
     [self addChildViewController:self.vc];
     [self.view addSubview:self.vc.view];
+    // 关注变化
+    MJWeakSelf;
+    [[RACObserve(self.vc.conversationData, title) distinctUntilChanged] subscribeNext:^(NSString *title) {
+        [weakSelf allocTitle:title];
+    }];
+
+    [[RACObserve(self.vc.conversationData, otherSideTyping) distinctUntilChanged] subscribeNext:^(id otherSideTyping) {
+      BOOL otherSideTypingFlag = [otherSideTyping boolValue];
+      if (!otherSideTypingFlag) {
+          [weakSelf.vc checkTitle:YES];
+      }
+      else {
+          NSString *typingText = [NSString stringWithFormat:@"%@...", TIMCommonLocalizableString(TUIKitTyping)];
+          [weakSelf allocTitle:typingText];
+      }
+    }];
 }
 
 - (void)viewWillAppear:(BOOL)animated {

+ 120 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Chat/Controller/TXCustom/KSTXC2CChatViewController.m

@@ -6,16 +6,136 @@
 //
 
 #import "KSTXC2CChatViewController.h"
+#import "TUIBaseChatViewController+ProtectedAPI.h"
+#import "TUIChatConfig.h"
+#import "TUICloudCustomDataTypeCenter.h"
+#import "TUILinkCellData.h"
+#import "TUIMessageController.h"
+#import "TUIMessageDataProvider.h"
+
+#define kC2CTypingTime 30.0
 
 @interface KSTXC2CChatViewController ()
 
+// 如果满足了一次sendTypingBaseCondation 则当前会话未退出前都使用 sendTypingBaseCondationInVC
+// If one sendTypingBaseCondation is satisfied, sendTypingBaseCondationInVC is used until the current session exits
+
+@property(nonatomic, assign) BOOL sendTypingBaseCondationInVC;
+
+
 @end
 
 @implementation KSTXC2CChatViewController
 
+- (void)dealloc {
+    self.sendTypingBaseCondationInVC = NO;
+}
 - (void)viewDidLoad {
     [super viewDidLoad];
     // Do any additional setup after loading the view.
+    self.sendTypingBaseCondationInVC = NO;
+}
+
+#pragma mark - Override Methods
+- (NSString *)forwardTitleWithMyName:(NSString *)nameStr {
+    return [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitRelayChatHistoryForSomebodyFormat), self.conversationData.title, nameStr];
+}
+
+- (void)inputController:(TUIInputController *)inputController didSelectMoreCell:(TUIInputMoreCell *)cell {
+    [super inputController:inputController didSelectMoreCell:cell];
+}
+
+- (void)inputControllerBeginTyping:(TUIInputController *)inputController {
+    [super inputControllerBeginTyping:inputController];
+
+    [self sendTypingMsgByStatus:YES];
+}
+
+- (void)inputControllerEndTyping:(TUIInputController *)inputController {
+    [super inputControllerEndTyping:inputController];
+
+    [self sendTypingMsgByStatus:NO];
+}
+
+- (BOOL)sendTypingBaseCondation {
+    if (self.sendTypingBaseCondationInVC) {
+        return YES;
+    }
+
+    if ([self.messageController isKindOfClass:TUIMessageController.class]) {
+        TUIMessageController *vc = (TUIMessageController *)self.messageController;
+        NSDictionary *messageFeatureDic = (id)[vc.C2CIncomingLastMsg parseCloudCustomData:messageFeature];
+
+        if (messageFeatureDic && [messageFeatureDic isKindOfClass:[NSDictionary class]] && [messageFeatureDic.allKeys containsObject:@"needTyping"] &&
+            [messageFeatureDic.allKeys containsObject:@"version"]) {
+            BOOL needTyping = NO;
+
+            BOOL versionControl = NO;
+
+            BOOL timeControl = NO;
+
+            if ([messageFeatureDic[@"needTyping"] intValue] == 1) {
+                needTyping = YES;
+            }
+
+            if ([messageFeatureDic[@"version"] intValue] == 1) {
+                versionControl = YES;
+            }
+
+            CFTimeInterval current = [NSDate.new timeIntervalSince1970];
+            long currentTimeFloor = floor(current);
+            long otherSideTimeFloor = floor([vc.C2CIncomingLastMsg.timestamp timeIntervalSince1970]);
+            long interval = currentTimeFloor - otherSideTimeFloor;
+            if (interval <= kC2CTypingTime) {
+                timeControl = YES;
+            }
+
+            if (needTyping && versionControl && timeControl) {
+                self.sendTypingBaseCondationInVC = YES;
+                return YES;
+            }
+        }
+    }
+    return NO;
+}
+- (void)sendTypingMsgByStatus:(BOOL)editing {
+    // switch control
+    if (![TUIChatConfig defaultConfig].enableTypingStatus) {
+        return;
+    }
+
+    if (![self sendTypingBaseCondation]) {
+        return;
+    }
+
+    NSError *error = nil;
+    NSDictionary *param = @{
+        BussinessID : BussinessID_Typing,
+        @"typingStatus" : editing ? @1 : @0,
+        @"version" : @1,
+        @"userAction" : @14,
+        @"actionParam" : editing ? @"EIMAMSG_InputStatus_Ing" : @"EIMAMSG_InputStatus_End",
+    };
+    NSData *data = [NSJSONSerialization dataWithJSONObject:param options:0 error:&error];
+
+    V2TIMMessage *msg = [TUIMessageDataProvider getCustomMessageWithJsonData:data];
+    TUISendMessageAppendParams *appendParams = [[TUISendMessageAppendParams alloc] init];
+    appendParams.isSendPushInfo = NO;
+    appendParams.isOnlineUserOnly = YES;
+    appendParams.priority = V2TIM_PRIORITY_DEFAULT;
+
+    [TUIMessageDataProvider sendMessage:msg
+        toConversation:self.conversationData
+        appendParams:appendParams
+        Progress:^(uint32_t progress) {
+
+        }
+        SuccBlock:^{
+          NSLog(@"success");
+        }
+        FailBlock:^(int code, NSString *desc) {
+          NSLog(@"Fail");
+        }];
 }
 
 /*

+ 1 - 1
KulexiuForTeacher/KulexiuForTeacher/Module/Chat/Controller/TXCustom/KSTXGroupChatViewController.m

@@ -489,7 +489,7 @@
     if (!atUserExist) {
         TUIUserModel *user = [[TUIUserModel alloc] init];
         user.userId = cell.messageData.identifier;
-        user.name = cell.messageData.name;
+        user.name = cell.messageData.senderName;
         [self.atUserList addObject:user];
         
         NSString *nameString = [NSString stringWithFormat:@"@%@ ", user.name];

+ 3 - 3
KulexiuForTeacher/Podfile

@@ -11,12 +11,12 @@ install! 'cocoapods', :disable_input_output_paths => true
   # Uncomment the next line if you're using Swift or would like to use dynamic frameworks
   #use_frameworks!
    
-  pod 'MJExtension', '~> 3.3.0'
+  pod 'MJExtension', '~> 3.4.1'
   pod 'AFNetworking', '~> 4.0'
   pod 'Masonry', '~> 1.1.0'
   pod 'MBProgressHUD', '~> 1.2.0'
-  pod 'SDWebImage', '~> 5.16.0'
-  pod 'MJRefresh', '~> 3.1.12'
+  pod 'SDWebImage', '~> 5.18.11'
+  pod 'MJRefresh', '~> 3.7.5'
   pod 'IQKeyboardManager'
   pod 'Reachability', '~> 3.2'
   pod 'JCore', '2.7.1-noidfa'

+ 141 - 145
KulexiuForTeacher/Podfile.lock

@@ -33,8 +33,8 @@ PODS:
   - lottie-ios (2.5.3)
   - Masonry (1.1.0)
   - MBProgressHUD (1.2.0)
-  - MJExtension (3.3.0)
-  - MJRefresh (3.1.17)
+  - MJExtension (3.4.2)
+  - MJRefresh (3.7.9)
   - NTLBridge (3.1.4)
   - QCloudCore/WithoutMTA (6.3.7)
   - QCloudCOSXML/Transfer (6.3.7):
@@ -42,191 +42,187 @@ PODS:
   - Reachability (3.2)
   - ReactiveObjC (3.1.1)
   - RSKImageCropper (3.0.2)
-  - SDWebImage (5.16.0):
-    - SDWebImage/Core (= 5.16.0)
-  - SDWebImage/Core (5.16.0)
+  - SDWebImage (5.18.12):
+    - SDWebImage/Core (= 5.18.12)
+  - SDWebImage/Core (5.18.12)
   - SocketRocket (0.6.0)
   - SSZipArchive (2.4.3)
-  - TIMCommon (7.4.4643):
-    - TIMCommon/BaseCell (= 7.4.4643)
-    - TIMCommon/BaseCellData (= 7.4.4643)
-    - TIMCommon/CommonModel (= 7.4.4643)
-    - TIMCommon/UI_Classic (= 7.4.4643)
-    - TIMCommon/UI_Minimalist (= 7.4.4643)
-  - TIMCommon/BaseCell (7.4.4643):
+  - TIMCommon (8.1.6116):
+    - TIMCommon/BaseCell (= 8.1.6116)
+    - TIMCommon/BaseCellData (= 8.1.6116)
+    - TIMCommon/CommonModel (= 8.1.6116)
+    - TIMCommon/UI_Classic (= 8.1.6116)
+    - TIMCommon/UI_Minimalist (= 8.1.6116)
+  - TIMCommon/BaseCell (8.1.6116):
     - TIMCommon/BaseCellData
-  - TIMCommon/BaseCellData (7.4.4643):
+  - TIMCommon/BaseCellData (8.1.6116):
     - TIMCommon/CommonModel
-  - TIMCommon/CommonModel (7.4.4643):
+  - TIMCommon/CommonModel (8.1.6116):
+    - Masonry
     - ReactiveObjC
     - SDWebImage
-    - TUICore (= 7.4.4643)
-    - TXIMSDK_Plus_iOS (= 7.4.4643)
-  - TIMCommon/UI_Classic (7.4.4643):
+    - TUICore
+    - TXIMSDK_Plus_iOS_XCFramework
+  - TIMCommon/UI_Classic (8.1.6116):
     - TIMCommon/BaseCell
-  - TIMCommon/UI_Minimalist (7.4.4643):
+  - TIMCommon/UI_Minimalist (8.1.6116):
     - TIMCommon/BaseCell
-  - TUIChat/BaseCell (7.4.4643):
+  - TUIChat/BaseCell (8.1.6116):
     - TUIChat/BaseCellData
-  - TUIChat/BaseCellData (7.4.4643):
-    - TUIChat/BaseCellData/Base (= 7.4.4643)
-    - TUIChat/BaseCellData/Chat (= 7.4.4643)
-    - TUIChat/BaseCellData/Custom (= 7.4.4643)
-    - TUIChat/BaseCellData/Emoji (= 7.4.4643)
-    - TUIChat/BaseCellData/Reply (= 7.4.4643)
-  - TUIChat/BaseCellData/Base (7.4.4643):
+  - TUIChat/BaseCellData (8.1.6116):
+    - TUIChat/BaseCellData/Base (= 8.1.6116)
+    - TUIChat/BaseCellData/Chat (= 8.1.6116)
+    - TUIChat/BaseCellData/Custom (= 8.1.6116)
+    - TUIChat/BaseCellData/Reply (= 8.1.6116)
+  - TUIChat/BaseCellData/Base (8.1.6116):
     - TUIChat/CommonModel
-  - TUIChat/BaseCellData/Chat (7.4.4643):
+  - TUIChat/BaseCellData/Chat (8.1.6116):
     - TUIChat/BaseCellData/Base
-  - TUIChat/BaseCellData/Custom (7.4.4643):
+  - TUIChat/BaseCellData/Custom (8.1.6116):
     - TUIChat/BaseCellData/Chat
-  - TUIChat/BaseCellData/Emoji (7.4.4643):
+  - TUIChat/BaseCellData/Reply (8.1.6116):
     - TUIChat/BaseCellData/Custom
-  - TUIChat/BaseCellData/Reply (7.4.4643):
-    - TUIChat/BaseCellData/Custom
-  - TUIChat/BaseDataProvider (7.4.4643):
-    - TUIChat/BaseDataProvider/Base (= 7.4.4643)
-    - TUIChat/BaseDataProvider/Impl (= 7.4.4643)
-  - TUIChat/BaseDataProvider/Base (7.4.4643):
+  - TUIChat/BaseDataProvider (8.1.6116):
+    - TUIChat/BaseDataProvider/Base (= 8.1.6116)
+    - TUIChat/BaseDataProvider/Impl (= 8.1.6116)
+  - TUIChat/BaseDataProvider/Base (8.1.6116):
     - TUIChat/BaseCellData
-  - TUIChat/BaseDataProvider/Impl (7.4.4643):
+  - TUIChat/BaseDataProvider/Impl (8.1.6116):
     - TUIChat/BaseCellData
     - TUIChat/BaseDataProvider/Base
-  - TUIChat/CommonModel (7.4.4643):
+  - TUIChat/CommonModel (8.1.6116):
+    - Masonry
     - ReactiveObjC
     - SDWebImage
-    - TIMCommon (= 7.4.4643)
-    - TUIChat/VoiceConvert
-    - TUICore (= 7.4.4643)
-    - TXIMSDK_Plus_iOS (= 7.4.4643)
-  - TUIChat/CommonUI (7.4.4643):
-    - TUIChat/CommonUI/Camera (= 7.4.4643)
-    - TUIChat/CommonUI/Pendency (= 7.4.4643)
-    - TUIChat/CommonUI/Pop (= 7.4.4643)
-  - TUIChat/CommonUI/Camera (7.4.4643):
+    - TIMCommon (~> 8.1.6116)
+    - TUICore
+    - TXIMSDK_Plus_iOS_XCFramework
+  - TUIChat/CommonUI (8.1.6116):
+    - TUIChat/CommonUI/Camera (= 8.1.6116)
+    - TUIChat/CommonUI/Pendency (= 8.1.6116)
+    - TUIChat/CommonUI/Pop (= 8.1.6116)
+  - TUIChat/CommonUI/Camera (8.1.6116):
     - TUIChat/BaseCell
     - TUIChat/BaseDataProvider
-  - TUIChat/CommonUI/Pendency (7.4.4643):
+  - TUIChat/CommonUI/Pendency (8.1.6116):
     - TUIChat/BaseCell
     - TUIChat/BaseDataProvider
-  - TUIChat/CommonUI/Pop (7.4.4643):
+  - TUIChat/CommonUI/Pop (8.1.6116):
     - TUIChat/BaseCell
     - TUIChat/BaseDataProvider
-  - TUIChat/UI_Classic (7.4.4643):
-    - TUIChat/UI_Classic/Cell (= 7.4.4643)
-    - TUIChat/UI_Classic/Chat (= 7.4.4643)
-    - TUIChat/UI_Classic/Header (= 7.4.4643)
-    - TUIChat/UI_Classic/Input (= 7.4.4643)
-    - TUIChat/UI_Classic/Service (= 7.4.4643)
-  - TUIChat/UI_Classic/Cell (7.4.4643):
-    - TUIChat/UI_Classic/Cell/Base (= 7.4.4643)
-    - TUIChat/UI_Classic/Cell/Chat (= 7.4.4643)
-    - TUIChat/UI_Classic/Cell/Custom (= 7.4.4643)
-    - TUIChat/UI_Classic/Cell/Reply (= 7.4.4643)
-  - TUIChat/UI_Classic/Cell/Base (7.4.4643):
+  - TUIChat/UI_Classic (8.1.6116):
+    - TUIChat/UI_Classic/Cell (= 8.1.6116)
+    - TUIChat/UI_Classic/Chat (= 8.1.6116)
+    - TUIChat/UI_Classic/Header (= 8.1.6116)
+    - TUIChat/UI_Classic/Input (= 8.1.6116)
+    - TUIChat/UI_Classic/Service (= 8.1.6116)
+  - TUIChat/UI_Classic/Cell (8.1.6116):
+    - TUIChat/UI_Classic/Cell/Base (= 8.1.6116)
+    - TUIChat/UI_Classic/Cell/Chat (= 8.1.6116)
+    - TUIChat/UI_Classic/Cell/Custom (= 8.1.6116)
+    - TUIChat/UI_Classic/Cell/Reply (= 8.1.6116)
+  - TUIChat/UI_Classic/Cell/Base (8.1.6116):
     - TUIChat/CommonUI
-  - TUIChat/UI_Classic/Cell/Chat (7.4.4643):
+  - TUIChat/UI_Classic/Cell/Chat (8.1.6116):
     - TUIChat/UI_Classic/Cell/Base
-  - TUIChat/UI_Classic/Cell/Custom (7.4.4643):
+  - TUIChat/UI_Classic/Cell/Custom (8.1.6116):
     - TUIChat/UI_Classic/Cell/Chat
-  - TUIChat/UI_Classic/Cell/Reply (7.4.4643):
+  - TUIChat/UI_Classic/Cell/Reply (8.1.6116):
     - TUIChat/UI_Classic/Cell/Custom
-  - TUIChat/UI_Classic/Chat (7.4.4643):
+  - TUIChat/UI_Classic/Chat (8.1.6116):
     - TUIChat/UI_Classic/Input
-  - TUIChat/UI_Classic/Header (7.4.4643):
+  - TUIChat/UI_Classic/Header (8.1.6116):
     - TUIChat/UI_Classic/Service
-  - TUIChat/UI_Classic/Input (7.4.4643):
+  - TUIChat/UI_Classic/Input (8.1.6116):
     - TUIChat/UI_Classic/Cell
-  - TUIChat/UI_Classic/Service (7.4.4643):
+  - TUIChat/UI_Classic/Service (8.1.6116):
     - TUIChat/UI_Classic/Chat
-  - TUIChat/VoiceConvert (7.4.4643)
-  - TUIConversation/BaseCell (7.4.4643):
-    - TUIConversation/BaseCell/CellData (= 7.4.4643)
-    - TUIConversation/BaseCell/CellUI (= 7.4.4643)
-  - TUIConversation/BaseCell/CellData (7.4.4643):
+  - TUIConversation/BaseCell (8.1.6116):
+    - TUIConversation/BaseCell/CellData (= 8.1.6116)
+    - TUIConversation/BaseCell/CellUI (= 8.1.6116)
+  - TUIConversation/BaseCell/CellData (8.1.6116):
     - TUIConversation/CommonModel
-  - TUIConversation/BaseCell/CellUI (7.4.4643):
+  - TUIConversation/BaseCell/CellUI (8.1.6116):
     - TUIConversation/BaseCell/CellData
-  - TUIConversation/BaseDataProvider (7.4.4643):
+  - TUIConversation/BaseDataProvider (8.1.6116):
     - TUIConversation/BaseCell
-  - TUIConversation/CommonModel (7.4.4643):
+  - TUIConversation/CommonModel (8.1.6116):
+    - Masonry
     - ReactiveObjC
-    - TIMCommon (= 7.4.4643)
-    - TUICore (= 7.4.4643)
-    - TXIMSDK_Plus_iOS (= 7.4.4643)
-  - TUIConversation/UI_Classic (7.4.4643):
-    - TUIConversation/UI_Classic/DataProvider (= 7.4.4643)
-    - TUIConversation/UI_Classic/Header (= 7.4.4643)
-    - TUIConversation/UI_Classic/Service (= 7.4.4643)
-    - TUIConversation/UI_Classic/UI (= 7.4.4643)
-  - TUIConversation/UI_Classic/DataProvider (7.4.4643):
+    - TIMCommon (~> 8.1.6116)
+    - TUICore
+    - TXIMSDK_Plus_iOS_XCFramework
+  - TUIConversation/UI_Classic (8.1.6116):
+    - TUIConversation/UI_Classic/DataProvider (= 8.1.6116)
+    - TUIConversation/UI_Classic/Header (= 8.1.6116)
+    - TUIConversation/UI_Classic/Service (= 8.1.6116)
+    - TUIConversation/UI_Classic/UI (= 8.1.6116)
+  - TUIConversation/UI_Classic/DataProvider (8.1.6116):
     - TUIConversation/BaseDataProvider
-  - TUIConversation/UI_Classic/Header (7.4.4643):
+  - TUIConversation/UI_Classic/Header (8.1.6116):
     - TUIConversation/UI_Classic/Service
-  - TUIConversation/UI_Classic/Service (7.4.4643):
+  - TUIConversation/UI_Classic/Service (8.1.6116):
     - TUIConversation/UI_Classic/UI
-  - TUIConversation/UI_Classic/UI (7.4.4643):
+  - TUIConversation/UI_Classic/UI (8.1.6116):
     - TUIConversation/UI_Classic/DataProvider
-  - TUICore (7.4.4643):
-    - ReactiveObjC
+  - TUICore (8.1.6116):
     - SDWebImage
-    - TUICore/ImSDK_Plus (= 7.4.4643)
-  - TUICore/Base (7.4.4643):
-    - ReactiveObjC
+    - TUICore/ImSDK_Plus (= 8.1.6116)
+  - TUICore/Base (8.1.6116):
     - SDWebImage
-  - TUICore/ImSDK_Plus (7.4.4643):
-    - ReactiveObjC
+  - TUICore/ImSDK_Plus (8.1.6116):
     - SDWebImage
     - TUICore/Base
-    - TXIMSDK_Plus_iOS (= 7.4.4643)
-  - TUIGroup/BaseCell (7.4.4643):
-    - TUIGroup/BaseCell/CellData (= 7.4.4643)
-    - TUIGroup/BaseCell/CellUI (= 7.4.4643)
-  - TUIGroup/BaseCell/CellData (7.4.4643):
+    - TXIMSDK_Plus_iOS_XCFramework
+  - TUIGroup/BaseCell (8.1.6116):
+    - TUIGroup/BaseCell/CellData (= 8.1.6116)
+    - TUIGroup/BaseCell/CellUI (= 8.1.6116)
+  - TUIGroup/BaseCell/CellData (8.1.6116):
     - TUIGroup/CommonModel
-  - TUIGroup/BaseCell/CellUI (7.4.4643):
+  - TUIGroup/BaseCell/CellUI (8.1.6116):
     - TUIGroup/BaseCell/CellData
-  - TUIGroup/BaseDataProvider (7.4.4643):
+  - TUIGroup/BaseDataProvider (8.1.6116):
     - TUIGroup/BaseCell
-  - TUIGroup/CommonModel (7.4.4643):
+  - TUIGroup/CommonModel (8.1.6116):
+    - Masonry
     - ReactiveObjC
-    - TIMCommon (= 7.4.4643)
-    - TUICore (= 7.4.4643)
-    - TXIMSDK_Plus_iOS (= 7.4.4643)
-  - TUIGroup/CommonUI (7.4.4643):
+    - TIMCommon (~> 8.1.6116)
+    - TUICore
+    - TXIMSDK_Plus_iOS_XCFramework
+  - TUIGroup/CommonUI (8.1.6116):
     - TUIGroup/BaseDataProvider
-  - TUIGroup/UI_Classic (7.4.4643):
-    - TUIGroup/UI_Classic/Header (= 7.4.4643)
-    - TUIGroup/UI_Classic/Service (= 7.4.4643)
-    - TUIGroup/UI_Classic/UI (= 7.4.4643)
-  - TUIGroup/UI_Classic/Header (7.4.4643):
+  - TUIGroup/UI_Classic (8.1.6116):
+    - TUIGroup/UI_Classic/Header (= 8.1.6116)
+    - TUIGroup/UI_Classic/Service (= 8.1.6116)
+    - TUIGroup/UI_Classic/UI (= 8.1.6116)
+  - TUIGroup/UI_Classic/Header (8.1.6116):
     - TUIGroup/UI_Classic/Service
-  - TUIGroup/UI_Classic/Service (7.4.4643):
+  - TUIGroup/UI_Classic/Service (8.1.6116):
     - TUIGroup/UI_Classic/UI
-  - TUIGroup/UI_Classic/UI (7.4.4643):
+  - TUIGroup/UI_Classic/UI (8.1.6116):
     - TUIGroup/CommonUI
-  - TUISearch/BaseCell (7.4.4643):
-    - TUISearch/BaseCell/CellData (= 7.4.4643)
-    - TUISearch/BaseCell/CellUI (= 7.4.4643)
-  - TUISearch/BaseCell/CellData (7.4.4643):
-    - TIMCommon (= 7.4.4643)
-    - TUICore (= 7.4.4643)
-    - TXIMSDK_Plus_iOS (= 7.4.4643)
-  - TUISearch/BaseCell/CellUI (7.4.4643):
+  - TUISearch/BaseCell (8.1.6116):
+    - TUISearch/BaseCell/CellData (= 8.1.6116)
+    - TUISearch/BaseCell/CellUI (= 8.1.6116)
+  - TUISearch/BaseCell/CellData (8.1.6116):
+    - TIMCommon (~> 8.1.6116)
+    - TUICore
+    - TXIMSDK_Plus_iOS_XCFramework
+  - TUISearch/BaseCell/CellUI (8.1.6116):
     - TUISearch/BaseCell/CellData
-  - TUISearch/BaseDataProvider (7.4.4643):
+  - TUISearch/BaseDataProvider (8.1.6116):
     - TUISearch/BaseCell
-  - TUISearch/UI_Classic (7.4.4643):
-    - TUISearch/UI_Classic/Header (= 7.4.4643)
-    - TUISearch/UI_Classic/Service (= 7.4.4643)
-    - TUISearch/UI_Classic/UI (= 7.4.4643)
-  - TUISearch/UI_Classic/Header (7.4.4643):
+  - TUISearch/UI_Classic (8.1.6116):
+    - TUISearch/UI_Classic/Header (= 8.1.6116)
+    - TUISearch/UI_Classic/Service (= 8.1.6116)
+    - TUISearch/UI_Classic/UI (= 8.1.6116)
+  - TUISearch/UI_Classic/Header (8.1.6116):
     - TUISearch/UI_Classic/Service
-  - TUISearch/UI_Classic/Service (7.4.4643):
+  - TUISearch/UI_Classic/Service (8.1.6116):
     - TUISearch/UI_Classic/UI
-  - TUISearch/UI_Classic/UI (7.4.4643):
+  - TUISearch/UI_Classic/UI (8.1.6116):
     - TUISearch/BaseDataProvider
-  - TXIMSDK_Plus_iOS (7.4.4643)
+  - TXIMSDK_Plus_iOS_XCFramework (8.1.6905)
   - TXLiteAVSDK_Professional (11.7.15304):
     - TXLiteAVSDK_Professional/Professional (= 11.7.15304)
   - TXLiteAVSDK_Professional/Professional (11.7.15304)
@@ -275,12 +271,12 @@ DEPENDENCIES:
   - lottie-ios (~> 2.5)
   - Masonry (~> 1.1.0)
   - MBProgressHUD (~> 1.2.0)
-  - MJExtension (~> 3.3.0)
-  - MJRefresh (~> 3.1.12)
+  - MJExtension (~> 3.4.1)
+  - MJRefresh (~> 3.7.5)
   - QCloudCOSXML/Transfer
   - Reachability (~> 3.2)
   - RSKImageCropper
-  - SDWebImage (~> 5.16.0)
+  - SDWebImage (~> 5.18.11)
   - SocketRocket
   - SSZipArchive
   - TUIChat/UI_Classic
@@ -326,7 +322,7 @@ SPEC REPOS:
     - TUICore
     - TUIGroup
     - TUISearch
-    - TXIMSDK_Plus_iOS
+    - TXIMSDK_Plus_iOS_XCFramework
     - TXLiteAVSDK_Professional
     - TYCyclePagerView
     - TZImagePickerController
@@ -349,30 +345,30 @@ SPEC CHECKSUMS:
   lottie-ios: a50d5c0160425cd4b01b852bb9578963e6d92d31
   Masonry: 678fab65091a9290e40e2832a55e7ab731aad201
   MBProgressHUD: 3ee5efcc380f6a79a7cc9b363dd669c5e1ae7406
-  MJExtension: 01704cca2b60a214c10761b6491eab74069d68a9
-  MJRefresh: ee5b68f639775462faba4db0fd243baf4d42c2cf
+  MJExtension: e97d164cb411aa9795cf576093a1fa208b4a8dd8
+  MJRefresh: ff9e531227924c84ce459338414550a05d2aea78
   NTLBridge: 49780dc966976d3221a0eb03c7368617c1987cb6
   QCloudCore: 1316c60dbc3c308d7d90d3406a4a735e43b03c42
   QCloudCOSXML: 2f4c36887311276014e7c9b70ea235ea29f975d8
   Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
   ReactiveObjC: 011caa393aa0383245f2dcf9bf02e86b80b36040
   RSKImageCropper: 1ac71e9a82e3f41eea3eedfff8eacb0d3821c9ec
-  SDWebImage: 2aea163b50bfcb569a2726b6a754c54a4506fcf6
+  SDWebImage: 2d6d229046fea284d62e36bfb8ebe8287dfc5b10
   SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
   SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
-  TIMCommon: 944f70d62dda70a2e048b125770efb32eae4bc3b
-  TUIChat: 92e4ea0472dad566da6e6c88a4676b3dfb9482da
-  TUIConversation: 9c79518b59fd7e213bd817d1dbbc179a6dc1bda1
-  TUICore: 5b6f4409de9af3bc18f08735808c907f18db0a6c
-  TUIGroup: 9b53d271c5556616058ef979762fccc621e7ff5e
-  TUISearch: 8f84a6a62b6fd97ae32a89a1d241593acb433d98
-  TXIMSDK_Plus_iOS: 4fe66054d3bc6953f21562c5b2bcce5526ba589f
+  TIMCommon: 910eee49ea7ad99a81c416888fe2db1efcc09a2e
+  TUIChat: 9d7ac7483d54314c5d3dec5f333a4060b1de010b
+  TUIConversation: 678cf6af792542cd24a21ba4d15c52eac69a34ba
+  TUICore: 23f2d58027b594b137d3a4e6242d9a0651620271
+  TUIGroup: fffb617fba5121e90f8fdb5e897df977007ddbb1
+  TUISearch: 9e3e521a6edd5580586dad316911ab689835d774
+  TXIMSDK_Plus_iOS_XCFramework: 545d980cbe116c5a60d2ef60f19e1b9097d58e61
   TXLiteAVSDK_Professional: 60add29dbe52bed7d828f1d68d88487d9f16e9d1
   TYCyclePagerView: 2b051dade0615c70784aa34f40c646feeddb7344
   TZImagePickerController: e9909edbadf7381140efc5b5c9f5bdbfd630f7d4
   Whiteboard: 4622f3866b7c35a9c757955619ba0a2b26d968f5
   YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30
 
-PODFILE CHECKSUM: 409015a7b6d294d1b0057c9e5290353dee49a410
+PODFILE CHECKSUM: 42bd0e153fd2fbac61fafae498f9d28f4a4c0645
 
 COCOAPODS: 1.15.2

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/MJRefresh/MJRefreshNormalTrailer.h

@@ -0,0 +1 @@
+../../../MJRefresh/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/MJRefresh/MJRefreshStateTrailer.h

@@ -0,0 +1 @@
+../../../MJRefresh/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/MJRefresh/MJRefreshTrailer.h

@@ -0,0 +1 @@
+../../../MJRefresh/MJRefresh/Base/MJRefreshTrailer.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/MJRefresh/UICollectionViewLayout+MJRefresh.h

@@ -0,0 +1 @@
+../../../MJRefresh/MJRefresh/UICollectionViewLayout+MJRefresh.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/SDWebImage/UIView+WebCacheState.h

@@ -0,0 +1 @@
+../../../SDWebImage/SDWebImage/Core/UIView+WebCacheState.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TIMCommonMediator.h

@@ -0,0 +1 @@
+../../../TIMCommon/TIMCommon/CommonModel/TIMCommonMediator.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TIMRTLUtil.h

@@ -0,0 +1 @@
+../../../TIMCommon/TIMCommon/CommonModel/TIMRTLUtil.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TUIEmojiMeditorProtocol.h

@@ -0,0 +1 @@
+../../../TIMCommon/TIMCommon/CommonModel/TUIEmojiMeditorProtocol.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TUIRelationUserModel.h

@@ -0,0 +1 @@
+../../../TIMCommon/TIMCommon/BaseCellData/TUIRelationUserModel.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TUISecurityStrikeView.h

@@ -0,0 +1 @@
+../../../TIMCommon/TIMCommon/BaseCell/TUISecurityStrikeView.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TUITagsCell.h

@@ -1 +0,0 @@
-../../../TIMCommon/TIMCommon/BaseCell/TUITagsCell.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TUITagsModel.h

@@ -1 +0,0 @@
-../../../TIMCommon/TIMCommon/BaseCellData/TUITagsModel.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TIMCommon/TUITagsView.h

@@ -1 +0,0 @@
-../../../TIMCommon/TIMCommon/BaseCell/TUITagsView.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TUIChat/EMVoiceConverter.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/VoiceConvert/EMVoiceConverter.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIChatContextEmojiDetailController.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/CommonUI/Pop/TUIChatContextEmojiDetailController.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIChatPopContextExtionView.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/CommonUI/Pop/TUIChatPopContextExtionView.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIChatPopContextRecentView.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/CommonUI/Pop/TUIChatPopContextRecentView.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIChatPopEmojiView.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/BaseCell/TUIChatPopEmojiView.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIChatPopMenuDefine.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/BaseCell/TUIChatPopMenuDefine.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIChatPopRecentView.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/BaseCell/TUIChatPopRecentView.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIEmojiCell.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/BaseCell/TUIEmojiCell.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIEmojiCellData.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/BaseCellData/Emoji/TUIEmojiCellData.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIEmojiConfig.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/CommonModel/TUIEmojiConfig.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIEmojiMeditorProtocolProvider.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/CommonModel/TUIEmojiMeditorProtocolProvider.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIFaceSegementScrollView.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/BaseCell/TUIFaceSegementScrollView.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIFaceVerticalView.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/BaseCell/TUIFaceVerticalView.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIGroupPinCell.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/CommonUI/Pendency/TUIGroupPinCell.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TUIChat/TUIGroupPinPageViewController.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/CommonUI/Pendency/TUIGroupPinPageViewController.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TUIChat/amrFileCodec.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/VoiceConvert/amrFileCodec.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TUIChat/dec_if.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/VoiceConvert/dec_if.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TUIChat/if_rom.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/VoiceConvert/if_rom.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TUIChat/interf_dec.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/VoiceConvert/interf_dec.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TUIChat/interf_enc.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/VoiceConvert/interf_enc.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TUICore/OfflinePushExtBusinessInfo.h

@@ -0,0 +1 @@
+../../../TUICore/TUICore/OfflinePushExtBusinessInfo.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TUICore/OfflinePushExtConfigInfo.h

@@ -0,0 +1 @@
+../../../TUICore/TUICore/OfflinePushExtConfigInfo.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Private/TUICore/OfflinePushExtInfo.h

@@ -0,0 +1 @@
+../../../TUICore/TUICore/OfflinePushExtInfo.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TUIGroup/TUIAddCell.h

@@ -1 +0,0 @@
-../../../TUIGroup/TUIGroup/BaseCell/CellUI/TUIAddCell.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Private/TUIGroup/TUIAddCellData.h

@@ -1 +0,0 @@
-../../../TUIGroup/TUIGroup/BaseCell/CellData/TUIAddCellData.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/MJRefresh/MJRefreshNormalTrailer.h

@@ -0,0 +1 @@
+../../../MJRefresh/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/MJRefresh/MJRefreshStateTrailer.h

@@ -0,0 +1 @@
+../../../MJRefresh/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/MJRefresh/MJRefreshTrailer.h

@@ -0,0 +1 @@
+../../../MJRefresh/MJRefresh/Base/MJRefreshTrailer.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/MJRefresh/UICollectionViewLayout+MJRefresh.h

@@ -0,0 +1 @@
+../../../MJRefresh/MJRefresh/UICollectionViewLayout+MJRefresh.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/SDWebImage/UIView+WebCacheState.h

@@ -0,0 +1 @@
+../../../SDWebImage/SDWebImage/Core/UIView+WebCacheState.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TIMCommonMediator.h

@@ -0,0 +1 @@
+../../../TIMCommon/TIMCommon/CommonModel/TIMCommonMediator.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TIMRTLUtil.h

@@ -0,0 +1 @@
+../../../TIMCommon/TIMCommon/CommonModel/TIMRTLUtil.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TUIEmojiMeditorProtocol.h

@@ -0,0 +1 @@
+../../../TIMCommon/TIMCommon/CommonModel/TUIEmojiMeditorProtocol.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TUIRelationUserModel.h

@@ -0,0 +1 @@
+../../../TIMCommon/TIMCommon/BaseCellData/TUIRelationUserModel.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TUISecurityStrikeView.h

@@ -0,0 +1 @@
+../../../TIMCommon/TIMCommon/BaseCell/TUISecurityStrikeView.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TUITagsCell.h

@@ -1 +0,0 @@
-../../../TIMCommon/TIMCommon/BaseCell/TUITagsCell.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TUITagsModel.h

@@ -1 +0,0 @@
-../../../TIMCommon/TIMCommon/BaseCellData/TUITagsModel.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TIMCommon/TUITagsView.h

@@ -1 +0,0 @@
-../../../TIMCommon/TIMCommon/BaseCell/TUITagsView.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TUIChat/EMVoiceConverter.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/VoiceConvert/EMVoiceConverter.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIChatContextEmojiDetailController.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/CommonUI/Pop/TUIChatContextEmojiDetailController.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIChatPopContextExtionView.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/CommonUI/Pop/TUIChatPopContextExtionView.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIChatPopContextRecentView.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/CommonUI/Pop/TUIChatPopContextRecentView.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIChatPopEmojiView.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/BaseCell/TUIChatPopEmojiView.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIChatPopMenuDefine.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/BaseCell/TUIChatPopMenuDefine.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIChatPopRecentView.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/BaseCell/TUIChatPopRecentView.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIEmojiCell.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/BaseCell/TUIEmojiCell.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIEmojiCellData.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/BaseCellData/Emoji/TUIEmojiCellData.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIEmojiConfig.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/CommonModel/TUIEmojiConfig.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIEmojiMeditorProtocolProvider.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/CommonModel/TUIEmojiMeditorProtocolProvider.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIFaceSegementScrollView.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/BaseCell/TUIFaceSegementScrollView.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIFaceVerticalView.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/BaseCell/TUIFaceVerticalView.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIGroupPinCell.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/CommonUI/Pendency/TUIGroupPinCell.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TUIChat/TUIGroupPinPageViewController.h

@@ -0,0 +1 @@
+../../../TUIChat/TUIChat/CommonUI/Pendency/TUIGroupPinPageViewController.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TUIChat/amrFileCodec.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/VoiceConvert/amrFileCodec.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TUIChat/dec_if.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/VoiceConvert/dec_if.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TUIChat/if_rom.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/VoiceConvert/if_rom.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TUIChat/interf_dec.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/VoiceConvert/interf_dec.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TUIChat/interf_enc.h

@@ -1 +0,0 @@
-../../../TUIChat/TUIChat/VoiceConvert/interf_enc.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TUICore/OfflinePushExtBusinessInfo.h

@@ -0,0 +1 @@
+../../../TUICore/TUICore/OfflinePushExtBusinessInfo.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TUICore/OfflinePushExtConfigInfo.h

@@ -0,0 +1 @@
+../../../TUICore/TUICore/OfflinePushExtConfigInfo.h

+ 1 - 0
KulexiuForTeacher/Pods/Headers/Public/TUICore/OfflinePushExtInfo.h

@@ -0,0 +1 @@
+../../../TUICore/TUICore/OfflinePushExtInfo.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TUIGroup/TUIAddCell.h

@@ -1 +0,0 @@
-../../../TUIGroup/TUIGroup/BaseCell/CellUI/TUIAddCell.h

+ 0 - 1
KulexiuForTeacher/Pods/Headers/Public/TUIGroup/TUIAddCellData.h

@@ -1 +0,0 @@
-../../../TUIGroup/TUIGroup/BaseCell/CellData/TUIAddCellData.h

+ 13 - 2
KulexiuForTeacher/Pods/MJExtension/MJExtension/NSObject+MJCoding.h

@@ -50,6 +50,17 @@ return self; \
 - (void)encodeWithCoder:(NSCoder *)encoder \
 { \
 [self mj_encode:encoder]; \
-}
+}\
+
+#define MJExtensionCodingImplementation MJCodingImplementation
+
+#define MJSecureCodingImplementation(CLASS, FLAG) \
+@interface CLASS (MJSecureCoding) <NSSecureCoding> \
+@end \
+@implementation CLASS (MJSecureCoding) \
+MJCodingImplementation \
++ (BOOL)supportsSecureCoding { \
+return FLAG; \
+} \
+@end \
 
-#define MJExtensionCodingImplementation MJCodingImplementation

+ 6 - 1
KulexiuForTeacher/Pods/MJExtension/MJExtension/NSObject+MJCoding.m

@@ -43,7 +43,12 @@
         if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return;
         if ([ignoredCodingPropertyNames containsObject:property.name]) return;
         
-        id value = [decoder decodeObjectForKey:property.name];
+        // fixed `-[NSKeyedUnarchiver validateAllowedClass:forKey:] allowed unarchiving safe plist type ''NSNumber'(This will be disallowed in the future.)` warning.
+        Class genericClass = [property objectClassInArrayForClass:property.srcClass];
+        // If genericClass exists, property.type.typeClass would be a collection type(Array, Set, Dictionary). This scenario([obj, nil, obj, nil]) would not happened.
+        NSSet *classes = [NSSet setWithObjects:NSNumber.class,
+                          property.type.typeClass, genericClass, nil];
+        id value = [decoder decodeObjectOfClasses:classes forKey:property.name];
         if (value == nil) { // 兼容以前的MJExtension版本
             value = [decoder decodeObjectForKey:[@"_" stringByAppendingString:property.name]];
         }

+ 1 - 1
KulexiuForTeacher/Pods/MJExtension/MJExtension/NSObject+MJKeyValue.m

@@ -255,7 +255,7 @@ static const char MJReferenceReplacedKeyWhenCreatingKeyValuesKey = '\0';
     MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], nil, [self class], @"keyValues参数不是一个字典");
     
     if ([self isSubclassOfClass:[NSManagedObject class]] && context) {
-        NSString *entityName = [NSStringFromClass(self) componentsSeparatedByString:@"."].lastObject;
+        NSString *entityName = [(NSManagedObject *)self entity].name;
         return [[NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context] mj_setKeyValues:keyValues context:context];
     }
     return [[[self alloc] init] mj_setKeyValues:keyValues];

+ 14 - 0
KulexiuForTeacher/Pods/MJExtension/MJExtension/PrivacyInfo.xcprivacy

@@ -0,0 +1,14 @@
+<?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>NSPrivacyTracking</key>
+	<false/>
+	<key>NSPrivacyTrackingDomains</key>
+	<array/>
+	<key>NSPrivacyAccessedAPITypes</key>
+	<array/>
+	<key>NSPrivacyCollectedDataTypes</key>
+	<array/>
+</dict>
+</plist>

+ 135 - 80
KulexiuForTeacher/Pods/MJExtension/README.md

@@ -1,61 +1,43 @@
 MJExtension
 ===
+[![SPM supported](https://img.shields.io/badge/SPM-supported-4BC51D.svg?style=flat)](https://github.com/apple/swift-package-manager)
 [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
-![podversion](https://img.shields.io/cocoapods/v/MJExtension.svg)
+[![podversion](https://img.shields.io/cocoapods/v/MJExtension.svg)](https://cocoapods.org/pods/MJExtension)
+![Platform](https://img.shields.io/cocoapods/p/MJExtension.svg?style=flat)
+
 - A fast, convenient and nonintrusive conversion framework between JSON and model.
 - 转换速度快、使用简单方便的字典转模型框架
 
 [📜✍🏻**Release Notes**: more details](https://github.com/CoderMJLee/MJExtension/releases)
 
-### ‼️ 纯Swift版的JSON与Model转换框架已经开源上架 ‼️
-
-- [KakaJSON](https://github.com/kakaopensource/KakaJSON)
-- [中文教程](https://www.cnblogs.com/mjios/p/11352776.html)
-- 如果你的项目是用Swift写的Model,墙裂推荐使用[KakaJSON](https://github.com/kakaopensource/KakaJSON)
-  - 已经对各种常用的数据场景进行了大量的单元测试
-  - 简单易用、功能丰富、转换快速
-
-
-
-### Use the Framework in Swift [关于在Swift中使用MJExtension] ‼️
-
-> Example: 
->
-> - [Model - MJTester.swift](MJExtensionTests/SwiftModel/MJTester.swift)
->
-> - [Usage - SwiftModelTests.swift](MJExtensionTests/SwiftModelTests.swift)
-
-#### ‼️ `@objc` attributes should be added to class and property for declaration of Objc accessibility [在 Swift4 之后, 请在属性前加 `@objc` 修饰. 以保证 Swift 的属性能够暴露给 Objc 使用. ]‼️
-#### ‼️ Use `NSNumber` instead of `Bool`, which is not bridged to `BOOL`. [请勿使用 `Bool` 类型, 因为在 Swift 中并没有桥接该类型, 不能显式的对应 `BOOL`, 请使用 `NSNumber` 替代] ‼️
-
-
-
 ## Contents
 
 * [Getting Started 【开始使用】](#Getting_Started)
 	* [Features 【能做什么】](#Features)
 	* [Installation 【安装】](#Installation)
 * [Examples 【示例】](#Examples)
-	* [JSON -> Model](#JSON_Model)
-	* [JSONString -> Model](#JSONString_Model)
-	* [Model contains model](#Model_contains_model)
-	* [Model contains model-array](#Model_contains_model_array)
-	* [Model name - JSON key mapping](#Model_name_JSON_key_mapping)
-	* [JSON array -> model array](#JSON_array_model_array)
-	* [Model -> JSON](#Model_JSON)
-	* [Model array -> JSON array](#Model_array_JSON_array)
-	* [Core Data](#Core_Data)
-	* [Coding](#Coding)
-	* [Camel -> underline](#Camel_underline)
-	* [NSString -> NSDate, nil -> @""](#NSString_NSDate)
-	* [NSDate -> NSString](#NSDate_NSString)
-	* [More use cases](#More_use_cases)
+  * [Usage in Swift](#usage_in_swift)
+  * [JSON -> Model](#JSON_Model)
+  * [JSONString -> Model](#JSONString_Model)
+  * [Model contains model](#Model_contains_model)
+  * [Model contains model-array](#Model_contains_model_array)
+  * [Model name - JSON key mapping](#Model_name_JSON_key_mapping)
+  * [JSON array -> model array](#JSON_array_model_array)
+  * [Model -> JSON](#Model_JSON)
+  * [Model array -> JSON array](#Model_array_JSON_array)
+  * [Core Data](#Core_Data)
+  * [Coding](#Coding)
+  * [Secure Coding](#SecureCoding)
+  * [Camel -> underline](#Camel_underline)
+  * [NSString -> NSDate, nil -> @""](#NSString_NSDate)
+  * [NSDate -> NSString](#NSDate_NSString)
+  * [More use cases](#More_use_cases)
 
 ---
 
-# <a id="Getting_Started"></a> Getting Started【开始使用】
+## <a id="Getting_Started"></a> Getting Started【开始使用】
 
-## <a id="Features"></a> Features【能做什么】
+### <a id="Features"></a> Features【能做什么】
 - MJExtension是一套字典和模型之间互相转换的超轻量级框架
 * `JSON` --> `Model`、`Core Data Model`
 * `JSONString` --> `Model`、`Core Data Model`
@@ -64,34 +46,66 @@ MJExtension
 * `JSONString` --> `Model Array`、`Core Data Model Array`
 * `Model Array`、`Core Data Model Array` --> `JSON Array`
 * Coding all properties of a model with only one line of code.
-    * 只需要一行代码,就能实现模型的所有属性进行Coding(归档和解档)
+    * 只需要一行代码,就能实现模型的所有属性进行Coding / SecureCoding(归档和解档)
 
-## <a id="Installation"></a> Installation【安装】
+### <a id="Installation"></a> Installation【安装】
 
-### From CocoaPods【使用CocoaPods】
+#### CocoaPods【使用CocoaPods】
 
 ```ruby
 pod 'MJExtension'
 ```
 
-### Manually【手动导入】
+#### Carthage
+
+```ruby
+github "CoderMJLee/MJExtension"
+```
+
+#### Swift Package Manager
+
+Released from [`3.4.0`](https://github.com/CoderMJLee/MJExtension/releases/)
+
+#### Manually【手动导入】
+
 - Drag all source files under folder `MJExtension` to your project.【将`MJExtension`文件夹中的所有源代码拽入项目中】
 - Import the main header file:`#import "MJExtension.h"`【导入主头文件:`#import "MJExtension.h"`】
 
-```objc
-MJExtension.h
-MJConst.h               MJConst.m
-MJFoundation.h          MJFoundation.m
-MJProperty.h            MJProperty.m
-MJType.h                MJType.m
-NSObject+MJCoding.h     NSObject+MJCoding.m
-NSObject+MJProperty.h   NSObject+MJProperty.m
-NSObject+MJKeyValue.h   NSObject+MJKeyValue.m
+## <a id="Examples"></a> Examples【示例】
+
+**Add `MJKeyValue` protocol to your model if needed【如果有需要, 请在模型中加入 `MJKeyValue` 协议】**
+
+### <a id="usage_in_swift"></a> Usage in Swift [关于在Swift中使用MJExtension] ‼️
+
+> Example: 
+>
+> - [Model - MJTester.swift](MJExtensionTests/SwiftModel/MJTester.swift)
+>
+> - [Usage - SwiftModelTests.swift](MJExtensionTests/SwiftModelTests.swift)
+
+```swift
+@objc(MJTester)
+@objcMembers
+class MJTester: NSObject {
+    // make sure to use `dynamic` attribute for basic type & must use as Non-Optional & must set initial value
+    dynamic var isSpecialAgent: Bool = false
+    dynamic var age: Int = 0
+    
+    var name: String?
+    var identifier: String?
+}
 ```
 
-# <a id="Examples"></a> Examples【示例】
+1.  `@objc` or `@objcMembers` attributes should be added to class or property for declaration of Objc accessibility [在 Swift4 之后, 请在属性前加 `@objc` 修饰或在类前增加 `@objcMembers`. 以保证 Swift 的属性能够暴露给 Objc 使用. ]
+2.  If you let `Bool` & `Int` as property type, make sure that using `dynamic` to attribute it. It must be `Non-Optional` type and assign `a default value`.[如果要使用 `Bool` 和 `Int` 等 Swfit 专用基本类型, 请使用 `dynamic` 关键字修饰, 类型为 `Non-Optional`, 並且给定初始值.]
 
-**Add `MJKeyValue` protocol to your model if needed【如果有需要, 请在模型中加入 `MJKeyValue` 协议】**
+> 纯Swift版的JSON与Model转换框架已经开源上架
+>
+> - [KakaJSON](https://github.com/kakaopensource/KakaJSON)
+> - [中文教程](https://www.cnblogs.com/mjios/p/11352776.html)
+> - 如果你的项目是用Swift写的Model,墙裂推荐使用[KakaJSON](https://github.com/kakaopensource/KakaJSON)
+>   - 已经对各种常用的数据场景进行了大量的单元测试
+>   - 简单易用、功能丰富、转换快速
 
 ### <a id="JSON_Model"></a> The most simple JSON -> Model【最简单的字典转模型】
 
@@ -448,58 +462,99 @@ NSLog(@"%@", dictArray);
 
 ### <a id="Core_Data"></a> Core Data
 
-```objc
-NSDictionary *dict = @{
-                         @"name" : @"Jack",
-                         @"icon" : @"lufy.png",
-                         @"age" : @20,
-                         @"height" : @1.55,
-                         @"money" : @"100.9",
-                         @"sex" : @(SexFemale),
-                         @"gay" : @"true"
-                     };
-
-// This demo just provide simple steps
-NSManagedObjectContext *context = nil;
-User *user = [User mj_objectWithKeyValues:dict context:context];
-
-[context save:nil];
+```swift
+func json2CoreDataObject() {
+    context.performAndWait {
+        let object = MJCoreDataTester.mj_object(withKeyValues: Values.testJSONObject, context: context)
+        // use the object
+    }
+}
+
+func coreDataObject2JSON() {
+    context.performAndWait {        
+        let dict = coreDataObject.mj_keyValues()
+        // use dict
+    }
+}
 ```
 
-### <a id="Coding"></a> Coding
+### <a id="Coding"></a> Coding (Archive & Unarchive methods are deprecated in iOS 12)
 
 ```objc
 #import "MJExtension.h"
 
-@implementation Bag
+@implementation MJBag
 // NSCoding Implementation
-MJExtensionCodingImplementation
+MJCodingImplementation
 @end
 
 /***********************************************/
 
 // what properties not to be coded
-[Bag mj_setupIgnoredCodingPropertyNames:^NSArray *{
+[MJBag mj_setupIgnoredCodingPropertyNames:^NSArray *{
     return @[@"name"];
 }];
-// Equals: Bag.m implements +mj_ignoredCodingPropertyNames method.
+// Equals: MJBag.m implements +mj_ignoredCodingPropertyNames method.
 
 // Create model
-Bag *bag = [[Bag alloc] init];
+MJBag *bag = [[MJBag alloc] init];
 bag.name = @"Red bag";
 bag.price = 200.8;
 
 NSString *file = [NSHomeDirectory() stringByAppendingPathComponent:@"Desktop/bag.data"];
-// Encoding
+// Encoding by archiving
 [NSKeyedArchiver archiveRootObject:bag toFile:file];
 
-// Decoding
-Bag *decodedBag = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
+// Decoding by unarchiving
+MJBag *decodedBag = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
 NSLog(@"name=%@, price=%f", decodedBag.name, decodedBag.price);
 // name=(null), price=200.800000
 ```
 
+### <a id="SecureCoding"></a> Secure Coding
+
+Using `MJSecureCodingImplementation(class, isSupport)` macro.
+
+```objc
+@import MJExtension;
+
+// NSSecureCoding Implementation
+MJSecureCodingImplementation(MJBag, YES)
+
+@implementation MJBag
+@end
+
+ /***********************************************/
+
+// what properties not to be coded
+[MJBag mj_setupIgnoredCodingPropertyNames:^NSArray *{
+    return @[@"name"];
+}];
+// Equals: MJBag.m implements +mj_ignoredCodingPropertyNames method.
+
+// Create model
+MJBag *bag = [[MJBag alloc] init];
+bag.name = @"Red bag";
+bag.price = 200.8;
+bag.isBig = YES;
+bag.weight = 200;
+
+NSString *file = [NSTemporaryDirectory() stringByAppendingPathComponent:@"bag.data"];
+
+NSError *error = nil;
+// Encoding by archiving
+NSData *data = [NSKeyedArchiver archivedDataWithRootObject:bag requiringSecureCoding:YES error:&error];
+[data writeToFile:file atomically:true];
+
+// Decoding by unarchiving
+NSData *readData = [NSFileManager.defaultManager contentsAtPath:file];
+error = nil;
+MJBag *decodedBag = [NSKeyedUnarchiver unarchivedObjectOfClass:MJBag.class fromData:readData error:&error];
+MJExtensionLog(@"name=%@, price=%f", decodedBag.name, decodedBag.price);
+```
+
 ### <a id="Camel_underline"></a> Camel -> underline【统一转换属性名(比如驼峰转下划线)】
+
 ```objc
 // Dog
 #import "MJExtension.h"

+ 14 - 3
KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h

@@ -1,12 +1,18 @@
 //
 //  MJRefreshAutoFooter.h
-//  MJRefreshExample
+//  MJRefresh
 //
 //  Created by MJ Lee on 15/4/24.
 //  Copyright (c) 2015年 小码哥. All rights reserved.
 //
 
+#if __has_include(<MJRefresh/MJRefreshFooter.h>)
+#import <MJRefresh/MJRefreshFooter.h>
+#else
 #import "MJRefreshFooter.h"
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
 
 @interface MJRefreshAutoFooter : MJRefreshFooter
 /** 是否自动刷新(默认为YES) */
@@ -18,6 +24,11 @@
 /** 当底部控件出现多少时就自动刷新(默认为1.0,也就是底部控件完全出现时,才会自动刷新) */
 @property (assign, nonatomic) CGFloat triggerAutomaticallyRefreshPercent;
 
-/** 是否每一次拖拽只发一次请求 */
-@property (assign, nonatomic, getter=isOnlyRefreshPerDrag) BOOL onlyRefreshPerDrag;
+/** 自动触发次数, 默认为 1, 仅在拖拽 ScrollView 时才生效,
+ 
+ 如果为 -1, 则为无限触发
+ */
+@property (nonatomic) NSInteger autoTriggerTimes;
 @end
+
+NS_ASSUME_NONNULL_END

+ 82 - 20
KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.m

@@ -1,16 +1,21 @@
 //
 //  MJRefreshAutoFooter.m
-//  MJRefreshExample
+//  MJRefresh
 //
 //  Created by MJ Lee on 15/4/24.
 //  Copyright (c) 2015年 小码哥. All rights reserved.
 //
 
 #import "MJRefreshAutoFooter.h"
+#import "NSBundle+MJRefresh.h"
+#import "UIView+MJExtension.h"
+#import "UIScrollView+MJExtension.h"
+#import "UIScrollView+MJRefresh.h"
 
 @interface MJRefreshAutoFooter()
 /** 一个新的拖拽 */
-@property (assign, nonatomic, getter=isOneNewPan) BOOL oneNewPan;
+@property (nonatomic) BOOL triggerByDrag;
+@property (nonatomic) NSInteger leftTriggerTimes;
 @end
 
 @implementation MJRefreshAutoFooter
@@ -26,7 +31,7 @@
         }
         
         // 设置位置
-        self.mj_y = _scrollView.mj_contentH;
+        self.mj_y = _scrollView.mj_contentH + self.ignoredScrollViewContentInsetBottom;
     } else { // 被移除了
         if (self.hidden == NO) {
             self.scrollView.mj_insetB -= self.mj_h;
@@ -56,16 +61,20 @@
     // 设置为默认状态
     self.automaticallyRefresh = YES;
     
-    // 默认是当offset达到条件就发送请求(可连续)
-    self.onlyRefreshPerDrag = NO;
+    self.autoTriggerTimes = 1;
 }
 
 - (void)scrollViewContentSizeDidChange:(NSDictionary *)change
 {
     [super scrollViewContentSizeDidChange:change];
     
+    CGSize size = [change[NSKeyValueChangeNewKey] CGSizeValue];
+    CGFloat contentHeight = size.height == 0 ? self.scrollView.mj_contentH : size.height;
     // 设置位置
-    self.mj_y = self.scrollView.mj_contentH;
+    CGFloat y = contentHeight + self.ignoredScrollViewContentInsetBottom;
+    if (self.mj_y != y) {
+        self.mj_y = y;
+    }
 }
 
 - (void)scrollViewContentOffsetDidChange:(NSDictionary *)change
@@ -82,6 +91,9 @@
             CGPoint new = [change[@"new"] CGPointValue];
             if (new.y <= old.y) return;
             
+            if (_scrollView.isDragging) {
+                self.triggerByDrag = YES;
+            }
             // 当底部刷新控件完全出现时,才刷新
             [self beginRefreshing];
         }
@@ -95,28 +107,45 @@
     if (self.state != MJRefreshStateIdle) return;
     
     UIGestureRecognizerState panState = _scrollView.panGestureRecognizer.state;
-    if (panState == UIGestureRecognizerStateEnded) {// 手松开
-        if (_scrollView.mj_insetT + _scrollView.mj_contentH <= _scrollView.mj_h) {  // 不够一个屏幕
-            if (_scrollView.mj_offsetY >= - _scrollView.mj_insetT) { // 向上拽
-                [self beginRefreshing];
-            }
-        } else { // 超出一个屏幕
-            if (_scrollView.mj_offsetY >= _scrollView.mj_contentH + _scrollView.mj_insetB - _scrollView.mj_h) {
-                [self beginRefreshing];
+    
+    switch (panState) {
+        // 手松开
+        case UIGestureRecognizerStateEnded: {
+            if (_scrollView.mj_insetT + _scrollView.mj_contentH <= _scrollView.mj_h) {  // 不够一个屏幕
+                if (_scrollView.mj_offsetY >= - _scrollView.mj_insetT) { // 向上拽
+                    self.triggerByDrag = YES;
+                    [self beginRefreshing];
+                }
+            } else { // 超出一个屏幕
+                if (_scrollView.mj_offsetY >= _scrollView.mj_contentH + _scrollView.mj_insetB - _scrollView.mj_h) {
+                    self.triggerByDrag = YES;
+                    [self beginRefreshing];
+                }
             }
         }
-    } else if (panState == UIGestureRecognizerStateBegan) {
-        self.oneNewPan = YES;
+            break;
+            
+        case UIGestureRecognizerStateBegan: {
+            [self resetTriggerTimes];
+        }
+            break;
+            
+        default:
+            break;
     }
 }
 
+- (BOOL)unlimitedTrigger {
+    return self.leftTriggerTimes == -1;
+}
+
 - (void)beginRefreshing
 {
-    if (!self.isOneNewPan && self.isOnlyRefreshPerDrag) return;
+    if (self.triggerByDrag && self.leftTriggerTimes <= 0 && !self.unlimitedTrigger) {
+        return;
+    }
     
     [super beginRefreshing];
-    
-    self.oneNewPan = NO;
 }
 
 - (void)setState:(MJRefreshState)state
@@ -126,7 +155,31 @@
     if (state == MJRefreshStateRefreshing) {
         [self executeRefreshingCallback];
     } else if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
+        if (self.triggerByDrag) {
+            if (!self.unlimitedTrigger) {
+                self.leftTriggerTimes -= 1;
+            }
+            self.triggerByDrag = NO;
+        }
+        
         if (MJRefreshStateRefreshing == oldState) {
+            if (self.scrollView.pagingEnabled) {
+                CGPoint offset = self.scrollView.contentOffset;
+                offset.y -= self.scrollView.mj_insetB;
+                [UIView animateWithDuration:self.slowAnimationDuration animations:^{
+                    self.scrollView.contentOffset = offset;
+                    
+                    if (self.endRefreshingAnimationBeginAction) {
+                        self.endRefreshingAnimationBeginAction();
+                    }
+                } completion:^(BOOL finished) {
+                    if (self.endRefreshingCompletionBlock) {
+                        self.endRefreshingCompletionBlock();
+                    }
+                }];
+                return;
+            }
+            
             if (self.endRefreshingCompletionBlock) {
                 self.endRefreshingCompletionBlock();
             }
@@ -134,6 +187,10 @@
     }
 }
 
+- (void)resetTriggerTimes {
+    self.leftTriggerTimes = self.autoTriggerTimes;
+}
+
 - (void)setHidden:(BOOL)hidden
 {
     BOOL lastHidden = self.isHidden;
@@ -148,7 +205,12 @@
         self.scrollView.mj_insetB += self.mj_h;
         
         // 设置位置
-        self.mj_y = _scrollView.mj_contentH;
+        self.mj_y = _scrollView.mj_contentH + self.ignoredScrollViewContentInsetBottom;
     }
 }
+
+- (void)setAutoTriggerTimes:(NSInteger)autoTriggerTimes {
+    _autoTriggerTimes = autoTriggerTimes;
+    self.leftTriggerTimes = autoTriggerTimes;
+}
 @end

+ 9 - 1
KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h

@@ -1,13 +1,21 @@
 //
 //  MJRefreshBackFooter.h
-//  MJRefreshExample
+//  MJRefresh
 //
 //  Created by MJ Lee on 15/4/24.
 //  Copyright (c) 2015年 小码哥. All rights reserved.
 //
 
+#if __has_include(<MJRefresh/MJRefreshFooter.h>)
+#import <MJRefresh/MJRefreshFooter.h>
+#else
 #import "MJRefreshFooter.h"
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
 
 @interface MJRefreshBackFooter : MJRefreshFooter
 
 @end
+
+NS_ASSUME_NONNULL_END

+ 19 - 7
KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.m

@@ -1,12 +1,16 @@
 //
 //  MJRefreshBackFooter.m
-//  MJRefreshExample
+//  MJRefresh
 //
 //  Created by MJ Lee on 15/4/24.
 //  Copyright (c) 2015年 小码哥. All rights reserved.
 //
 
 #import "MJRefreshBackFooter.h"
+#import "NSBundle+MJRefresh.h"
+#import "UIView+MJExtension.h"
+#import "UIScrollView+MJExtension.h"
+#import "UIScrollView+MJRefresh.h"
 
 @interface MJRefreshBackFooter()
 @property (assign, nonatomic) NSInteger lastRefreshCount;
@@ -72,12 +76,17 @@
 {
     [super scrollViewContentSizeDidChange:change];
     
+    CGSize size = [change[NSKeyValueChangeNewKey] CGSizeValue];
+    CGFloat contentHeight = size.height == 0 ? self.scrollView.mj_contentH : size.height;
     // 内容的高度
-    CGFloat contentHeight = self.scrollView.mj_contentH + self.ignoredScrollViewContentInsetBottom;
+    contentHeight += self.ignoredScrollViewContentInsetBottom;
     // 表格的高度
     CGFloat scrollHeight = self.scrollView.mj_h - self.scrollViewOriginalInset.top - self.scrollViewOriginalInset.bottom + self.ignoredScrollViewContentInsetBottom;
-    // 设置位置和尺寸
-    self.mj_y = MAX(contentHeight, scrollHeight);
+    // 设置位置
+    CGFloat y = MAX(contentHeight, scrollHeight);
+    if (self.mj_y != y) {
+        self.mj_y = y;
+    }
 }
 
 - (void)setState:(MJRefreshState)state
@@ -88,9 +97,12 @@
     if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
         // 刷新完毕
         if (MJRefreshStateRefreshing == oldState) {
-            [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
-                self.scrollView.mj_insetB -= self.lastBottomDelta;
+            [UIView animateWithDuration:self.slowAnimationDuration animations:^{
+                if (self.endRefreshingAnimationBeginAction) {
+                    self.endRefreshingAnimationBeginAction();
+                }
                 
+                self.scrollView.mj_insetB -= self.lastBottomDelta;
                 // 自动调整透明度
                 if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0;
             } completion:^(BOOL finished) {
@@ -111,7 +123,7 @@
         // 记录刷新前的数量
         self.lastRefreshCount = self.scrollView.mj_totalDataCount;
         
-        [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
+        [UIView animateWithDuration:self.fastAnimationDuration animations:^{
             CGFloat bottom = self.mj_h + self.scrollViewOriginalInset.bottom;
             CGFloat deltaH = [self heightForContentBreakView];
             if (deltaH < 0) { // 如果内容高度小于view的高度

+ 61 - 17
KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.h

@@ -1,18 +1,19 @@
 //  代码地址: https://github.com/CoderMJLee/MJRefresh
-//  代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
 //  MJRefreshComponent.h
-//  MJRefreshExample
+//  MJRefresh
 //
 //  Created by MJ Lee on 15/3/4.
 //  Copyright (c) 2015年 小码哥. All rights reserved.
 //  刷新控件的基类
 
 #import <UIKit/UIKit.h>
+#if __has_include(<MJRefresh/MJRefreshConst.h>)
+#import <MJRefresh/MJRefreshConst.h>
+#else
 #import "MJRefreshConst.h"
-#import "UIView+MJExtension.h"
-#import "UIScrollView+MJExtension.h"
-#import "UIScrollView+MJRefresh.h"
-#import "NSBundle+MJRefresh.h"
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
 
 /** 刷新控件的状态 */
 typedef NS_ENUM(NSInteger, MJRefreshState) {
@@ -29,11 +30,14 @@ typedef NS_ENUM(NSInteger, MJRefreshState) {
 };
 
 /** 进入刷新状态的回调 */
-typedef void (^MJRefreshComponentRefreshingBlock)(void);
+typedef void (^MJRefreshComponentRefreshingBlock)(void) MJRefreshDeprecated("first deprecated in 3.3.0 - Use `MJRefreshComponentAction` instead");
 /** 开始刷新后的回调(进入刷新状态后的回调) */
-typedef void (^MJRefreshComponentbeginRefreshingCompletionBlock)(void);
+typedef void (^MJRefreshComponentBeginRefreshingCompletionBlock)(void) MJRefreshDeprecated("first deprecated in 3.3.0 - Use `MJRefreshComponentAction` instead");
 /** 结束刷新后的回调 */
-typedef void (^MJRefreshComponentEndRefreshingCompletionBlock)(void);
+typedef void (^MJRefreshComponentEndRefreshingCompletionBlock)(void) MJRefreshDeprecated("first deprecated in 3.3.0 - Use `MJRefreshComponentAction` instead");
+
+/** 刷新用到的回调类型 */
+typedef void (^MJRefreshComponentAction)(void);
 
 /** 刷新控件的基类 */
 @interface MJRefreshComponent : UIView
@@ -43,9 +47,18 @@ typedef void (^MJRefreshComponentEndRefreshingCompletionBlock)(void);
     /** 父控件 */
     __weak UIScrollView *_scrollView;
 }
+
+#pragma mark - 刷新动画时间控制
+/** 快速动画时间(一般用在刷新开始的回弹动画), 默认 0.25 */
+@property (nonatomic) NSTimeInterval fastAnimationDuration;
+/** 慢速动画时间(一般用在刷新结束后的回弹动画), 默认 0.4*/
+@property (nonatomic) NSTimeInterval slowAnimationDuration;
+/** 关闭全部默认动画效果, 可以简单粗暴地解决 CollectionView 的回弹动画 bug */
+- (instancetype)setAnimationDisabled;
+
 #pragma mark - 刷新回调
 /** 正在刷新的回调 */
-@property (copy, nonatomic) MJRefreshComponentRefreshingBlock refreshingBlock;
+@property (copy, nonatomic, nullable) MJRefreshComponentAction refreshingBlock;
 /** 设置回调对象和回调方法 */
 - (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action;
 
@@ -61,15 +74,18 @@ typedef void (^MJRefreshComponentEndRefreshingCompletionBlock)(void);
 - (void)beginRefreshing;
 - (void)beginRefreshingWithCompletionBlock:(void (^)(void))completionBlock;
 /** 开始刷新后的回调(进入刷新状态后的回调) */
-@property (copy, nonatomic) MJRefreshComponentbeginRefreshingCompletionBlock beginRefreshingCompletionBlock;
+@property (copy, nonatomic, nullable) MJRefreshComponentAction beginRefreshingCompletionBlock;
+/** 带动画的结束刷新的回调 */
+@property (copy, nonatomic, nullable) MJRefreshComponentAction endRefreshingAnimateCompletionBlock MJRefreshDeprecated("first deprecated in 3.3.0 - Use `endRefreshingAnimationBeginAction` instead");
+@property (copy, nonatomic, nullable) MJRefreshComponentAction endRefreshingAnimationBeginAction;
 /** 结束刷新的回调 */
-@property (copy, nonatomic) MJRefreshComponentEndRefreshingCompletionBlock endRefreshingCompletionBlock;
+@property (copy, nonatomic, nullable) MJRefreshComponentAction endRefreshingCompletionBlock;
 /** 结束刷新状态 */
 - (void)endRefreshing;
 - (void)endRefreshingWithCompletionBlock:(void (^)(void))completionBlock;
 /** 是否正在刷新 */
 @property (assign, nonatomic, readonly, getter=isRefreshing) BOOL refreshing;
-//- (BOOL)isRefreshing;
+
 /** 刷新状态 一般交给子类内部实现 */
 @property (assign, nonatomic) MJRefreshState state;
 
@@ -85,12 +101,19 @@ typedef void (^MJRefreshComponentEndRefreshingCompletionBlock)(void);
 /** 摆放子控件frame */
 - (void)placeSubviews NS_REQUIRES_SUPER;
 /** 当scrollView的contentOffset发生改变的时候调用 */
-- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;
+- (void)scrollViewContentOffsetDidChange:(nullable NSDictionary *)change NS_REQUIRES_SUPER;
 /** 当scrollView的contentSize发生改变的时候调用 */
-- (void)scrollViewContentSizeDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;
+- (void)scrollViewContentSizeDidChange:(nullable NSDictionary *)change NS_REQUIRES_SUPER;
 /** 当scrollView的拖拽状态发生改变的时候调用 */
-- (void)scrollViewPanStateDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;
+- (void)scrollViewPanStateDidChange:(nullable NSDictionary *)change NS_REQUIRES_SUPER;
 
+/** 多语言配置 language 发生变化时调用
+ 
+ `MJRefreshConfig.defaultConfig.language` 发生改变时调用.
+ 
+ ⚠️ 父类会调用 `placeSubviews` 方法, 请勿在 placeSubviews 中调用本方法, 造成死循环. 子类在需要重新布局时, 在配置完修改后, 最后再调用 super 方法, 否则可能导致配置修改后, 定位先于修改执行.
+ */
+- (void)i18nDidChange NS_REQUIRES_SUPER;
 
 #pragma mark - 其他
 /** 拉拽的百分比(交给子类重写) */
@@ -103,5 +126,26 @@ typedef void (^MJRefreshComponentEndRefreshingCompletionBlock)(void);
 
 @interface UILabel(MJRefresh)
 + (instancetype)mj_label;
-- (CGFloat)mj_textWith;
+- (CGFloat)mj_textWidth;
+@end
+
+@interface MJRefreshComponent (ChainingGrammar)
+
+#pragma mark - <<< 为 Swift 扩展链式语法 >>> -
+/// 自动变化透明度
+- (instancetype)autoChangeTransparency:(BOOL)isAutoChange;
+/// 刷新开始后立即调用的回调
+- (instancetype)afterBeginningAction:(MJRefreshComponentAction)action;
+/// 刷新动画开始后立即调用的回调
+- (instancetype)endingAnimationBeginningAction:(MJRefreshComponentAction)action;
+/// 刷新结束后立即调用的回调
+- (instancetype)afterEndingAction:(MJRefreshComponentAction)action;
+
+
+/// 需要子类必须实现
+/// @param scrollView 赋值给的 ScrollView 的 Header/Footer/Trailer
+- (instancetype)linkTo:(UIScrollView *)scrollView;
+
 @end
+
+NS_ASSUME_NONNULL_END

+ 83 - 32
KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.m

@@ -1,7 +1,6 @@
 //  代码地址: https://github.com/CoderMJLee/MJRefresh
-//  代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
 //  MJRefreshComponent.m
-//  MJRefreshExample
+//  MJRefresh
 //
 //  Created by MJ Lee on 15/3/4.
 //  Copyright (c) 2015年 小码哥. All rights reserved.
@@ -9,6 +8,11 @@
 
 #import "MJRefreshComponent.h"
 #import "MJRefreshConst.h"
+#import "MJRefreshConfig.h"
+#import "UIView+MJExtension.h"
+#import "UIScrollView+MJExtension.h"
+#import "UIScrollView+MJRefresh.h"
+#import "NSBundle+MJRefresh.h"
 
 @interface MJRefreshComponent()
 @property (strong, nonatomic) UIPanGestureRecognizer *pan;
@@ -24,6 +28,8 @@
         
         // 默认是普通状态
         self.state = MJRefreshStateIdle;
+        self.fastAnimationDuration = 0.25;
+        self.slowAnimationDuration = 0.4;
     }
     return self;
 }
@@ -55,13 +61,14 @@
     [self removeObservers];
     
     if (newSuperview) { // 新的父控件
+        // 记录UIScrollView
+        _scrollView = (UIScrollView *)newSuperview;
+        
         // 设置宽度
-        self.mj_w = newSuperview.mj_w;
+        self.mj_w = _scrollView.mj_w;
         // 设置位置
         self.mj_x = -_scrollView.mj_insetL;
-        
-        // 记录UIScrollView
-        _scrollView = (UIScrollView *)newSuperview;
+    
         // 设置永远支持垂直弹簧效果
         _scrollView.alwaysBounceVertical = YES;
         // 记录UIScrollView最开始的contentInset
@@ -90,6 +97,8 @@
     [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentSize options:options context:nil];
     self.pan = self.scrollView.panGestureRecognizer;
     [self.pan addObserver:self forKeyPath:MJRefreshKeyPathPanState options:options context:nil];
+    
+    [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(i18nDidChange) name:MJRefreshDidChangeLanguageNotification object:MJRefreshConfig.defaultConfig];
 }
 
 - (void)removeObservers
@@ -123,6 +132,10 @@
 - (void)scrollViewContentSizeDidChange:(NSDictionary *)change{}
 - (void)scrollViewPanStateDidChange:(NSDictionary *)change{}
 
+- (void)i18nDidChange {
+    [self placeSubviews];
+}
+
 #pragma mark - 公共方法
 #pragma mark 设置回调对象和回调方法
 - (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action
@@ -142,7 +155,7 @@
 #pragma mark 进入刷新状态
 - (void)beginRefreshing
 {
-    [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
+    [UIView animateWithDuration:self.fastAnimationDuration animations:^{
         self.alpha = 1.0;
     }];
     self.pullingPercent = 1.0;
@@ -224,17 +237,28 @@
 #pragma mark - 内部方法
 - (void)executeRefreshingCallback
 {
-    MJRefreshDispatchAsyncOnMainQueue({
-        if (self.refreshingBlock) {
-            self.refreshingBlock();
-        }
-        if ([self.refreshingTarget respondsToSelector:self.refreshingAction]) {
-            MJRefreshMsgSend(MJRefreshMsgTarget(self.refreshingTarget), self.refreshingAction, self);
-        }
-        if (self.beginRefreshingCompletionBlock) {
-            self.beginRefreshingCompletionBlock();
-        }
-    })
+    if (self.refreshingBlock) {
+        self.refreshingBlock();
+    }
+    if ([self.refreshingTarget respondsToSelector:self.refreshingAction]) {
+        MJRefreshMsgSend(MJRefreshMsgTarget(self.refreshingTarget), self.refreshingAction, self);
+    }
+    if (self.beginRefreshingCompletionBlock) {
+        self.beginRefreshingCompletionBlock();
+    }
+}
+
+#pragma mark - 刷新动画时间控制
+- (instancetype)setAnimationDisabled {
+    self.fastAnimationDuration = 0;
+    self.slowAnimationDuration = 0;
+    
+    return self;
+}
+
+#pragma mark - <<< Deprecation compatible function >>> -
+- (void)setEndRefreshingAnimateCompletionBlock:(MJRefreshComponentEndRefreshingCompletionBlock)endRefreshingAnimateCompletionBlock {
+    _endRefreshingAnimationBeginAction = endRefreshingAnimateCompletionBlock;
 }
 @end
 
@@ -250,23 +274,50 @@
     return label;
 }
 
-- (CGFloat)mj_textWith {
+- (CGFloat)mj_textWidth {
     CGFloat stringWidth = 0;
     CGSize size = CGSizeMake(MAXFLOAT, MAXFLOAT);
-    if (self.text.length > 0) {
-#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
-        stringWidth =[self.text
-                      boundingRectWithSize:size
-                      options:NSStringDrawingUsesLineFragmentOrigin
-                      attributes:@{NSFontAttributeName:self.font}
-                      context:nil].size.width;
-#else
-        
-        stringWidth = [self.text sizeWithFont:self.font
-                            constrainedToSize:size
-                                lineBreakMode:NSLineBreakByCharWrapping].width;
-#endif
+    
+    if (self.attributedText) {
+        if (self.attributedText.length == 0) { return 0; }
+        stringWidth = [self.attributedText boundingRectWithSize:size
+                                                        options:NSStringDrawingUsesLineFragmentOrigin
+                                                        context:nil].size.width;
+    } else {
+        if (self.text.length == 0) { return 0; }
+        NSAssert(self.font != nil, @"请检查 mj_label's `font` 是否设置正确");
+        stringWidth = [self.text boundingRectWithSize:size
+                                              options:NSStringDrawingUsesLineFragmentOrigin
+                                           attributes:@{NSFontAttributeName:self.font}
+                                              context:nil].size.width;
     }
     return stringWidth;
 }
 @end
+
+
+#pragma mark - <<< 为 Swift 扩展链式语法 >>> -
+@implementation MJRefreshComponent (ChainingGrammar)
+
+- (instancetype)autoChangeTransparency:(BOOL)isAutoChange {
+    self.automaticallyChangeAlpha = isAutoChange;
+    return self;
+}
+- (instancetype)afterBeginningAction:(MJRefreshComponentAction)action {
+    self.beginRefreshingCompletionBlock = action;
+    return self;
+}
+- (instancetype)endingAnimationBeginningAction:(MJRefreshComponentAction)action {
+    self.endRefreshingAnimationBeginAction = action;
+    return self;
+}
+- (instancetype)afterEndingAction:(MJRefreshComponentAction)action {
+    self.endRefreshingCompletionBlock = action;
+    return self;
+}
+
+- (instancetype)linkTo:(UIScrollView *)scrollView {
+    return self;
+}
+
+@end

+ 10 - 3
KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.h

@@ -1,17 +1,22 @@
 //  代码地址: https://github.com/CoderMJLee/MJRefresh
-//  代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
 //  MJRefreshFooter.h
-//  MJRefreshExample
+//  MJRefresh
 //
 //  Created by MJ Lee on 15/3/5.
 //  Copyright (c) 2015年 小码哥. All rights reserved.
 //  上拉刷新控件
 
+#if __has_include(<MJRefresh/MJRefreshComponent.h>)
+#import <MJRefresh/MJRefreshComponent.h>
+#else
 #import "MJRefreshComponent.h"
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
 
 @interface MJRefreshFooter : MJRefreshComponent
 /** 创建footer */
-+ (instancetype)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock;
++ (instancetype)footerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock;
 /** 创建footer */
 + (instancetype)footerWithRefreshingTarget:(id)target refreshingAction:(SEL)action;
 
@@ -28,3 +33,5 @@
 /** 自动根据有无数据来显示和隐藏(有数据就显示,没有数据隐藏。默认是NO) */
 @property (assign, nonatomic, getter=isAutomaticallyHidden) BOOL automaticallyHidden MJRefreshDeprecated("已废弃此属性,开发者请自行控制footer的显示和隐藏");
 @end
+
+NS_ASSUME_NONNULL_END

+ 10 - 19
KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.m

@@ -1,14 +1,14 @@
 //  代码地址: https://github.com/CoderMJLee/MJRefresh
-//  代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
 //  MJRefreshFooter.m
-//  MJRefreshExample
+//  MJRefresh
 //
 //  Created by MJ Lee on 15/3/5.
 //  Copyright (c) 2015年 小码哥. All rights reserved.
 //
 
 #import "MJRefreshFooter.h"
-#include "UIScrollView+MJRefresh.h"
+#import "UIScrollView+MJRefresh.h"
+#import "UIView+MJExtension.h"
 
 @interface MJRefreshFooter()
 
@@ -16,7 +16,7 @@
 
 @implementation MJRefreshFooter
 #pragma mark - 构造方法
-+ (instancetype)footerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock
++ (instancetype)footerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock
 {
     MJRefreshFooter *cmp = [[self alloc] init];
     cmp.refreshingBlock = refreshingBlock;
@@ -41,21 +41,12 @@
 //    self.automaticallyHidden = NO;
 }
 
-//- (void)willMoveToSuperview:(UIView *)newSuperview
-//{
-//    [super willMoveToSuperview:newSuperview];
-//
-//    if (newSuperview) {
-//        // 监听scrollView数据的变化
-//        if ([self.scrollView isKindOfClass:[UITableView class]] || [self.scrollView isKindOfClass:[UICollectionView class]]) {
-//            [self.scrollView setMj_reloadDataBlock:^(NSInteger totalDataCount) {
-//                if (self.isAutomaticallyHidden) {
-//                    self.hidden = (totalDataCount == 0);
-//                }
-//            }];
-//        }
-//    }
-//}
+#pragma mark . 链式语法部分 .
+
+- (instancetype)linkTo:(UIScrollView *)scrollView {
+    scrollView.mj_footer = self;
+    return self;
+}
 
 #pragma mark - 公共方法
 - (void)endRefreshingWithNoMoreData

+ 14 - 4
KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.h

@@ -1,25 +1,35 @@
 //  代码地址: https://github.com/CoderMJLee/MJRefresh
-//  代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
 //  MJRefreshHeader.h
-//  MJRefreshExample
+//  MJRefresh
 //
 //  Created by MJ Lee on 15/3/4.
 //  Copyright (c) 2015年 小码哥. All rights reserved.
 //  下拉刷新控件:负责监控用户下拉的状态
 
+#if __has_include(<MJRefresh/MJRefreshComponent.h>)
+#import <MJRefresh/MJRefreshComponent.h>
+#else
 #import "MJRefreshComponent.h"
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
 
 @interface MJRefreshHeader : MJRefreshComponent
 /** 创建header */
-+ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock;
++ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock;
 /** 创建header */
 + (instancetype)headerWithRefreshingTarget:(id)target refreshingAction:(SEL)action;
 
 /** 这个key用来存储上一次下拉刷新成功的时间 */
 @property (copy, nonatomic) NSString *lastUpdatedTimeKey;
 /** 上一次下拉刷新成功的时间 */
-@property (strong, nonatomic, readonly) NSDate *lastUpdatedTime;
+@property (strong, nonatomic, readonly, nullable) NSDate *lastUpdatedTime;
 
 /** 忽略多少scrollView的contentInset的top */
 @property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetTop;
+
+/** 默认是关闭状态, 如果遇到 CollectionView 的动画异常问题可以尝试打开 */
+@property (nonatomic) BOOL isCollectionViewAnimationBug;
 @end
+
+NS_ASSUME_NONNULL_END

+ 169 - 37
KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.m

@@ -1,21 +1,26 @@
 //  代码地址: https://github.com/CoderMJLee/MJRefresh
-//  代码地址: http://code4app.com/ios/%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E4%B8%8B%E6%8B%89%E4%B8%8A%E6%8B%89%E5%88%B7%E6%96%B0/52326ce26803fabc46000000
 //  MJRefreshHeader.m
-//  MJRefreshExample
+//  MJRefresh
 //
 //  Created by MJ Lee on 15/3/4.
 //  Copyright (c) 2015年 小码哥. All rights reserved.
 //
 
 #import "MJRefreshHeader.h"
+#import "UIView+MJExtension.h"
+#import "UIScrollView+MJExtension.h"
+#import "UIScrollView+MJRefresh.h"
 
-@interface MJRefreshHeader()
+NSString * const MJRefreshHeaderRefreshing2IdleBoundsKey = @"MJRefreshHeaderRefreshing2IdleBounds";
+NSString * const MJRefreshHeaderRefreshingBoundsKey = @"MJRefreshHeaderRefreshingBounds";
+
+@interface MJRefreshHeader() <CAAnimationDelegate>
 @property (assign, nonatomic) CGFloat insetTDelta;
 @end
 
 @implementation MJRefreshHeader
 #pragma mark - 构造方法
-+ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock
++ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock
 {
     MJRefreshHeader *cmp = [[self alloc] init];
     cmp.refreshingBlock = refreshingBlock;
@@ -48,10 +53,20 @@
     self.mj_y = - self.mj_h - self.ignoredScrollViewContentInsetTop;
 }
 
-- (void)willMoveToWindow:(UIWindow *)newWindow {
+- (void)resetInset {
+    if (@available(iOS 11.0, *)) {
+    } else {
+        // 如果 iOS 10 及以下系统在刷新时, push 新的 VC, 等待刷新完成后回来, 会导致顶部 Insets.top 异常, 不能 resetInset, 检查一下这种特殊情况
+        if (!self.window) { return; }
+    }
     
-    if (!newWindow && self.isRefreshing) {
-        [self endRefreshing];
+    // sectionheader停留解决
+    CGFloat insetT = - self.scrollView.mj_offsetY > _scrollViewOriginalInset.top ? - self.scrollView.mj_offsetY : _scrollViewOriginalInset.top;
+    insetT = insetT > self.mj_h + _scrollViewOriginalInset.top ? self.mj_h + _scrollViewOriginalInset.top : insetT;
+    self.insetTDelta = _scrollViewOriginalInset.top - insetT;
+    // 避免 CollectionView 在使用根据 Autolayout 和 内容自动伸缩 Cell, 刷新时导致的 Layout 异常渲染问题
+    if (fabs(self.scrollView.mj_insetT - insetT) > FLT_EPSILON) {
+        self.scrollView.mj_insetT = insetT;
     }
 }
 
@@ -61,15 +76,7 @@
     
     // 在刷新的refreshing状态
     if (self.state == MJRefreshStateRefreshing) {
-        // 暂时保留
-        if (self.window == nil) return;
-        
-        // sectionheader停留解决
-        CGFloat insetT = - self.scrollView.mj_offsetY > _scrollViewOriginalInset.top ? - self.scrollView.mj_offsetY : _scrollViewOriginalInset.top;
-        insetT = insetT > self.mj_h + _scrollViewOriginalInset.top ? self.mj_h + _scrollViewOriginalInset.top : insetT;
-        self.scrollView.mj_insetT = insetT;
-        
-        self.insetTDelta = _scrollViewOriginalInset.top - insetT;
+        [self resetInset];
         return;
     }
     
@@ -114,14 +121,26 @@
     if (state == MJRefreshStateIdle) {
         if (oldState != MJRefreshStateRefreshing) return;
         
-        // 保存刷新时间
-        [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:self.lastUpdatedTimeKey];
-        [[NSUserDefaults standardUserDefaults] synchronize];
-        
+        [self headerEndingAction];
+    } else if (state == MJRefreshStateRefreshing) {
+        [self headerRefreshingAction];
+    }
+}
+
+- (void)headerEndingAction {
+    // 保存刷新时间
+    [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:self.lastUpdatedTimeKey];
+    [[NSUserDefaults standardUserDefaults] synchronize];
+    
+    // 默认使用 UIViewAnimation 动画
+    if (!self.isCollectionViewAnimationBug) {
         // 恢复inset和offset
-        [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
+        [UIView animateWithDuration:self.slowAnimationDuration animations:^{
             self.scrollView.mj_insetT += self.insetTDelta;
             
+            if (self.endRefreshingAnimationBeginAction) {
+                self.endRefreshingAnimationBeginAction();
+            }
             // 自动调整透明度
             if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0;
         } completion:^(BOOL finished) {
@@ -131,22 +150,135 @@
                 self.endRefreshingCompletionBlock();
             }
         }];
-    } else if (state == MJRefreshStateRefreshing) {
-        MJRefreshDispatchAsyncOnMainQueue({
-            [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
-                if (self.scrollView.panGestureRecognizer.state != UIGestureRecognizerStateCancelled) {
-                    CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
-                    // 增加滚动区域top
-                    self.scrollView.mj_insetT = top;
-                    // 设置滚动位置
-                    CGPoint offset = self.scrollView.contentOffset;
-                    offset.y = -top;
-                    [self.scrollView setContentOffset:offset animated:NO];
-                }
-            } completion:^(BOOL finished) {
-                [self executeRefreshingCallback];
-            }];
-        })
+        
+        return;
+    }
+    
+    /**
+     这个解决方法的思路出自 https://github.com/CoderMJLee/MJRefresh/pull/844
+     修改了用+ [UIView animateWithDuration: animations:]实现的修改contentInset的动画
+     fix issue#225 https://github.com/CoderMJLee/MJRefresh/issues/225
+     另一种解法 pull#737 https://github.com/CoderMJLee/MJRefresh/pull/737
+     
+     同时, 处理了 Refreshing 中的动画替换.
+    */
+    
+    // 由于修改 Inset 会导致 self.pullingPercent 联动设置 self.alpha, 故提前获取 alpha 值, 后续用于还原 alpha 动画
+    CGFloat viewAlpha = self.alpha;
+    
+    self.scrollView.mj_insetT += self.insetTDelta;
+    // 禁用交互, 如果不禁用可能会引起渲染问题.
+    self.scrollView.userInteractionEnabled = NO;
+
+    //CAAnimation keyPath 不支持 contentInset 用Bounds的动画代替
+    CABasicAnimation *boundsAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"];
+    boundsAnimation.fromValue = [NSValue valueWithCGRect:CGRectOffset(self.scrollView.bounds, 0, self.insetTDelta)];
+    boundsAnimation.duration = self.slowAnimationDuration;
+    //在delegate里移除
+    boundsAnimation.removedOnCompletion = NO;
+    boundsAnimation.fillMode = kCAFillModeBoth;
+    boundsAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+    boundsAnimation.delegate = self;
+    [boundsAnimation setValue:MJRefreshHeaderRefreshing2IdleBoundsKey forKey:@"identity"];
+
+    [self.scrollView.layer addAnimation:boundsAnimation forKey:MJRefreshHeaderRefreshing2IdleBoundsKey];
+    
+    if (self.endRefreshingAnimationBeginAction) {
+        self.endRefreshingAnimationBeginAction();
+    }
+    // 自动调整透明度的动画
+    if (self.isAutomaticallyChangeAlpha) {
+        CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
+        opacityAnimation.fromValue = @(viewAlpha);
+        opacityAnimation.toValue = @(0.0);
+        opacityAnimation.duration = self.slowAnimationDuration;
+        opacityAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+        [self.layer addAnimation:opacityAnimation forKey:@"MJRefreshHeaderRefreshing2IdleOpacity"];
+
+        // 由于修改了 inset 导致, pullingPercent 被设置值, alpha 已经被提前修改为 0 了. 所以这里不用置 0, 但为了代码的严谨性, 不依赖其他的特殊实现方式, 这里还是置 0.
+        self.alpha = 0;
+    }
+}
+
+- (void)headerRefreshingAction {
+    // 默认使用 UIViewAnimation 动画
+    if (!self.isCollectionViewAnimationBug) {
+        [UIView animateWithDuration:self.fastAnimationDuration animations:^{
+            if (self.scrollView.panGestureRecognizer.state != UIGestureRecognizerStateCancelled) {
+                CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
+                // 增加滚动区域top
+                self.scrollView.mj_insetT = top;
+                // 设置滚动位置
+                CGPoint offset = self.scrollView.contentOffset;
+                offset.y = -top;
+                [self.scrollView setContentOffset:offset animated:NO];
+            }
+        } completion:^(BOOL finished) {
+            [self executeRefreshingCallback];
+        }];
+        return;
+    }
+    
+    if (self.scrollView.panGestureRecognizer.state != UIGestureRecognizerStateCancelled) {
+        CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
+        // 禁用交互, 如果不禁用可能会引起渲染问题.
+        self.scrollView.userInteractionEnabled = NO;
+
+        // CAAnimation keyPath不支持 contentOffset 用Bounds的动画代替
+        CABasicAnimation *boundsAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"];
+        CGRect bounds = self.scrollView.bounds;
+        bounds.origin.y = -top;
+        boundsAnimation.fromValue = [NSValue valueWithCGRect:self.scrollView.bounds];
+        boundsAnimation.toValue = [NSValue valueWithCGRect:bounds];
+        boundsAnimation.duration = self.fastAnimationDuration;
+        //在delegate里移除
+        boundsAnimation.removedOnCompletion = NO;
+        boundsAnimation.fillMode = kCAFillModeBoth;
+        boundsAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+        boundsAnimation.delegate = self;
+        [boundsAnimation setValue:MJRefreshHeaderRefreshingBoundsKey forKey:@"identity"];
+        [self.scrollView.layer addAnimation:boundsAnimation forKey:MJRefreshHeaderRefreshingBoundsKey];
+    } else {
+        [self executeRefreshingCallback];
+    }
+}
+
+#pragma mark . 链式语法部分 .
+
+- (instancetype)linkTo:(UIScrollView *)scrollView {
+    scrollView.mj_header = self;
+    return self;
+}
+
+#pragma mark - CAAnimationDelegate
+- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
+    NSString *identity = [anim valueForKey:@"identity"];
+    if ([identity isEqualToString:MJRefreshHeaderRefreshing2IdleBoundsKey]) {
+        self.pullingPercent = 0.0;
+        self.scrollView.userInteractionEnabled = YES;
+        if (self.endRefreshingCompletionBlock) {
+            self.endRefreshingCompletionBlock();
+        }
+    } else if ([identity isEqualToString:MJRefreshHeaderRefreshingBoundsKey]) {
+        // 避免出现 end 先于 Refreshing 状态
+        if (self.state != MJRefreshStateIdle) {
+            CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
+            self.scrollView.mj_insetT = top;
+            // 设置最终滚动位置
+            CGPoint offset = self.scrollView.contentOffset;
+            offset.y = -top;
+            [self.scrollView setContentOffset:offset animated:NO];
+         }
+        self.scrollView.userInteractionEnabled = YES;
+        [self executeRefreshingCallback];
+    }
+    
+    if ([self.scrollView.layer animationForKey:MJRefreshHeaderRefreshing2IdleBoundsKey]) {
+        [self.scrollView.layer removeAnimationForKey:MJRefreshHeaderRefreshing2IdleBoundsKey];
+    }
+    
+    if ([self.scrollView.layer animationForKey:MJRefreshHeaderRefreshingBoundsKey]) {
+        [self.scrollView.layer removeAnimationForKey:MJRefreshHeaderRefreshingBoundsKey];
     }
 }
 

+ 30 - 0
KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.h

@@ -0,0 +1,30 @@
+//
+//  MJRefreshTrailer.h
+//  MJRefresh
+//
+//  Created by kinarobin on 2020/5/3.
+//  Copyright © 2020 小码哥. All rights reserved.
+//
+
+#if __has_include(<MJRefresh/MJRefreshComponent.h>)
+#import <MJRefresh/MJRefreshComponent.h>
+#else
+#import "MJRefreshComponent.h"
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MJRefreshTrailer : MJRefreshComponent
+
+/** 创建trailer*/
++ (instancetype)trailerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock;
+/** 创建trailer */
++ (instancetype)trailerWithRefreshingTarget:(id)target refreshingAction:(SEL)action;
+
+/** 忽略多少scrollView的contentInset的right */
+@property (assign, nonatomic) CGFloat ignoredScrollViewContentInsetRight;
+
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 179 - 0
KulexiuForTeacher/Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.m

@@ -0,0 +1,179 @@
+//
+//  MJRefreshTrailer.m
+//  MJRefresh
+//
+//  Created by kinarobin on 2020/5/3.
+//  Copyright © 2020 小码哥. All rights reserved.
+//
+
+#import "MJRefreshTrailer.h"
+#import "UIView+MJExtension.h"
+#import "UIScrollView+MJRefresh.h"
+#import "UIScrollView+MJExtension.h"
+
+@interface MJRefreshTrailer()
+@property (assign, nonatomic) NSInteger lastRefreshCount;
+@property (assign, nonatomic) CGFloat lastRightDelta;
+@end
+
+@implementation MJRefreshTrailer
+
+#pragma mark - 构造方法
++ (instancetype)trailerWithRefreshingBlock:(MJRefreshComponentAction)refreshingBlock {
+    MJRefreshTrailer *cmp = [[self alloc] init];
+    cmp.refreshingBlock = refreshingBlock;
+    return cmp;
+}
+
++ (instancetype)trailerWithRefreshingTarget:(id)target refreshingAction:(SEL)action {
+    MJRefreshTrailer *cmp = [[self alloc] init];
+    [cmp setRefreshingTarget:target refreshingAction:action];
+    return cmp;
+}
+
+- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change {
+    [super scrollViewContentOffsetDidChange:change];
+    
+    // 如果正在刷新,直接返回
+    if (self.state == MJRefreshStateRefreshing) return;
+    
+    _scrollViewOriginalInset = self.scrollView.mj_inset;
+    
+    // 当前的contentOffset
+    CGFloat currentOffsetX = self.scrollView.mj_offsetX;
+    // 尾部控件刚好出现的offsetX
+    CGFloat happenOffsetX = [self happenOffsetX];
+    // 如果是向右滚动到看不见右边控件,直接返回
+    if (currentOffsetX <= happenOffsetX) return;
+    
+    CGFloat pullingPercent = (currentOffsetX - happenOffsetX) / self.mj_w;
+    
+    // 如果已全部加载,仅设置pullingPercent,然后返回
+    if (self.state == MJRefreshStateNoMoreData) {
+        self.pullingPercent = pullingPercent;
+        return;
+    }
+    
+    if (self.scrollView.isDragging) {
+        self.pullingPercent = pullingPercent;
+        // 普通 和 即将刷新 的临界点
+        CGFloat normal2pullingOffsetX = happenOffsetX + self.mj_w;
+        
+        if (self.state == MJRefreshStateIdle && currentOffsetX > normal2pullingOffsetX) {
+            self.state = MJRefreshStatePulling;
+        } else if (self.state == MJRefreshStatePulling && currentOffsetX <= normal2pullingOffsetX) {
+            // 转为普通状态
+            self.state = MJRefreshStateIdle;
+        }
+    } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开
+        // 开始刷新
+        [self beginRefreshing];
+    } else if (pullingPercent < 1) {
+        self.pullingPercent = pullingPercent;
+    }
+}
+
+- (void)setState:(MJRefreshState)state {
+    MJRefreshCheckState
+    // 根据状态来设置属性
+    if (state == MJRefreshStateNoMoreData || state == MJRefreshStateIdle) {
+        // 刷新完毕
+        if (MJRefreshStateRefreshing == oldState) {
+            [UIView animateWithDuration:self.slowAnimationDuration animations:^{
+                if (self.endRefreshingAnimationBeginAction) {
+                    self.endRefreshingAnimationBeginAction();
+                }
+                
+                self.scrollView.mj_insetR -= self.lastRightDelta;
+                // 自动调整透明度
+                if (self.isAutomaticallyChangeAlpha) self.alpha = 0.0;
+            } completion:^(BOOL finished) {
+                self.pullingPercent = 0.0;
+                
+                if (self.endRefreshingCompletionBlock) {
+                    self.endRefreshingCompletionBlock();
+                }
+            }];
+        }
+        
+        CGFloat deltaW = [self widthForContentBreakView];
+        // 刚刷新完毕
+        if (MJRefreshStateRefreshing == oldState && deltaW > 0 && self.scrollView.mj_totalDataCount != self.lastRefreshCount) {
+            self.scrollView.mj_offsetX = self.scrollView.mj_offsetX;
+        }
+    } else if (state == MJRefreshStateRefreshing) {
+        // 记录刷新前的数量
+        self.lastRefreshCount = self.scrollView.mj_totalDataCount;
+        
+        [UIView animateWithDuration:self.fastAnimationDuration animations:^{
+            CGFloat right = self.mj_w + self.scrollViewOriginalInset.right;
+            CGFloat deltaW = [self widthForContentBreakView];
+            if (deltaW < 0) { // 如果内容宽度小于view的宽度
+                right -= deltaW;
+            }
+            self.lastRightDelta = right - self.scrollView.mj_insetR;
+            self.scrollView.mj_insetR = right;
+            
+            // 设置滚动位置
+            CGPoint offset = self.scrollView.contentOffset;
+            offset.x = [self happenOffsetX] + self.mj_w;
+            [self.scrollView setContentOffset:offset animated:NO];
+        } completion:^(BOOL finished) {
+            [self executeRefreshingCallback];
+        }];
+    }
+}
+
+- (void)scrollViewContentSizeDidChange:(NSDictionary *)change {
+    [super scrollViewContentSizeDidChange:change];
+    
+    // 内容的宽度
+    CGFloat contentWidth = self.scrollView.mj_contentW + self.ignoredScrollViewContentInsetRight;
+    // 表格的宽度
+    CGFloat scrollWidth = self.scrollView.mj_w - self.scrollViewOriginalInset.left - self.scrollViewOriginalInset.right + self.ignoredScrollViewContentInsetRight;
+    // 设置位置和尺寸
+    self.mj_x = MAX(contentWidth, scrollWidth);
+}
+
+- (void)placeSubviews {
+    [super placeSubviews];
+    
+    self.mj_h = _scrollView.mj_h;
+    // 设置自己的宽度
+    self.mj_w = MJRefreshTrailWidth;
+}
+
+- (void)willMoveToSuperview:(UIView *)newSuperview {
+    [super willMoveToSuperview:newSuperview];
+    
+    if (newSuperview) {
+        // 设置支持水平弹簧效果
+        _scrollView.alwaysBounceHorizontal = YES;
+        _scrollView.alwaysBounceVertical = NO;
+    }
+}
+
+#pragma mark . 链式语法部分 .
+
+- (instancetype)linkTo:(UIScrollView *)scrollView {
+    scrollView.mj_trailer = self;
+    return self;
+}
+
+#pragma mark - 刚好看到上拉刷新控件时的contentOffset.x
+- (CGFloat)happenOffsetX {
+    CGFloat deltaW = [self widthForContentBreakView];
+    if (deltaW > 0) {
+        return deltaW - self.scrollViewOriginalInset.left;
+    } else {
+        return - self.scrollViewOriginalInset.left;
+    }
+}
+
+#pragma mark 获得scrollView的内容 超出 view 的宽度
+- (CGFloat)widthForContentBreakView {
+    CGFloat w = self.scrollView.frame.size.width - self.scrollViewOriginalInset.right - self.scrollViewOriginalInset.left;
+    return self.scrollView.contentSize.width - w;
+}
+
+@end

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