RQExerciseQuestionItemViewModel.m 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. //
  2. // RQExerciseQuestionItemViewModel.m
  3. // JSJP
  4. //
  5. // Created by 张嵘 on 2021/8/16.
  6. //
  7. #import "RQExerciseQuestionItemViewModel.h"
  8. #import "payRequsestHandler.h"
  9. @interface RQExerciseQuestionItemViewModel ()
  10. @property (nonatomic, readwrite, strong) RQExerciseModel *exerciseModel;
  11. @property (nonatomic, readwrite, strong) YYLabel *yyLabel;
  12. @property (nonatomic, readwrite, strong) NSString *typeString;
  13. @property (nonatomic, readwrite, strong) NSString *newString;
  14. @property (nonatomic, readwrite, strong) NSMutableAttributedString *qusetionString;
  15. @property (nonatomic, readwrite, assign) CGFloat labelHeight;
  16. @property (nonatomic, readwrite, assign) CGFloat imageHeight;
  17. @property (nonatomic, readwrite, strong) NSString *imageString;
  18. @property (nonatomic, readwrite, strong) NSString *videoString;
  19. @property (nonatomic, readwrite, strong) NSMutableAttributedString *spaceAttachment;
  20. @property (nonatomic, readwrite, strong) QMUIButton *readBtn;
  21. @property (nonatomic,assign) int readCount;
  22. @end
  23. @implementation RQExerciseQuestionItemViewModel
  24. - (instancetype)initWithRQExerciseModel:(RQExerciseModel *)exerciseModel {
  25. if (self = [super init]) {
  26. @weakify(self)
  27. self.readCount = 0;
  28. self.exerciseModel = exerciseModel;
  29. dispatch_async(dispatch_get_main_queue(), ^{
  30. @strongify(self)
  31. self.qusetionString = [self dealQuestionStrWithRQExerciseModel:exerciseModel];
  32. self.labelHeight = [self getMessageHeightWithAttributedStr:self.qusetionString andLabel:self.yyLabel];
  33. });
  34. if (RQStringIsNotEmpty(exerciseModel.ydtQuestionModel.media_url)) {
  35. if (RQStringIsNotEmpty(exerciseModel.ydtQuestionModel.sinaimg)) {
  36. self.imageString = exerciseModel.ydtQuestionModel.media_url;
  37. self.videoString = @"";
  38. } else if (RQStringIsNotEmpty(exerciseModel.ydtQuestionModel.video_url)) {
  39. self.imageString = @"";
  40. self.videoString = exerciseModel.ydtQuestionModel.media_url;
  41. } else {
  42. self.imageString = @"";
  43. self.videoString = @"";
  44. }
  45. } else {
  46. self.imageString = @"";
  47. self.videoString = @"";
  48. }
  49. if (RQStringIsNotEmpty(exerciseModel.ydtQuestionModel.sinaimg) || RQStringIsNotEmpty(exerciseModel.ydtQuestionModel.media_url)) {
  50. if (!RQObjectIsNil(exerciseModel.ydtQuestionModel.media_content)) {
  51. NSString *imageStr = [exerciseModel.ydtQuestionModel.media_content base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
  52. UIImage *sinaImage = [UIImage sd_imageWithData:[NSData dataWithBase64EncodedString:imageStr] scale:1 firstFrameOnly:YES];
  53. exerciseModel.ydtQuestionModel.sinaImage = sinaImage;
  54. } else {
  55. NSString *media_name;
  56. if(RQStringIsNotEmpty(exerciseModel.ydtQuestionModel.sinaimg)){//不为空
  57. media_name = exerciseModel.ydtQuestionModel.sinaimg;
  58. self.imageString = exerciseModel.ydtQuestionModel.sinaimg;
  59. }else{
  60. if ([exerciseModel.ydtQuestionModel.media_url containsString:@"https://"]) {
  61. media_name = [exerciseModel.ydtQuestionModel.media_url componentsSeparatedByString:@"/"].lastObject;
  62. }
  63. }
  64. // NSData *mediaData = [RQ_YDTQuestion_Module getMediaDataWithMediaName:media_name];
  65. // if (mediaData) {
  66. // NSString *imageStr = [mediaData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
  67. // UIImage *sinaImage = [UIImage sd_imageWithData:[NSData dataWithBase64EncodedString:imageStr] scale:1 firstFrameOnly:YES];
  68. // exerciseModel.ydtQuestionModel.sinaImage = sinaImage;
  69. // }else{
  70. //本地无数据-使用网络url-本地图片有水印 直接用url
  71. self.imageString = exerciseModel.ydtQuestionModel.media_url;
  72. self.videoString = @"";
  73. // }
  74. }
  75. } else if (RQStringIsNotEmpty(exerciseModel.ydtQuestionModel.video_url) && RQStringIsNotEmpty(exerciseModel.ydtQuestionModel.media_url)) {
  76. NSString *media_name;
  77. if ([exerciseModel.ydtQuestionModel.video_url containsString:@"https://"]) {
  78. media_name = [exerciseModel.ydtQuestionModel.video_url componentsSeparatedByString:@"/"].lastObject;
  79. } else {
  80. media_name = exerciseModel.ydtQuestionModel.video_url;
  81. }
  82. NSURL *video_filePath_Url;
  83. if (!RQObjectIsNil(exerciseModel.ydtQuestionModel.media_content) && !RQ_COMMON_MANAGER.JSJP_Is_Online_Media_Video) {
  84. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  85. NSString *documentsDirectory = [paths objectAtIndex:0];
  86. NSString *path = [documentsDirectory stringByAppendingPathComponent:media_name];
  87. [[NSFileManager defaultManager] createFileAtPath:path contents:exerciseModel.ydtQuestionModel.media_content attributes:nil];
  88. video_filePath_Url = [NSURL fileURLWithPath:path];
  89. }
  90. exerciseModel.ydtQuestionModel.video_filePath_Url = video_filePath_Url;
  91. }
  92. if (RQStringIsNotEmpty(self.imageString)) {
  93. __block CGSize imageSize = CGSizeMake(RQ_SCREEN_WIDTH - 32.f, (RQ_SCREEN_WIDTH - 32.f) * (175.f / 345.f));
  94. if (exerciseModel.ydtQuestionModel.sinaImage) {
  95. imageSize = exerciseModel.ydtQuestionModel.sinaImage.size;
  96. } else {
  97. NSString *cacheKeyStr = [[YYWebImageManager sharedManager] cacheKeyForURL:[NSURL URLWithString:self.imageString]];
  98. UIImage *cacheImage = [[YYImageCache sharedCache] getImageForKey:cacheKeyStr];
  99. if (cacheImage) {
  100. imageSize = cacheImage.size;
  101. } else {
  102. dispatch_async(dispatch_get_global_queue(0, 0), ^{
  103. @strongify(self)
  104. // 处理耗时操作的代码块...
  105. imageSize = [UIImage getImageSizeWithURL:self.imageString];
  106. //通知主线程刷新
  107. dispatch_async(dispatch_get_main_queue(), ^{
  108. @strongify(self)
  109. //回调或者说是通知主线程刷新,
  110. if (imageSize.width == 0 || imageSize.height == 0) {
  111. self.imageHeight = (RQ_SCREEN_WIDTH - 32.f) * (175.f / 345.f);
  112. } else {
  113. self.imageHeight = (RQ_SCREEN_WIDTH - 32.f) * (imageSize.height / imageSize.width);
  114. }
  115. });
  116. });
  117. }
  118. }
  119. if (imageSize.width == 0 || imageSize.height == 0) {
  120. self.imageHeight = (RQ_SCREEN_WIDTH - 32.f) * (175.f / 345.f);
  121. } else {
  122. self.imageHeight = (RQ_SCREEN_WIDTH - 32.f) * (imageSize.height / imageSize.width);
  123. }
  124. } else if (RQStringIsNotEmpty(self.videoString)) {
  125. self.imageHeight = (RQ_SCREEN_WIDTH - 32.f) * (175.f / 345.f);
  126. }
  127. self.rowHeight = 16.f + self.labelHeight + ((self.imageHeight == 0)? 0 : (16.f + self.imageHeight)) + 16.f;
  128. [[[RACObserve(RQ_Exercise_Module, exerciseFontSize) takeUntil:self.rac_willDeallocSignal] deliverOnMainThread] subscribeNext:^(id _Nullable x) {
  129. @strongify(self)
  130. self.qusetionString = [self dealQuestionStrWithRQExerciseModel:exerciseModel];
  131. self.labelHeight = [self getMessageHeightWithAttributedStr:self.qusetionString andLabel:self.yyLabel];
  132. }];
  133. RAC(self, rowHeight) = [[RACSignal combineLatest:@[[RACObserve(RQ_Exercise_Module, exerciseFontSize) takeUntil:self.rac_willDeallocSignal],[RACObserve(self, imageHeight) takeUntil:self.rac_willDeallocSignal],[RACObserve(self, labelHeight) takeUntil:self.rac_willDeallocSignal]] reduce:^(NSNumber *exerciseFontSize, NSNumber *imageHeight, NSNumber *labelHeight) {
  134. return @(16.f + labelHeight.floatValue + ((imageHeight.floatValue == 0)? 0 : (16.f + imageHeight.floatValue)) + 16.f);
  135. }] deliverOnMainThread] ;
  136. }
  137. return self;
  138. }
  139. - (NSString *)newString {
  140. return @"新规题";
  141. }
  142. - (NSMutableAttributedString *)dealQuestionStrWithRQExerciseModel:(RQExerciseModel *)exerciseModel {
  143. switch (exerciseModel.ydtQuestionModel.Type) {
  144. case 1:
  145. self.typeString = @"判断题";
  146. break;
  147. case 2:
  148. self.typeString = @"单选题";
  149. break;
  150. case 3:
  151. self.typeString = @"多选题";
  152. break;
  153. default:
  154. self.typeString = @"";
  155. break;
  156. }
  157. NSString *questionStr = [NSString stringWithFormat:@"%ld、%@",(long)exerciseModel.ydtQuestionModel.num + 1 ,exerciseModel.ydtQuestionModel.Question];
  158. NSMutableAttributedString *text = [NSMutableAttributedString new];
  159. NSMutableAttributedString *testText = [NSMutableAttributedString new];
  160. NSMutableAttributedString *typeAttachment = nil;
  161. QMUIButton *typeBtn = [QMUIButton buttonWithType:UIButtonTypeCustom];
  162. CGFloat btnHeight = (25.f / 17.f) * RQ_Exercise_Module.exerciseFontSize;
  163. CGFloat btnWeight = (60.f / 25.f) * btnHeight;
  164. CGFloat btnCornerRadius = btnHeight / 2.f;
  165. CGSize btnSize = CGSizeMake(btnWeight, btnHeight);
  166. typeBtn.size = btnSize;
  167. [typeBtn setTitle:self.typeString textColor:RQ_MAIN_TEXT_COLOR_WHITE Font:RQ_Exercise_Module.exerciseFontSize - 3 fotState:UIControlStateNormal];
  168. [typeBtn setBackgroundImage:[UIImage qmui_imageWithColor:RQ_MAIN_COLOR size:btnSize cornerRadiusArray:@[@(btnCornerRadius), @(btnCornerRadius), @0, @(btnCornerRadius)]] forState:UIControlStateNormal];
  169. typeAttachment = [NSMutableAttributedString yy_attachmentStringWithContent:typeBtn
  170. contentMode:UIViewContentModeCenter
  171. attachmentSize:btnSize
  172. alignToFont:[UIFont systemFontOfSize:RQ_Exercise_Module.exerciseFontSize]
  173. alignment:YYTextVerticalAlignmentCenter];
  174. [testText appendAttributedString:typeAttachment];
  175. [testText appendAttributedString:self.spaceAttachment];
  176. if(exerciseModel.isLocalityLX){//是本地练习
  177. NSMutableAttributedString *newAttachment = nil;
  178. QMUIButton *newBtn = [QMUIButton buttonWithType:UIButtonTypeCustom];
  179. newBtn.size = btnSize;
  180. [newBtn setTitle:@"本市考题" textColor:RQ_MAIN_TEXT_COLOR_WHITE Font:RQ_Exercise_Module.exerciseFontSize - 6 fotState:UIControlStateNormal];
  181. [newBtn setBackgroundImage:[UIImage qmui_imageWithColor:RQColor(30, 222, 91) size:btnSize cornerRadiusArray:@[@(btnCornerRadius), @(btnCornerRadius), @(btnCornerRadius), @(btnCornerRadius)]] forState:UIControlStateNormal];
  182. newAttachment = [NSMutableAttributedString yy_attachmentStringWithContent:newBtn
  183. contentMode:UIViewContentModeCenter
  184. attachmentSize:btnSize
  185. alignToFont:[UIFont systemFontOfSize:RQ_Exercise_Module.exerciseFontSize]
  186. alignment:YYTextVerticalAlignmentCenter];
  187. [testText appendAttributedString:newAttachment];
  188. [testText appendAttributedString:self.spaceAttachment];
  189. }
  190. if ([RQ_YDTQuestion_Module isNewRuleQuestionWithQuestionId:exerciseModel.ydtQuestionModel.ID]) {
  191. NSMutableAttributedString *newAttachment = nil;
  192. QMUIButton *newBtn = [QMUIButton buttonWithType:UIButtonTypeCustom];
  193. newBtn.size = btnSize;
  194. [newBtn setTitle:@"新规题" textColor:RQ_MAIN_TEXT_COLOR_WHITE Font:RQ_Exercise_Module.exerciseFontSize - 3 fotState:UIControlStateNormal];
  195. [newBtn setBackgroundImage:[UIImage qmui_imageWithColor:RQColorFromHexString(@"#9C5FED") size:btnSize cornerRadiusArray:@[@(btnCornerRadius), @(btnCornerRadius), @(btnCornerRadius), @(btnCornerRadius)]] forState:UIControlStateNormal];
  196. newAttachment = [NSMutableAttributedString yy_attachmentStringWithContent:newBtn
  197. contentMode:UIViewContentModeCenter
  198. attachmentSize:btnSize
  199. alignToFont:[UIFont systemFontOfSize:RQ_Exercise_Module.exerciseFontSize]
  200. alignment:YYTextVerticalAlignmentCenter];
  201. [testText appendAttributedString:newAttachment];
  202. [testText appendAttributedString:self.spaceAttachment];
  203. }
  204. NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:questionStr];
  205. attrString.yy_font = [UIFont systemFontOfSize:RQ_Exercise_Module.exerciseFontSize];
  206. attrString.yy_color = RQ_MAIN_TEXT_COLOR_1;
  207. // if (RQStringIsNotEmpty(exerciseModel.titlekeyword) && exerciseModel.isShowSkillExplanation) {
  208. // [[[exerciseModel.titlekeyword componentsSeparatedByString:@"-"].rac_sequence.signal deliverOnMainThread] subscribeNext:^(NSString * _Nullable skillkeywordStr) {
  209. // NSRange range = [questionStr rangeOfString:skillkeywordStr];
  210. // [attrString yy_setColor:RQ_MAIN_TEXT_COLOR_RED range:range];
  211. // [attrString yy_setFont:[UIFont boldSystemFontOfSize:RQ_Exercise_Module.exerciseFontSize + 2] range:range];
  212. // }];
  213. // }
  214. // NSRange range = [string rangeOfString:@"《遵守实验室须知》"];
  215. // [attrString yy_setColor:[UIColor colorWithHexString:@"#19ADA9"] range:range];
  216. //
  217. // [attrString yy_setTextHighlightRange:range
  218. // color:[UIColor colorWithHexString:@"#19ADA9"]
  219. // backgroundColor:[UIColor blueColor]
  220. // tapAction:^(UIView * _Nonnull containerView, NSAttributedString * _Nonnull text, NSRange range, CGRect rect) {
  221. // [_self showMessage:[NSString stringWithFormat:@"Tap: %@",[text.string substringWithRange:range]]];
  222. // }];
  223. [testText appendAttributedString: attrString];
  224. if (RQ_COMMON_MANAGER.JSJP_APP_VOICE) {//读题-是否开启
  225. //读题
  226. QMUIButton *readBtn = [QMUIButton buttonWithType:UIButtonTypeCustom];
  227. CGFloat ebtnH = 20.f;
  228. CGFloat ebtnW = 58.f;
  229. CGSize ebtnSize = CGSizeMake(ebtnW, ebtnH);
  230. readBtn.size = btnSize;
  231. [readBtn setImage:RQImageNamed(@"读题pay") forState:UIControlStateNormal];
  232. [readBtn setImage:RQImageNamed(@"读题pause") forState:UIControlStateSelected];
  233. [readBtn addTarget:self action:@selector(buttonActionPayVoicedo:) forControlEvents:UIControlEventTouchUpInside];
  234. NSMutableAttributedString *readAttachment = [NSMutableAttributedString yy_attachmentStringWithContent:readBtn
  235. contentMode:UIViewContentModeCenter
  236. attachmentSize:ebtnSize
  237. alignToFont:[UIFont systemFontOfSize:RQ_Exercise_Module.exerciseFontSize]
  238. alignment:YYTextVerticalAlignmentCenter];
  239. self.readBtn = readBtn;
  240. [testText appendAttributedString:self.spaceAttachment];
  241. [testText appendAttributedString:readAttachment];
  242. }
  243. [text appendAttributedString:testText];
  244. // [text appendAttributedString:[self padding]];
  245. text.yy_lineSpacing = 8;
  246. return text;
  247. }
  248. /**
  249. * 获取lb的高度
  250. * @param mess lb.text
  251. * @param lb (YYLabel *)label
  252. * @return lb的高度
  253. */
  254. - (CGFloat)getMessageHeight:(NSString *)mess andLabel:(YYLabel *)lb {
  255. NSMutableAttributedString *introText = [[NSMutableAttributedString alloc] initWithString:mess];
  256. introText.yy_font = [UIFont boldSystemFontOfSize:RQ_Exercise_Module.exerciseFontSize];
  257. introText.yy_lineSpacing = 8;
  258. lb.attributedText = introText;
  259. CGSize introSize = CGSizeMake(RQ_SCREEN_WIDTH - (16 * 2), CGFLOAT_MAX);
  260. YYTextLayout *layout = [YYTextLayout layoutWithContainerSize:introSize text:introText];
  261. lb.textLayout = layout;
  262. CGFloat introHeight = layout.textBoundingSize.height;
  263. return introHeight;
  264. }
  265. - (CGFloat)getMessageHeightWithAttributedStr:(NSMutableAttributedString *)attributedStr andLabel:(YYLabel *)lb {
  266. lb.attributedText = attributedStr;
  267. CGSize introSize = CGSizeMake(RQ_SCREEN_WIDTH - (16 * 2), CGFLOAT_MAX);
  268. YYTextLayout *layout = [YYTextLayout layoutWithContainerSize:introSize text:attributedStr];
  269. lb.textLayout = layout;
  270. CGFloat introHeight = layout.textBoundingSize.height;
  271. return introHeight;
  272. }
  273. - (YYLabel *)yyLabel {
  274. if (!_yyLabel) {
  275. _yyLabel = [[YYLabel alloc] init];
  276. }
  277. return _yyLabel;
  278. }
  279. - (NSMutableAttributedString *)spaceAttachment {
  280. if (!_spaceAttachment) {
  281. UIView *spaceView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 4, 25)];
  282. _spaceAttachment = [NSMutableAttributedString yy_attachmentStringWithContent:spaceView contentMode:UIViewContentModeCenter attachmentSize:CGSizeMake(4, 25) alignToFont:[UIFont boldSystemFontOfSize:RQ_Exercise_Module.exerciseFontSize] alignment:YYTextVerticalAlignmentCenter];
  283. }
  284. return _spaceAttachment;
  285. }
  286. - (NSString *)itemClassName {
  287. return @"RQExerciseQuestionCell";
  288. }
  289. #pragma mark -事件
  290. - (void)buttonActionPayVoicedo:(UIButton *)btn
  291. {
  292. btn.userInteractionEnabled = NO;
  293. btn.selected = !btn.isSelected;
  294. if(btn.selected){//播放
  295. if (NY_VOICE_MANAGER.state == NYVoiceType_Pause){
  296. [NY_VOICE_MANAGER resumePayVoiceActionUrl];//恢复
  297. }else {
  298. // self.exerciseModel.ydtQuestionModel.Question
  299. NSString *Question = self.exerciseModel.ydtQuestionModel.Question;
  300. WeakSelf(weakSelf)
  301. NY_VOICE_MANAGER.paytext = Question;
  302. [NY_VOICE_MANAGER stopPayVoiceActionUrl];
  303. NSString *url = self.exerciseModel.ydtQuestionModel.question_audio;
  304. [NY_VOICE_MANAGER startPayVoiceActionUrl:url completedBlock:^(NYVoiceType voiceType) {
  305. switch (voiceType) {
  306. case NYVoiceType_Start:{
  307. if (![weakSelf.exerciseModel.userAnswer containsObject:@"预选Option"]&&
  308. weakSelf.exerciseModel.userAnswer.count) {//未答题
  309. weakSelf.readCount++;
  310. }
  311. weakSelf.readBtn.selected = YES;
  312. }break;
  313. case NYVoiceType_Wait:{//完成-等待
  314. if ([weakSelf.exerciseModel.userAnswer containsObject:@"预选Option"]||
  315. weakSelf.exerciseModel.userAnswer.count==0) {//未答题
  316. weakSelf.readBtn.selected = NO;
  317. }else{//已答题
  318. if(weakSelf.readCount==2){
  319. weakSelf.readBtn.selected = NO;
  320. }else if(weakSelf.readCount==1){
  321. if(RQStringIsNotEmpty(weakSelf.exerciseModel.ydtQuestionModel.answer_audio)){
  322. //播放选项
  323. [NY_VOICE_MANAGER startPayVoiceActionUrl:weakSelf.exerciseModel.ydtQuestionModel.answer_audio];
  324. }else{
  325. weakSelf.readBtn.selected = NO;
  326. }
  327. }
  328. }
  329. }break;
  330. case NYVoiceType_Resume:{//恢复
  331. weakSelf.readBtn.selected = YES;
  332. }break;
  333. case NYVoiceType_Pause:{//暂停
  334. weakSelf.readBtn.selected = NO;
  335. }break;
  336. case NYVoiceType_Stop:{//停止
  337. weakSelf.readBtn.selected = NO;
  338. }break;
  339. default:
  340. break;
  341. }
  342. }];
  343. }
  344. }else {//暂停
  345. [NY_VOICE_MANAGER pausePayVoiceActionUrl];
  346. }
  347. //防止频繁点击 1 秒点一次
  348. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  349. self.readBtn.userInteractionEnabled = YES;
  350. });
  351. }
  352. @end