TIMCommonModel.m 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437
  1. //
  2. // TIMCommonModel.m
  3. // TIMCommon
  4. //
  5. // Created by cologne on 2023/3/14.
  6. // Copyright © 2023 Tencent. All rights reserved.
  7. //
  8. #import "TIMCommonModel.h"
  9. /////////////////////////////////////////////////////////////////////////////////
  10. //
  11. // TUIPopView
  12. //
  13. /////////////////////////////////////////////////////////////////////////////////
  14. @interface TUIPopView () <UITableViewDelegate, UITableViewDataSource, UIGestureRecognizerDelegate>
  15. @property(nonatomic, strong) NSMutableArray *data;
  16. @end
  17. @implementation TUIPopView
  18. - (id)initWithFrame:(CGRect)frame {
  19. self = [super initWithFrame:frame];
  20. if (self) {
  21. [self setupViews];
  22. }
  23. return self;
  24. }
  25. - (void)setData:(NSMutableArray *)data {
  26. _data = data;
  27. [_tableView reloadData];
  28. }
  29. - (void)showInWindow:(UIWindow *)window {
  30. [window addSubview:self];
  31. __weak typeof(self) ws = self;
  32. self.alpha = 0;
  33. [UIView animateWithDuration:0.25
  34. delay:0
  35. options:UIViewAnimationOptionCurveEaseOut
  36. animations:^{
  37. ws.alpha = 1;
  38. }
  39. completion:nil];
  40. }
  41. - (void)setupViews {
  42. UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)];
  43. tap.delegate = self;
  44. [self addGestureRecognizer:tap];
  45. UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)];
  46. [self addGestureRecognizer:pan];
  47. self.backgroundColor = [UIColor clearColor];
  48. CGSize arrowSize = TUIPopView_Arrow_Size;
  49. _tableView = [[UITableView alloc] initWithFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y + arrowSize.height, self.frame.size.width,
  50. self.frame.size.height - arrowSize.height)];
  51. self.frame = [UIScreen mainScreen].bounds;
  52. _tableView.delegate = self;
  53. _tableView.dataSource = self;
  54. _tableView.backgroundColor = TUIDemoDynamicColor(@"pop_bg_color", @"#FFFFFF");
  55. _tableView.tableFooterView = [[UIView alloc] init];
  56. _tableView.scrollEnabled = NO;
  57. _tableView.layer.cornerRadius = 5.0;
  58. [self addSubview:_tableView];
  59. }
  60. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  61. return _data.count;
  62. }
  63. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  64. return [TUIPopCell getHeight];
  65. }
  66. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  67. TUIPopCell *cell = [tableView dequeueReusableCellWithIdentifier:TUIPopCell_ReuseId];
  68. if (!cell) {
  69. cell = [[TUIPopCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TUIPopCell_ReuseId];
  70. }
  71. [cell setData:_data[indexPath.row]];
  72. if (indexPath.row == _data.count - 1) {
  73. cell.separatorInset = UIEdgeInsetsMake(0, self.bounds.size.width, 0, 0);
  74. }
  75. return cell;
  76. }
  77. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  78. [tableView deselectRowAtIndexPath:indexPath animated:NO];
  79. if (_delegate && [_delegate respondsToSelector:@selector(popView:didSelectRowAtIndex:)]) {
  80. [_delegate popView:self didSelectRowAtIndex:indexPath.row];
  81. }
  82. [self hide];
  83. }
  84. - (void)drawRect:(CGRect)rect {
  85. [[UIColor whiteColor] set];
  86. CGSize arrowSize = TUIPopView_Arrow_Size;
  87. UIBezierPath *arrowPath = [[UIBezierPath alloc] init];
  88. [arrowPath moveToPoint:_arrowPoint];
  89. [arrowPath addLineToPoint:CGPointMake(_arrowPoint.x + arrowSize.width * 0.5, _arrowPoint.y + arrowSize.height)];
  90. [arrowPath addLineToPoint:CGPointMake(_arrowPoint.x - arrowSize.width * 0.5, _arrowPoint.y + arrowSize.height)];
  91. [arrowPath closePath];
  92. [arrowPath fill];
  93. }
  94. - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
  95. if ([NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"]) {
  96. return NO;
  97. }
  98. return YES;
  99. }
  100. - (void)onTap:(UIGestureRecognizer *)recognizer {
  101. [self hide];
  102. }
  103. - (void)hide {
  104. __weak typeof(self) ws = self;
  105. self.alpha = 1;
  106. [UIView animateWithDuration:0.25
  107. delay:0
  108. options:UIViewAnimationOptionCurveEaseOut
  109. animations:^{
  110. ws.alpha = 0;
  111. }
  112. completion:^(BOOL finished) {
  113. if ([ws superview]) {
  114. [ws removeFromSuperview];
  115. }
  116. }];
  117. }
  118. @end
  119. @implementation TUIPopCellData
  120. @end
  121. @implementation TUIPopCell
  122. - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  123. self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  124. if (self) {
  125. [self setupViews];
  126. }
  127. return self;
  128. }
  129. - (void)setupViews {
  130. self.backgroundColor = [UIColor clearColor];
  131. _image = [[UIImageView alloc] init];
  132. _image.contentMode = UIViewContentModeScaleAspectFit;
  133. [self addSubview:_image];
  134. _title = [[UILabel alloc] init];
  135. _title.font = [UIFont systemFontOfSize:15];
  136. _title.textColor = TUIDemoDynamicColor(@"pop_text_color", @"#444444");
  137. _title.numberOfLines = 0;
  138. [self addSubview:_title];
  139. [self setSeparatorInset:UIEdgeInsetsMake(0, TUIPopCell_Padding, 0, 0)];
  140. }
  141. - (void)layoutSubviews {
  142. CGFloat headHeight = TUIPopCell_Height - 2 * TUIPopCell_Padding;
  143. self.image.frame = CGRectMake(TUIPopCell_Padding, TUIPopCell_Padding, headHeight, headHeight);
  144. self.image.center = CGPointMake(self.image.center.x, self.contentView.center.y);
  145. CGFloat titleWidth = self.frame.size.width - 2 * TUIPopCell_Padding - TUIPopCell_Margin - _image.frame.size.width;
  146. self.title.frame =
  147. CGRectMake(_image.frame.origin.x + _image.frame.size.width + TUIPopCell_Margin, TUIPopCell_Padding, titleWidth, self.contentView.bounds.size.height);
  148. self.title.center = CGPointMake(self.title.center.x, self.contentView.center.y);
  149. if (isRTL()) {
  150. [self.image resetFrameToFitRTL];
  151. [self.title resetFrameToFitRTL];
  152. }
  153. }
  154. - (void)setData:(TUIPopCellData *)data {
  155. _image.image = data.image;
  156. _title.text = data.title;
  157. }
  158. + (CGFloat)getHeight {
  159. return TUIPopCell_Height;
  160. }
  161. @end
  162. /////////////////////////////////////////////////////////////////////////////////
  163. //
  164. // TUIModifyView
  165. //
  166. /////////////////////////////////////////////////////////////////////////////////
  167. #define kContainerWidth Screen_Width
  168. #define kContainerHeight kContainerWidth * 3 / 4
  169. @implementation TUIModifyViewData
  170. - (instancetype)init {
  171. if (self = [super init]) {
  172. self.enableNull = NO;
  173. }
  174. return self;
  175. }
  176. @end
  177. @interface TUIModifyView () <UITextFieldDelegate, UIGestureRecognizerDelegate>
  178. @property(nonatomic, assign) BOOL keyboardShowing;
  179. @property(nonatomic, strong) TUIModifyViewData *data;
  180. @property(nonatomic, strong) UIButton *closeBtn;
  181. @end
  182. @implementation TUIModifyView
  183. - (id)init {
  184. self = [super init];
  185. if (self) {
  186. [self setupViews];
  187. }
  188. return self;
  189. }
  190. - (void)setupViews {
  191. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
  192. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
  193. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
  194. self.frame = [UIScreen mainScreen].bounds;
  195. UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)];
  196. tap.delegate = self;
  197. [self addGestureRecognizer:tap];
  198. self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
  199. _container = [[UIView alloc] initWithFrame:CGRectMake(0, Screen_Height, kContainerWidth, kContainerHeight)];
  200. _container.backgroundColor = TUIContactDynamicColor(@"group_modify_container_view_bg_color", @"#FFFFFF");
  201. _container.layer.cornerRadius = 8;
  202. [_container.layer setMasksToBounds:YES];
  203. [self addSubview:_container];
  204. CGFloat buttonHeight = 46;
  205. CGFloat titleHeight = 63;
  206. _title = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, _container.frame.size.width, titleHeight)];
  207. _title.font = [UIFont fontWithName:@"PingFangSC-Medium" size:17];
  208. _title.textColor = TUIContactDynamicColor(@"group_modify_title_color", @"#000000");
  209. _title.textAlignment = NSTextAlignmentCenter;
  210. [_container addSubview:_title];
  211. _hLine = [[UIView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(_title.frame), kContainerWidth, TLine_Heigh)];
  212. _hLine.backgroundColor = TIMCommonDynamicColor(@"separator_color", @"#E4E5E9");
  213. [_container addSubview:_hLine];
  214. CGFloat contentMargin = 20;
  215. CGFloat contentWidth = _container.frame.size.width - 2 * contentMargin;
  216. CGFloat contentY = CGRectGetMaxY(_hLine.frame) + 17;
  217. CGFloat contentheight = 40;
  218. _content = [[UITextField alloc] initWithFrame:CGRectMake(contentMargin, contentY, contentWidth, contentheight)];
  219. _content.textAlignment = isRTL()?NSTextAlignmentRight:NSTextAlignmentLeft;
  220. _content.delegate = self;
  221. _content.backgroundColor = TUIContactDynamicColor(@"group_modify_input_bg_color", @"#F5F5F5");
  222. _content.textColor = TUIContactDynamicColor(@"group_modify_input_text_color", @"#000000");
  223. [_content setFont:[UIFont systemFontOfSize:16]];
  224. [_content.layer setMasksToBounds:YES];
  225. [_content.layer setCornerRadius:4.0f];
  226. [_content setReturnKeyType:UIReturnKeyDone];
  227. [_content addTarget:self action:@selector(textChanged) forControlEvents:UIControlEventEditingChanged];
  228. CGRect leftviewFrame = _content.frame;
  229. leftviewFrame.size.width = 16;
  230. UIView *leftview = [[UIView alloc] initWithFrame:leftviewFrame];
  231. _content.leftView = leftview;
  232. _content.leftViewMode = UITextFieldViewModeAlways;
  233. CGRect rightviewFrame = _content.frame;
  234. rightviewFrame.size.width = 16;
  235. rightviewFrame.origin.x = rightviewFrame.size.width - 16;
  236. UIView *rightView = [[UIView alloc] initWithFrame:rightviewFrame];
  237. _content.rightView = rightView;
  238. _content.rightViewMode = UITextFieldViewModeAlways;
  239. [_container addSubview:_content];
  240. _descLabel = [[UILabel alloc] initWithFrame:CGRectMake(_content.frame.origin.x, CGRectGetMaxY(_content.frame) + 17, contentWidth, 20)];
  241. _descLabel.textColor = TUIContactDynamicColor(@"group_modify_desc_color", @"#888888");
  242. _descLabel.font = [UIFont systemFontOfSize:13.0];
  243. _descLabel.numberOfLines = 0;
  244. _descLabel.text = @"desc";
  245. [_container addSubview:_descLabel];
  246. _confirm = [[UIButton alloc] initWithFrame:CGRectMake(_content.frame.origin.x, CGRectGetMaxY(_descLabel.frame) + 30, contentWidth, buttonHeight)];
  247. [_confirm setTitle:TIMCommonLocalizableString(Confirm) forState:UIControlStateNormal];
  248. [_confirm setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  249. _confirm.titleLabel.font = [UIFont systemFontOfSize:15];
  250. _confirm.layer.cornerRadius = 8;
  251. _confirm.layer.masksToBounds = YES;
  252. _confirm.imageView.contentMode = UIViewContentModeScaleToFill;
  253. [self enableConfirmButton:self.data.enableNull];
  254. [_confirm addTarget:self action:@selector(didConfirm:) forControlEvents:UIControlEventTouchUpInside];
  255. [_container addSubview:_confirm];
  256. _closeBtn = [[UIButton alloc] initWithFrame:CGRectMake(_container.frame.size.width - 24 - 20, 0, 24, 24)];
  257. _closeBtn.mm__centerY(_title.mm_centerY);
  258. [_closeBtn setImage:[UIImage imageNamed:TUIContactImagePath(@"ic_close_poppings")] forState:UIControlStateNormal];
  259. [_closeBtn addTarget:self action:@selector(didCancel:) forControlEvents:UIControlEventTouchUpInside];
  260. [_container addSubview:_closeBtn];
  261. }
  262. - (void)setData:(TUIModifyViewData *)data {
  263. _title.text = data.title;
  264. _content.text = data.content;
  265. _descLabel.text = data.desc;
  266. _data = data;
  267. CGRect rect = [data.desc boundingRectWithSize:CGSizeMake(self.content.bounds.size.width, CGFLOAT_MAX)
  268. options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
  269. attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:13.0]}
  270. context:nil];
  271. CGRect frame = _descLabel.frame;
  272. frame.size.height = rect.size.height;
  273. _descLabel.frame = frame;
  274. [self textChanged];
  275. }
  276. - (void)showInWindow:(UIWindow *)window {
  277. [window addSubview:self];
  278. [self layoutIfNeeded];
  279. CGFloat height = CGRectGetMaxY(self.confirm.frame) + 50;
  280. __weak typeof(self) ws = self;
  281. [UIView animateWithDuration:0.25
  282. delay:0
  283. options:UIViewAnimationOptionCurveEaseOut
  284. animations:^{
  285. ws.container.frame = CGRectMake(0, Screen_Height - height, kContainerWidth, height);
  286. }
  287. completion:nil];
  288. }
  289. - (void)onTap:(UIGestureRecognizer *)recognizer {
  290. [_content resignFirstResponder];
  291. if (!self.keyboardShowing) {
  292. [self hide];
  293. }
  294. }
  295. - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
  296. return [touch.view isEqual:self];
  297. }
  298. - (void)hide {
  299. __weak typeof(self) ws = self;
  300. self.alpha = 1;
  301. [UIView animateWithDuration:0.25
  302. delay:0
  303. options:UIViewAnimationOptionCurveEaseOut
  304. animations:^{
  305. ws.alpha = 0;
  306. }
  307. completion:^(BOOL finished) {
  308. [[NSNotificationCenter defaultCenter] removeObserver:ws];
  309. if ([ws superview]) {
  310. [ws removeFromSuperview];
  311. }
  312. }];
  313. }
  314. - (void)didCancel:(UIButton *)sender {
  315. [self hide];
  316. }
  317. - (void)didConfirm:(UIButton *)sender {
  318. if (_delegate && [_delegate respondsToSelector:@selector(modifyView:didModiyContent:)]) {
  319. [_delegate modifyView:self didModiyContent:_content.text];
  320. }
  321. [self hide];
  322. }
  323. - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
  324. if ([text isEqualToString:@"\n"]) {
  325. [textView resignFirstResponder];
  326. return NO;
  327. }
  328. return YES;
  329. }
  330. - (void)textChanged {
  331. [self enableConfirmButton:(self.content.text.length || self.data.enableNull)];
  332. }
  333. - (void)keyboardWillChangeFrame:(NSNotification *)notification {
  334. CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
  335. self.keyboardShowing = keyboardFrame.size.height > 0;
  336. [self animateContainer:keyboardFrame.size.height];
  337. }
  338. - (void)keyboardWillHide:(NSNotification *)notification {
  339. [self animateContainer:0];
  340. }
  341. - (void)keyboardDidHide:(NSNotification *)notice {
  342. self.keyboardShowing = NO;
  343. }
  344. - (void)animateContainer:(CGFloat)keyboardHeight {
  345. CGFloat height = CGRectGetMaxY(self.confirm.frame) + 50;
  346. CGRect frame = _container.frame;
  347. frame.origin.y = Screen_Height - height - keyboardHeight; //(self.frame.size.height - keyboardHeight - frame.size.height) * 0.5;
  348. __weak typeof(self) ws = self;
  349. [UIView animateWithDuration:0.3
  350. delay:0
  351. options:UIViewAnimationOptionCurveEaseOut
  352. animations:^{
  353. ws.container.frame = frame;
  354. }
  355. completion:nil];
  356. }
  357. - (void)enableConfirmButton:(BOOL)enable {
  358. if (enable) {
  359. _confirm.backgroundColor = TUIContactDynamicColor(@"group_modify_confirm_enable_bg_color", @"147AFF");
  360. _confirm.enabled = YES;
  361. } else {
  362. _confirm.backgroundColor = [TUIContactDynamicColor(@"group_modify_confirm_enable_bg_color", @"147AFF") colorWithAlphaComponent:0.3];
  363. _confirm.enabled = NO;
  364. }
  365. }
  366. @end
  367. /////////////////////////////////////////////////////////////////////////////////
  368. //
  369. // TUINaviBarIndicatorView
  370. //
  371. /////////////////////////////////////////////////////////////////////////////////
  372. @implementation TUINaviBarIndicatorView
  373. - (id)init {
  374. self = [super init];
  375. if (self) {
  376. [self setupViews];
  377. }
  378. return self;
  379. }
  380. - (void)setupViews {
  381. _indicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
  382. _indicator.center = CGPointMake(0, NavBar_Height * 0.5);
  383. _indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
  384. [self addSubview:_indicator];
  385. _label = [[UILabel alloc] init];
  386. _label.backgroundColor = [UIColor clearColor];
  387. _label.font = [UIFont boldSystemFontOfSize:17];
  388. _label.textColor = TIMCommonDynamicColor(@"nav_title_text_color", @"#000000");
  389. [self addSubview:_label];
  390. _maxLabelLength = 150;
  391. }
  392. - (void)setTitle:(NSString *)title {
  393. _label.textColor = TIMCommonDynamicColor(@"nav_title_text_color", @"#000000");
  394. _label.text = title;
  395. [self updateLayout];
  396. }
  397. - (void)updateLayout {
  398. [_label sizeToFit];
  399. CGSize labelSize = _label.bounds.size; // [_label sizeThatFits:CGSizeMake(Screen_Width, NavBar_Height)];
  400. CGFloat labelWidth = MIN(labelSize.width, _maxLabelLength);
  401. CGFloat labelY = 0;
  402. CGFloat labelX = _indicator.hidden ? 0 : (_indicator.frame.origin.x + _indicator.frame.size.width + TUINaviBarIndicatorView_Margin);
  403. _label.frame = CGRectMake(labelX, labelY, labelWidth, NavBar_Height);
  404. self.frame = CGRectMake(0, 0, labelX + labelWidth + TUINaviBarIndicatorView_Margin, NavBar_Height);
  405. // self.center = CGPointMake(Screen_Width * 0.5, NavBar_Height * 0.5);
  406. }
  407. - (void)startAnimating {
  408. [_indicator startAnimating];
  409. }
  410. - (void)stopAnimating {
  411. [_indicator stopAnimating];
  412. }
  413. @end
  414. /////////////////////////////////////////////////////////////////////////////////
  415. //
  416. // TUICommonCell & data
  417. //
  418. /////////////////////////////////////////////////////////////////////////////////
  419. @implementation TUICommonCellData
  420. - (CGFloat)heightOfWidth:(CGFloat)width {
  421. return 60;
  422. }
  423. - (CGFloat)estimatedHeight {
  424. return 60;
  425. }
  426. @end
  427. @interface TUICommonTableViewCell () <UIGestureRecognizerDelegate>
  428. @property TUICommonCellData *data;
  429. @property UITapGestureRecognizer *tapRecognizer;
  430. @end
  431. @implementation TUICommonTableViewCell
  432. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  433. self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  434. if (self) {
  435. _tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGesture:)];
  436. _tapRecognizer.delegate = self;
  437. _tapRecognizer.cancelsTouchesInView = NO;
  438. self.backgroundColor = TIMCommonDynamicColor(@"form_bg_color", @"#FFFFFF");
  439. self.contentView.backgroundColor = TIMCommonDynamicColor(@"form_bg_color", @"#FFFFFF");
  440. }
  441. return self;
  442. }
  443. - (void)tapGesture:(UIGestureRecognizer *)gesture {
  444. if (self.data.cselector) {
  445. UIViewController *vc = self.mm_viewController;
  446. if ([vc respondsToSelector:self.data.cselector]) {
  447. self.selected = YES;
  448. #pragma clang diagnostic push
  449. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  450. [vc performSelector:self.data.cselector withObject:self];
  451. #pragma clang diagnostic pop
  452. }
  453. }
  454. }
  455. - (void)fillWithData:(TUICommonCellData *)data {
  456. self.data = data;
  457. if (data.cselector) {
  458. [self addGestureRecognizer:self.tapRecognizer];
  459. } else {
  460. [self removeGestureRecognizer:self.tapRecognizer];
  461. }
  462. }
  463. @end
  464. /////////////////////////////////////////////////////////////////////////////////
  465. //
  466. // TUICommonTextCell & data
  467. //
  468. /////////////////////////////////////////////////////////////////////////////////
  469. @implementation TUICommonTextCellData
  470. - (instancetype)init {
  471. self = [super init];
  472. self.keyEdgeInsets = UIEdgeInsetsMake(0, 20, 0, 0);
  473. return self;
  474. }
  475. - (CGFloat)heightOfWidth:(CGFloat)width {
  476. CGFloat height = [super heightOfWidth:width];
  477. if (self.enableMultiLineValue) {
  478. NSString *str = self.value;
  479. NSDictionary *attribute = @{NSFontAttributeName : [UIFont systemFontOfSize:16]};
  480. CGSize size = [str boundingRectWithSize:CGSizeMake(280, 999)
  481. options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
  482. attributes:attribute
  483. context:nil]
  484. .size;
  485. height = size.height + 30;
  486. }
  487. return height;
  488. }
  489. @end
  490. @interface TUICommonTextCell ()
  491. @property TUICommonTextCellData *textData;
  492. @end
  493. @implementation TUICommonTextCell
  494. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  495. if (self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier]) {
  496. self.backgroundColor = TIMCommonDynamicColor(@"form_bg_color", @"#FFFFFF");
  497. self.contentView.backgroundColor = TIMCommonDynamicColor(@"form_bg_color", @"#FFFFFF");
  498. _keyLabel = [[UILabel alloc] init];
  499. _keyLabel.textColor = TIMCommonDynamicColor(@"form_key_text_color", @"#444444");
  500. _keyLabel.font = [UIFont systemFontOfSize:16.0];
  501. [self.contentView addSubview:_keyLabel];
  502. [_keyLabel setRtlAlignment:TUITextRTLAlignmentTrailing];
  503. _valueLabel = [[UILabel alloc] init];
  504. [self.contentView addSubview:_valueLabel];
  505. _valueLabel.textColor = TIMCommonDynamicColor(@"form_value_text_color", @"#000000");
  506. _valueLabel.font = [UIFont systemFontOfSize:16.0];
  507. [_valueLabel setRtlAlignment:TUITextRTLAlignmentTrailing];
  508. self.selectionStyle = UITableViewCellSelectionStyleNone;
  509. }
  510. return self;
  511. }
  512. - (void)fillWithData:(TUICommonTextCellData *)textData {
  513. [super fillWithData:textData];
  514. self.textData = textData;
  515. RAC(_keyLabel, text) = [RACObserve(textData, key) takeUntil:self.rac_prepareForReuseSignal];
  516. RAC(_valueLabel, text) = [RACObserve(textData, value) takeUntil:self.rac_prepareForReuseSignal];
  517. if (textData.showAccessory) {
  518. self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  519. } else {
  520. self.accessoryType = UITableViewCellAccessoryNone;
  521. }
  522. if (self.textData.keyColor) {
  523. self.keyLabel.textColor = self.textData.keyColor;
  524. }
  525. if (self.textData.valueColor) {
  526. self.valueLabel.textColor = self.textData.valueColor;
  527. }
  528. if (self.textData.enableMultiLineValue) {
  529. self.valueLabel.numberOfLines = 0;
  530. } else {
  531. self.valueLabel.numberOfLines = 1;
  532. }
  533. // tell constraints they need updating
  534. [self setNeedsUpdateConstraints];
  535. // update constraints now so we can animate the change
  536. [self updateConstraintsIfNeeded];
  537. [self layoutIfNeeded];
  538. }
  539. + (BOOL)requiresConstraintBasedLayout {
  540. return YES;
  541. }
  542. // this is Apple's recommended place for adding/updating constraints
  543. - (void)updateConstraints {
  544. [super updateConstraints];
  545. [self.keyLabel sizeToFit];
  546. [self.keyLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
  547. make.size.mas_equalTo(self.keyLabel.frame.size);
  548. make.leading.mas_equalTo(self.contentView).mas_offset(self.textData.keyEdgeInsets.left);
  549. make.centerY.mas_equalTo(self.contentView);
  550. }];
  551. [self.valueLabel sizeToFit];
  552. [self.valueLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
  553. make.leading.mas_equalTo(self.keyLabel.mas_trailing).mas_offset(10);
  554. if (self.textData.showAccessory) {
  555. make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(-10);
  556. }
  557. else {
  558. make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(-20);
  559. }
  560. make.centerY.mas_equalTo(self.contentView);
  561. }];
  562. }
  563. @end
  564. /////////////////////////////////////////////////////////////////////////////////
  565. //
  566. // TUICommonSwitchCell & data
  567. //
  568. /////////////////////////////////////////////////////////////////////////////////
  569. @implementation TUICommonSwitchCellData
  570. - (instancetype)init {
  571. self = [super init];
  572. _margin = 20;
  573. return self;
  574. }
  575. - (CGFloat)heightOfWidth:(CGFloat)width {
  576. CGFloat height = [super heightOfWidth:width];
  577. if (self.desc.length > 0) {
  578. NSString *str = self.desc;
  579. NSDictionary *attribute = @{NSFontAttributeName : [UIFont systemFontOfSize:12]};
  580. CGSize size = [str boundingRectWithSize:CGSizeMake(264, 999)
  581. options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
  582. attributes:attribute
  583. context:nil]
  584. .size;
  585. height += size.height + 10;
  586. }
  587. return height;
  588. }
  589. @end
  590. @interface TUICommonSwitchCell ()
  591. @property TUICommonSwitchCellData *switchData;
  592. @property(nonatomic, strong) UIView *leftSeparatorLine;
  593. @end
  594. @implementation TUICommonSwitchCell
  595. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  596. if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
  597. _titleLabel = [[UILabel alloc] init];
  598. _titleLabel.textColor = TIMCommonDynamicColor(@"form_key_text_color", @"#444444");
  599. _titleLabel.font = [UIFont systemFontOfSize:16];
  600. [_titleLabel setRtlAlignment:TUITextRTLAlignmentLeading];
  601. [self.contentView addSubview:_titleLabel];
  602. _descLabel = [[UILabel alloc] init];
  603. _descLabel.textColor = TIMCommonDynamicColor(@"group_modify_desc_color", @"#888888");
  604. _descLabel.font = [UIFont systemFontOfSize:12];
  605. _descLabel.numberOfLines = 0;
  606. [_descLabel setRtlAlignment:TUITextRTLAlignmentLeading];
  607. _descLabel.hidden = YES;
  608. [self.contentView addSubview:_descLabel];
  609. _switcher = [[UISwitch alloc] init];
  610. _switcher.onTintColor = TIMCommonDynamicColor(@"common_switch_on_color", @"#147AFF");
  611. self.accessoryView = _switcher;
  612. [self.contentView addSubview:_switcher];
  613. [_switcher addTarget:self action:@selector(switchClick) forControlEvents:UIControlEventValueChanged];
  614. _leftSeparatorLine = [[UIView alloc] init];
  615. _leftSeparatorLine.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:0.3];
  616. [self.contentView addSubview:_leftSeparatorLine];
  617. self.selectionStyle = UITableViewCellSelectionStyleNone;
  618. }
  619. return self;
  620. }
  621. - (void)layoutSubviews {
  622. [super layoutSubviews];
  623. }
  624. - (void)fillWithData:(TUICommonSwitchCellData *)switchData {
  625. [super fillWithData:switchData];
  626. self.switchData = switchData;
  627. _titleLabel.text = switchData.title;
  628. [_switcher setOn:switchData.isOn];
  629. _descLabel.text = switchData.desc;
  630. // tell constraints they need updating
  631. [self setNeedsUpdateConstraints];
  632. // update constraints now so we can animate the change
  633. [self updateConstraintsIfNeeded];
  634. [self layoutIfNeeded];
  635. }
  636. + (BOOL)requiresConstraintBasedLayout {
  637. return YES;
  638. }
  639. // this is Apple's recommended place for adding/updating constraints
  640. - (void)updateConstraints {
  641. [super updateConstraints];
  642. if (self.switchData.disableChecked) {
  643. _titleLabel.textColor = [UIColor grayColor];
  644. _titleLabel.alpha = 0.4;
  645. _switcher.alpha = 0.4;
  646. self.userInteractionEnabled = NO;
  647. } else {
  648. _titleLabel.alpha = 1;
  649. _switcher.alpha = 1;
  650. _titleLabel.textColor = TIMCommonDynamicColor(@"form_key_text_color", @"#444444");
  651. _switcher.onTintColor = TIMCommonDynamicColor(@"common_switch_on_color", @"#147AFF");
  652. self.userInteractionEnabled = YES;
  653. }
  654. CGFloat leftMargin = 0;
  655. CGFloat padding = 5;
  656. if (self.switchData.displaySeparatorLine) {
  657. _leftSeparatorLine.mm_width(10).mm_height(2).mm_left(self.switchData.margin).mm__centerY(self.contentView.mm_h / 2);
  658. leftMargin = self.switchData.margin + _leftSeparatorLine.mm_w + padding;
  659. } else {
  660. _leftSeparatorLine.mm_width(0).mm_height(0);
  661. leftMargin = self.switchData.margin;
  662. }
  663. if (self.switchData.desc.length > 0) {
  664. _descLabel.text = self.switchData.desc;
  665. _descLabel.hidden = NO;
  666. NSString *str = self.switchData.desc;
  667. NSDictionary *attribute = @{NSFontAttributeName : [UIFont systemFontOfSize:12]};
  668. CGSize size = [str boundingRectWithSize:CGSizeMake(264, 999)
  669. options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
  670. attributes:attribute
  671. context:nil]
  672. .size;
  673. [self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
  674. make.width.mas_equalTo(size.width);
  675. make.height.mas_equalTo(24);
  676. make.leading.mas_equalTo(leftMargin);
  677. make.top.mas_equalTo(12);
  678. }];
  679. [self.descLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
  680. make.width.mas_equalTo(size.width);
  681. make.height.mas_equalTo(size.height);
  682. make.leading.mas_equalTo(self.titleLabel.mas_leading);
  683. make.top.mas_equalTo(self.titleLabel.mas_bottom).mas_offset(2);
  684. }];
  685. } else {
  686. _descLabel.text = @"";
  687. [self.titleLabel sizeToFit];
  688. [self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
  689. make.size.mas_equalTo(self.titleLabel.frame.size);
  690. make.leading.mas_equalTo(self.switchData.margin);
  691. make.centerY.mas_equalTo(self.contentView);
  692. }];
  693. }
  694. }
  695. - (void)switchClick {
  696. if (self.switchData.cswitchSelector) {
  697. UIViewController *vc = self.mm_viewController;
  698. if ([vc respondsToSelector:self.switchData.cswitchSelector]) {
  699. #pragma clang diagnostic push
  700. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  701. [vc performSelector:self.switchData.cswitchSelector withObject:self];
  702. #pragma clang diagnostic pop
  703. }
  704. }
  705. }
  706. @end
  707. /////////////////////////////////////////////////////////////////////////////////
  708. //
  709. // TUIGroupPendencyCell & data
  710. //
  711. /////////////////////////////////////////////////////////////////////////////////
  712. @interface TUIGroupPendencyCellData ()
  713. @property V2TIMUserFullInfo *fromUserProfile;
  714. @property V2TIMGroupApplication *pendencyItem;
  715. @end
  716. @implementation TUIGroupPendencyCellData
  717. - (instancetype)initWithPendency:(V2TIMGroupApplication *)args {
  718. self = [self init];
  719. _pendencyItem = args;
  720. _groupId = args.groupID;
  721. _fromUser = args.fromUser;
  722. _toUser = args.toUser;
  723. if (args.fromUserNickName.length > 0) {
  724. _title = args.fromUserNickName;
  725. } else {
  726. _title = args.fromUser;
  727. }
  728. _avatarUrl = [NSURL URLWithString:args.fromUserFaceUrl];
  729. _requestMsg = args.requestMsg;
  730. if (_requestMsg.length == 0) {
  731. if (args.applicationType == V2TIM_GROUP_INVITE_APPLICATION_NEED_APPROVED_BY_ADMIN) {
  732. _requestMsg = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitInviteJoinGroupFormat), _toUser];
  733. } else {
  734. _requestMsg = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitWhoRequestForJoinGroupFormat), _title];
  735. }
  736. }
  737. return self;
  738. }
  739. - (void)accept {
  740. [self agreeWithSuccess:nil failure:nil];
  741. }
  742. - (void)reject {
  743. [self rejectWithSuccess:nil failure:nil];
  744. }
  745. - (void)agreeWithSuccess:(TUIGroupPendencyCellDataSuccessCallback)success
  746. failure:(TUIGroupPendencyCellDataFailureCallback)failure {
  747. [[V2TIMManager sharedInstance] acceptGroupApplication:_pendencyItem
  748. reason:TIMCommonLocalizableString(TUIKitAgreedByAdministor)
  749. succ:^{
  750. [TUITool makeToast:TIMCommonLocalizableString(Have_been_sent)];
  751. [[NSNotificationCenter defaultCenter] postNotificationName:
  752. TUIGroupPendencyCellData_onPendencyChanged object:nil];
  753. if (success) {
  754. success();
  755. }
  756. ;
  757. }
  758. fail:^(int code, NSString *msg) {
  759. [TUITool makeToastError:code msg:msg];
  760. if (failure) {
  761. failure(code,msg);
  762. }
  763. }];
  764. self.isAccepted = YES;
  765. }
  766. - (void)rejectWithSuccess:(TUIGroupPendencyCellDataSuccessCallback)success
  767. failure:(TUIGroupPendencyCellDataFailureCallback)failure {
  768. [[V2TIMManager sharedInstance] refuseGroupApplication:_pendencyItem
  769. reason:TIMCommonLocalizableString(TUIkitDiscliedByAdministor)
  770. succ:^{
  771. [TUITool makeToast:TIMCommonLocalizableString(Have_been_sent)];
  772. [[NSNotificationCenter defaultCenter] postNotificationName: TUIGroupPendencyCellData_onPendencyChanged object:nil];
  773. if (success) {
  774. success();
  775. }
  776. }
  777. fail:^(int code, NSString *msg) {
  778. [TUITool makeToastError:code msg:msg];
  779. if (failure) {
  780. failure(code,msg);
  781. }
  782. }];
  783. self.isRejectd = YES;
  784. }
  785. @end
  786. @implementation TUIGroupPendencyCell
  787. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  788. self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  789. self.avatarView = [[UIImageView alloc] initWithImage:DefaultAvatarImage];
  790. [self.contentView addSubview:self.avatarView];
  791. self.avatarView.mm_width(54).mm_height(54).mm__centerY(38).mm_left(12);
  792. self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
  793. [self.contentView addSubview:self.titleLabel];
  794. self.titleLabel.textColor = [UIColor darkTextColor];
  795. self.titleLabel.mm_left(self.avatarView.mm_maxX + 12).mm_top(14).mm_height(20).mm_width(120);
  796. self.addWordingLabel = [[UILabel alloc] initWithFrame:CGRectZero];
  797. [self.contentView addSubview:self.addWordingLabel];
  798. self.addWordingLabel.textColor = [UIColor lightGrayColor];
  799. self.addWordingLabel.font = [UIFont systemFontOfSize:15];
  800. self.addWordingLabel.mm_left(self.titleLabel.mm_x).mm_top(self.titleLabel.mm_maxY + 6).mm_height(15).mm_width(self.mm_w - self.titleLabel.mm_x - 80);
  801. self.agreeButton = [UIButton buttonWithType:UIButtonTypeSystem];
  802. self.accessoryView = self.agreeButton;
  803. [self.agreeButton addTarget:self action:@selector(agreeClick) forControlEvents:UIControlEventTouchUpInside];
  804. return self;
  805. }
  806. - (void)awakeFromNib {
  807. [super awakeFromNib];
  808. // Initialization code
  809. }
  810. - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
  811. [super setSelected:selected animated:animated];
  812. // Configure the view for the selected state
  813. }
  814. - (void)fillWithData:(TUIGroupPendencyCellData *)pendencyData {
  815. [super fillWithData:pendencyData];
  816. self.pendencyData = pendencyData;
  817. self.titleLabel.text = pendencyData.title;
  818. self.addWordingLabel.text = pendencyData.requestMsg;
  819. self.avatarView.image = DefaultAvatarImage;
  820. if (pendencyData.avatarUrl) {
  821. [self.avatarView sd_setImageWithURL:pendencyData.avatarUrl placeholderImage:[UIImage imageNamed:TIMCommonImagePath(@"default_c2c_head")]];
  822. }
  823. @weakify(self);
  824. [[RACObserve(pendencyData, isAccepted) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(NSNumber *isAccepted) {
  825. @strongify(self);
  826. if ([isAccepted boolValue]) {
  827. [self.agreeButton setTitle:TIMCommonLocalizableString(Agreed) forState:UIControlStateNormal];
  828. self.agreeButton.enabled = NO;
  829. [self.agreeButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
  830. self.agreeButton.layer.borderColor = [UIColor clearColor].CGColor;
  831. }
  832. }];
  833. [[RACObserve(pendencyData, isRejectd) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(NSNumber *isAccepted) {
  834. @strongify(self);
  835. if ([isAccepted boolValue]) {
  836. [self.agreeButton setTitle:TIMCommonLocalizableString(Disclined) forState:UIControlStateNormal];
  837. self.agreeButton.enabled = NO;
  838. [self.agreeButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
  839. self.agreeButton.layer.borderColor = [UIColor clearColor].CGColor;
  840. }
  841. }];
  842. if (!(pendencyData.isAccepted || pendencyData.isRejectd)) {
  843. [self.agreeButton setTitle:TIMCommonLocalizableString(Agree) forState:UIControlStateNormal];
  844. self.agreeButton.enabled = YES;
  845. [self.agreeButton setTitleColor:[UIColor darkTextColor] forState:UIControlStateNormal];
  846. self.agreeButton.layer.borderColor = [UIColor grayColor].CGColor;
  847. self.agreeButton.layer.borderWidth = 1;
  848. }
  849. self.agreeButton.mm_sizeToFit().mm_width(self.agreeButton.mm_w + 20);
  850. }
  851. - (void)agreeClick {
  852. if (self.pendencyData.cbuttonSelector) {
  853. UIViewController *vc = self.mm_viewController;
  854. if ([vc respondsToSelector:self.pendencyData.cbuttonSelector]) {
  855. #pragma clang diagnostic push
  856. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  857. [vc performSelector:self.pendencyData.cbuttonSelector withObject:self];
  858. #pragma clang diagnostic pop
  859. }
  860. }
  861. }
  862. - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
  863. if ((touch.view == self.agreeButton)) {
  864. return NO;
  865. }
  866. return YES;
  867. }
  868. @end
  869. /////////////////////////////////////////////////////////////////////////////////
  870. //
  871. // TUIButtonCell & data
  872. //
  873. /////////////////////////////////////////////////////////////////////////////////
  874. @implementation TUIButtonCellData
  875. - (CGFloat)heightOfWidth:(CGFloat)width {
  876. return TButtonCell_Height;
  877. }
  878. @end
  879. @implementation TUIButtonCell {
  880. UIView *_line;
  881. }
  882. - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  883. self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  884. if (self) {
  885. [self setupViews];
  886. self.changeColorWhenTouched = YES;
  887. }
  888. return self;
  889. }
  890. - (void)setupViews {
  891. self.backgroundColor = TIMCommonDynamicColor(@"form_bg_color", @"#FFFFFF");
  892. self.contentView.backgroundColor = TIMCommonDynamicColor(@"form_bg_color", @"#FFFFFF");
  893. _button = [UIButton buttonWithType:UIButtonTypeCustom];
  894. [_button.titleLabel setFont:[UIFont systemFontOfSize:18]];
  895. [_button addTarget:self action:@selector(onClick:) forControlEvents:UIControlEventTouchUpInside];
  896. [self.contentView addSubview:_button];
  897. [self setSeparatorInset:UIEdgeInsetsMake(0, Screen_Width, 0, 0)];
  898. [self setSelectionStyle:UITableViewCellSelectionStyleNone];
  899. self.changeColorWhenTouched = YES;
  900. _line = [[UIView alloc] initWithFrame:CGRectZero];
  901. [self.contentView addSubview:_line];
  902. _line.backgroundColor = TIMCommonDynamicColor(@"separator_color", @"#DBDBDB");
  903. }
  904. - (void)fillWithData:(TUIButtonCellData *)data {
  905. [super fillWithData:data];
  906. self.buttonData = data;
  907. [_button setTitle:data.title forState:UIControlStateNormal];
  908. switch (data.style) {
  909. case ButtonGreen: {
  910. [_button setTitleColor:TIMCommonDynamicColor(@"form_green_button_text_color", @"#FFFFFF") forState:UIControlStateNormal];
  911. _button.backgroundColor = TIMCommonDynamicColor(@"form_green_button_bg_color", @"#232323");
  912. [_button setBackgroundImage:[self imageWithColor:TIMCommonDynamicColor(@"form_green_button_highlight_bg_color", @"#179A1A")]
  913. forState:UIControlStateHighlighted];
  914. } break;
  915. case ButtonWhite: {
  916. [_button setTitleColor:TIMCommonDynamicColor(@"form_white_button_text_color", @"#000000") forState:UIControlStateNormal];
  917. _button.backgroundColor = TIMCommonDynamicColor(@"form_white_button_bg_color", @"#FFFFFF");
  918. } break;
  919. case ButtonRedText: {
  920. [_button setTitleColor:TIMCommonDynamicColor(@"form_redtext_button_text_color", @"#FF0000") forState:UIControlStateNormal];
  921. _button.backgroundColor = TIMCommonDynamicColor(@"form_redtext_button_bg_color", @"#FFFFFF");
  922. break;
  923. }
  924. case ButtonBule: {
  925. [_button.titleLabel setTextColor:TIMCommonDynamicColor(@"form_blue_button_text_color", @"#FFFFFF")];
  926. _button.backgroundColor = TIMCommonDynamicColor(@"form_blue_button_bg_color", @"#1E90FF");
  927. [_button setBackgroundImage:[self imageWithColor:TIMCommonDynamicColor(@"form_blue_button_highlight_bg_color", @"#1978D5")]
  928. forState:UIControlStateHighlighted];
  929. } break;
  930. default:
  931. break;
  932. }
  933. if (data.textColor) {
  934. [_button setTitleColor:data.textColor forState:UIControlStateNormal];
  935. }
  936. _line.hidden = data.hideSeparatorLine;
  937. }
  938. - (void)layoutSubviews {
  939. [super layoutSubviews];
  940. _button.mm_width(Screen_Width - 2 * TButtonCell_Margin).mm_height(self.mm_h - TButtonCell_Margin).mm_left(TButtonCell_Margin);
  941. _line.mm_width(Screen_Width).mm_height(0.2).mm_left(20).mm_bottom(0);
  942. }
  943. - (void)onClick:(UIButton *)sender {
  944. if (self.buttonData.cbuttonSelector) {
  945. UIViewController *vc = self.mm_viewController;
  946. if ([vc respondsToSelector:self.buttonData.cbuttonSelector]) {
  947. #pragma clang diagnostic push
  948. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  949. [vc performSelector:self.buttonData.cbuttonSelector withObject:self];
  950. #pragma clang diagnostic pop
  951. }
  952. }
  953. }
  954. - (void)didAddSubview:(UIView *)subview {
  955. [super didAddSubview:subview];
  956. if (subview != self.contentView) {
  957. [subview removeFromSuperview];
  958. }
  959. }
  960. - (UIImage *)imageWithColor:(UIColor *)color {
  961. CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
  962. UIGraphicsBeginImageContext(rect.size);
  963. CGContextRef context = UIGraphicsGetCurrentContext();
  964. CGContextSetFillColorWithColor(context, [color CGColor]);
  965. CGContextFillRect(context, rect);
  966. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  967. UIGraphicsEndImageContext();
  968. return image;
  969. }
  970. @end
  971. /////////////////////////////////////////////////////////////////////////////////
  972. //
  973. // TUIFaceCell & data
  974. //
  975. /////////////////////////////////////////////////////////////////////////////////
  976. @implementation TUIFaceCellData
  977. @end
  978. @implementation TUIFaceCell
  979. - (id)initWithFrame:(CGRect)frame {
  980. self = [super initWithFrame:frame];
  981. if (self) {
  982. [self setupViews];
  983. [self defaultLayout];
  984. }
  985. return self;
  986. }
  987. - (void)setupViews {
  988. _face = [[UIImageView alloc] init];
  989. _face.contentMode = UIViewContentModeScaleAspectFill;
  990. [self addSubview:_face];
  991. UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPress:)];
  992. [self addGestureRecognizer:longPress];
  993. [self setUserInteractionEnabled:YES];
  994. }
  995. - (void)defaultLayout {
  996. CGSize size = self.frame.size;
  997. _face.frame = CGRectMake(0, 0, size.width, size.height);
  998. }
  999. #define kTUIFaceCellAllowDynamicImageShow 0
  1000. - (void)setData:(TUIFaceCellData *)data {
  1001. if (!kTUIFaceCellAllowDynamicImageShow) {
  1002. UIImage * image = [[TUIImageCache sharedInstance] getFaceFromCache:data.path];
  1003. SDImageFormat imageFormat = [image sd_imageFormat];
  1004. if (SDImageFormatGIF == imageFormat ) {
  1005. self.gifImage = image;
  1006. if (image.images.count > 1) {
  1007. self.staicImage = image.images[0];
  1008. }
  1009. }
  1010. else {
  1011. self.staicImage = image;
  1012. }
  1013. _face.image = self.staicImage;
  1014. }
  1015. else {
  1016. _face.image = [[TUIImageCache sharedInstance] getFaceFromCache:data.path];
  1017. }
  1018. [self defaultLayout];
  1019. }
  1020. - (void)onLongPress:(UILongPressGestureRecognizer *)longPress {
  1021. if (self.longPressCallback) {
  1022. self.longPressCallback(longPress);
  1023. }
  1024. }
  1025. @end
  1026. /////////////////////////////////////////////////////////////////////////////////
  1027. //
  1028. // TUIFaceGroup
  1029. //
  1030. /////////////////////////////////////////////////////////////////////////////////
  1031. @implementation TUIFaceGroup
  1032. - (NSDictionary *)facesMap {
  1033. if (!_facesMap || (_facesMap.count != _faces.count )) {
  1034. NSMutableDictionary *faceDic = [NSMutableDictionary dictionaryWithCapacity:3];
  1035. if (_faces.count > 0) {
  1036. for (TUIFaceCellData *data in _faces) {
  1037. [faceDic setObject:data.path forKey:data.name];
  1038. }
  1039. }
  1040. _facesMap = [NSDictionary dictionaryWithDictionary:faceDic];
  1041. }
  1042. return _facesMap;
  1043. }
  1044. @end
  1045. @implementation TUIEmojiTextAttachment
  1046. - (CGRect)attachmentBoundsForTextContainer:(NSTextContainer *)textContainer
  1047. proposedLineFragment:(CGRect)lineFrag
  1048. glyphPosition:(CGPoint)position
  1049. characterIndex:(NSUInteger)charIndex {
  1050. return CGRectMake( 0 , -0.4* lineFrag.size.height, kTIMDefaultEmojiSize.width , kTIMDefaultEmojiSize.height);
  1051. }
  1052. @end
  1053. /////////////////////////////////////////////////////////////////////////////////
  1054. //
  1055. // TUIUnReadView
  1056. //
  1057. /////////////////////////////////////////////////////////////////////////////////
  1058. @implementation TUIUnReadView
  1059. - (id)init {
  1060. self = [super init];
  1061. if (self) {
  1062. [self setupViews];
  1063. [self defaultLayout];
  1064. }
  1065. return self;
  1066. }
  1067. - (void)setNum:(NSInteger)num {
  1068. NSString *unReadStr = [[NSNumber numberWithInteger:num] stringValue];
  1069. if (num > 99) {
  1070. unReadStr = @"99+";
  1071. }
  1072. _unReadLabel.text = unReadStr;
  1073. self.hidden = (num == 0 ? YES : NO);
  1074. [self defaultLayout];
  1075. }
  1076. - (void)setupViews {
  1077. _unReadLabel = [[UILabel alloc] init];
  1078. _unReadLabel.text = @"11";
  1079. _unReadLabel.font = [UIFont systemFontOfSize:10];
  1080. _unReadLabel.textColor = [UIColor whiteColor];
  1081. _unReadLabel.textAlignment = NSTextAlignmentCenter;
  1082. [_unReadLabel sizeToFit];
  1083. [self addSubview:_unReadLabel];
  1084. self.layer.cornerRadius = (_unReadLabel.frame.size.height + TUnReadView_Margin_TB * 2) / 2.0;
  1085. [self.layer masksToBounds];
  1086. self.backgroundColor = [UIColor redColor];
  1087. self.hidden = YES;
  1088. }
  1089. - (void)defaultLayout {
  1090. [_unReadLabel sizeToFit];
  1091. CGFloat width = _unReadLabel.frame.size.width + 2 * TUnReadView_Margin_LR;
  1092. CGFloat height = _unReadLabel.frame.size.height + 2 * TUnReadView_Margin_TB;
  1093. if (width < height) {
  1094. width = height;
  1095. }
  1096. self.bounds = CGRectMake(0, 0, width, height);
  1097. _unReadLabel.frame = self.bounds;
  1098. }
  1099. - (void)layoutSubviews {
  1100. [super layoutSubviews];
  1101. if (@available(iOS 11.0, *)) {
  1102. // Here is a workaround on iOS 11 UINavigationBarItem init with custom view, position issue
  1103. UIView *view = self;
  1104. while (![view isKindOfClass:[UINavigationBar class]] && [view superview] != nil) {
  1105. view = [view superview];
  1106. if ([view isKindOfClass:[UIStackView class]] && [view superview] != nil) {
  1107. CGFloat margin = 40.0f;
  1108. // margin = 4.0f;
  1109. [view.superview addConstraint:[NSLayoutConstraint constraintWithItem:view
  1110. attribute:NSLayoutAttributeLeading
  1111. relatedBy:NSLayoutRelationEqual
  1112. toItem:view.superview
  1113. attribute:NSLayoutAttributeLeading
  1114. multiplier:1.0
  1115. constant:margin]];
  1116. break;
  1117. }
  1118. }
  1119. }
  1120. }
  1121. @end
  1122. /////////////////////////////////////////////////////////////////////////////////
  1123. //
  1124. // TUIConversationPin
  1125. //
  1126. /////////////////////////////////////////////////////////////////////////////////
  1127. #define TOP_CONV_KEY @"TUIKIT_TOP_CONV_KEY"
  1128. NSString *kTopConversationListChangedNotification = @"kTopConversationListChangedNotification";
  1129. @implementation TUIConversationPin
  1130. + (instancetype)sharedInstance {
  1131. static TUIConversationPin *instance;
  1132. static dispatch_once_t onceToken;
  1133. dispatch_once(&onceToken, ^{
  1134. instance = [TUIConversationPin new];
  1135. });
  1136. return instance;
  1137. }
  1138. - (NSArray *)topConversationList {
  1139. #ifndef SDKPlaceTop
  1140. #define SDKPlaceTop
  1141. #endif
  1142. #ifdef SDKPlaceTop
  1143. return @[];
  1144. #else
  1145. NSArray *list = [[NSUserDefaults standardUserDefaults] objectForKey:TOP_CONV_KEY];
  1146. if ([list isKindOfClass:[NSArray class]]) {
  1147. return list;
  1148. }
  1149. return @[];
  1150. #endif
  1151. }
  1152. - (void)addTopConversation:(NSString *)conv callback:(void (^)(BOOL success, NSString *errorMessage))callback {
  1153. #ifndef SDKPlaceTop
  1154. #define SDKPlaceTop
  1155. #endif
  1156. #ifdef SDKPlaceTop
  1157. [V2TIMManager.sharedInstance pinConversation:conv
  1158. isPinned:YES
  1159. succ:^{
  1160. if (callback) {
  1161. callback(YES, nil);
  1162. }
  1163. }
  1164. fail:^(int code, NSString *desc) {
  1165. if (callback) {
  1166. callback(NO, desc);
  1167. }
  1168. }];
  1169. #else
  1170. [TUITool dispatchMainAsync:^{
  1171. NSMutableArray *list = [self topConversationList].mutableCopy;
  1172. if ([list containsObject:conv]) {
  1173. [list removeObject:conv];
  1174. }
  1175. [list insertObject:conv atIndex:0];
  1176. [[NSUserDefaults standardUserDefaults] setValue:list forKey:TOP_CONV_KEY];
  1177. [[NSNotificationCenter defaultCenter] postNotificationName:kTopConversationListChangedNotification object:nil];
  1178. if (callback) {
  1179. callback(YES, nil);
  1180. }
  1181. }];
  1182. #endif
  1183. }
  1184. - (void)removeTopConversation:(NSString *)conv callback:(void (^)(BOOL success, NSString *errorMessage))callback {
  1185. #ifndef SDKPlaceTop
  1186. #define SDKPlaceTop
  1187. #endif
  1188. #ifdef SDKPlaceTop
  1189. [V2TIMManager.sharedInstance pinConversation:conv
  1190. isPinned:NO
  1191. succ:^{
  1192. if (callback) {
  1193. callback(YES, nil);
  1194. }
  1195. }
  1196. fail:^(int code, NSString *desc) {
  1197. if (callback) {
  1198. callback(NO, desc);
  1199. }
  1200. }];
  1201. #else
  1202. [TUITool dispatchMainAsync:^{
  1203. NSMutableArray *list = [self topConversationList].mutableCopy;
  1204. if ([list containsObject:conv]) {
  1205. [list removeObject:conv];
  1206. [[NSUserDefaults standardUserDefaults] setValue:list forKey:TOP_CONV_KEY];
  1207. [[NSNotificationCenter defaultCenter] postNotificationName:kTopConversationListChangedNotification object:nil];
  1208. }
  1209. if (callback) {
  1210. callback(YES, nil);
  1211. }
  1212. }];
  1213. #endif
  1214. }
  1215. @end
  1216. /////////////////////////////////////////////////////////////////////////////////
  1217. //
  1218. // TUICommonContactSelectCellData
  1219. //
  1220. /////////////////////////////////////////////////////////////////////////////////
  1221. @implementation TUICommonContactSelectCellData
  1222. - (instancetype)init {
  1223. self = [super init];
  1224. if (self) {
  1225. _enabled = YES;
  1226. }
  1227. return self;
  1228. }
  1229. - (NSComparisonResult)compare:(TUICommonContactSelectCellData *)data {
  1230. return [self.title localizedCompare:data.title];
  1231. }
  1232. @end
  1233. /////////////////////////////////////////////////////////////////////////////////
  1234. //
  1235. // TUICommonContactListPickerCell
  1236. //
  1237. /////////////////////////////////////////////////////////////////////////////////
  1238. @implementation TUICommonContactListPickerCell
  1239. - (instancetype)initWithFrame:(CGRect)frame {
  1240. self = [super initWithFrame:frame];
  1241. if (self) {
  1242. CGFloat avatarWidth = 35.0;
  1243. _avatar = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, avatarWidth, avatarWidth)];
  1244. [self.contentView addSubview:_avatar];
  1245. _avatar.center = CGPointMake(avatarWidth / 2.0, avatarWidth / 2.0);
  1246. _avatar.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin |
  1247. UIViewAutoresizingFlexibleBottomMargin;
  1248. }
  1249. return self;
  1250. }
  1251. - (void)layoutSubviews {
  1252. [super layoutSubviews];
  1253. if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
  1254. _avatar.layer.masksToBounds = YES;
  1255. _avatar.layer.cornerRadius = _avatar.frame.size.height / 2;
  1256. } else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
  1257. _avatar.layer.masksToBounds = YES;
  1258. _avatar.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
  1259. }
  1260. }
  1261. @end
  1262. /////////////////////////////////////////////////////////////////////////////////
  1263. //
  1264. // TUIContactListPickerOnCancel
  1265. //
  1266. /////////////////////////////////////////////////////////////////////////////////
  1267. @interface TUIContactListPicker () <UICollectionViewDelegate, UICollectionViewDataSource>
  1268. @property(nonatomic) UICollectionView *collectionView;
  1269. @property(nonatomic) UIButton *accessoryBtn;
  1270. @end
  1271. @implementation TUIContactListPicker
  1272. - (instancetype)initWithFrame:(CGRect)frame {
  1273. self = [super initWithFrame:frame];
  1274. [self initControl];
  1275. [self setupBinding];
  1276. return self;
  1277. }
  1278. - (void)initControl {
  1279. UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
  1280. layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
  1281. self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
  1282. self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  1283. self.collectionView.showsHorizontalScrollIndicator = NO;
  1284. self.collectionView.decelerationRate = UIScrollViewDecelerationRateNormal;
  1285. [self.collectionView registerClass:[TUICommonContactListPickerCell class] forCellWithReuseIdentifier:@"PickerIdentifier"];
  1286. [self.collectionView setBackgroundColor:[UIColor clearColor]];
  1287. [self.collectionView setDelegate:self];
  1288. [self.collectionView setDataSource:self];
  1289. [self addSubview:_collectionView];
  1290. self.accessoryBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  1291. [self.accessoryBtn setBackgroundImage:TIMCommonBundleImage(@"icon_cell_blue_normal") forState:UIControlStateNormal];
  1292. [self.accessoryBtn setBackgroundImage:TIMCommonBundleImage(@"icon_cell_blue_normal") forState:UIControlStateHighlighted];
  1293. [self.accessoryBtn setTitle:[NSString stringWithFormat:@" %@ ", TIMCommonLocalizableString(Confirm)] forState:UIControlStateNormal];
  1294. self.accessoryBtn.enabled = NO;
  1295. [self addSubview:self.accessoryBtn];
  1296. }
  1297. - (void)setupBinding {
  1298. [self addObserver:self forKeyPath:@"selectArray" options:NSKeyValueObservingOptionNew context:nil];
  1299. }
  1300. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context {
  1301. if ([keyPath isEqualToString:@"selectArray"]) {
  1302. [self.collectionView reloadData];
  1303. NSArray *newSelectArray = change[NSKeyValueChangeNewKey];
  1304. if ([newSelectArray isKindOfClass:NSArray.class]) {
  1305. self.accessoryBtn.enabled = [newSelectArray count];
  1306. }
  1307. }
  1308. }
  1309. - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
  1310. return [self.selectArray count];
  1311. }
  1312. - (CGSize)collectionView:(nonnull UICollectionView *)collectionView
  1313. layout:(nonnull UICollectionViewLayout *)collectionViewLayout
  1314. sizeForItemAtIndexPath:(nonnull NSIndexPath *)indexPath {
  1315. return CGSizeMake(35, collectionView.bounds.size.height);
  1316. }
  1317. - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
  1318. TUICommonContactListPickerCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"PickerIdentifier" forIndexPath:indexPath];
  1319. TUICommonContactSelectCellData *data = self.selectArray[indexPath.row];
  1320. if (data.avatarUrl) {
  1321. [cell.avatar sd_setImageWithURL:data.avatarUrl placeholderImage:DefaultAvatarImage];
  1322. } else if (data.avatarImage) {
  1323. cell.avatar.image = data.avatarImage;
  1324. } else {
  1325. cell.avatar.image = DefaultAvatarImage;
  1326. }
  1327. return cell;
  1328. }
  1329. - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
  1330. [collectionView deselectItemAtIndexPath:indexPath animated:NO];
  1331. if (indexPath.item >= self.selectArray.count) {
  1332. return;
  1333. }
  1334. TUICommonContactSelectCellData *data = self.selectArray[indexPath.item];
  1335. if (self.onCancel) {
  1336. self.onCancel(data);
  1337. }
  1338. }
  1339. - (void)layoutSubviews {
  1340. [super layoutSubviews];
  1341. self.accessoryBtn.mm_sizeToFit().mm_height(30).mm_right(15).mm_top(13);
  1342. self.collectionView.mm_left(15).mm_height(40).mm_width(self.accessoryBtn.mm_x - 30).mm__centerY(self.accessoryBtn.mm_centerY);
  1343. if (isRTL()) {
  1344. [self.accessoryBtn resetFrameToFitRTL];
  1345. [self.collectionView resetFrameToFitRTL];
  1346. }
  1347. }
  1348. @end
  1349. /////////////////////////////////////////////////////////////////////////////////
  1350. //
  1351. // TUIProfileCardCell & VC
  1352. //
  1353. /////////////////////////////////////////////////////////////////////////////////
  1354. @implementation TUIProfileCardCellData
  1355. - (instancetype)init {
  1356. self = [super init];
  1357. if (self) {
  1358. _avatarImage = DefaultAvatarImage;
  1359. if ([_genderString isEqualToString:TIMCommonLocalizableString(Male)]) {
  1360. _genderIconImage = TUIContactCommonBundleImage(@"male");
  1361. } else if ([_genderString isEqualToString:TIMCommonLocalizableString(Female)]) {
  1362. _genderIconImage = TUIContactCommonBundleImage(@"female");
  1363. } else {
  1364. _genderIconImage = nil;
  1365. }
  1366. }
  1367. return self;
  1368. }
  1369. - (CGFloat)heightOfWidth:(CGFloat)width {
  1370. return TPersonalCommonCell_Image_Size.height + 2 * TPersonalCommonCell_Margin + (self.showSignature ? 24 : 0);
  1371. }
  1372. @end
  1373. @implementation TUIProfileCardCell
  1374. - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  1375. self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  1376. if (self) {
  1377. [self setupViews];
  1378. }
  1379. return self;
  1380. }
  1381. - (void)setupViews {
  1382. CGSize headSize = TPersonalCommonCell_Image_Size;
  1383. _avatar = [[UIImageView alloc] initWithFrame:CGRectMake(TPersonalCommonCell_Margin, TPersonalCommonCell_Margin, headSize.width, headSize.height)];
  1384. _avatar.contentMode = UIViewContentModeScaleAspectFit;
  1385. _avatar.layer.cornerRadius = 4;
  1386. _avatar.layer.masksToBounds = YES;
  1387. UITapGestureRecognizer *tapAvatar = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapAvatar)];
  1388. [_avatar addGestureRecognizer:tapAvatar];
  1389. _avatar.userInteractionEnabled = YES;
  1390. if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
  1391. self.avatar.layer.masksToBounds = YES;
  1392. self.avatar.layer.cornerRadius = headSize.height / 2;
  1393. } else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
  1394. self.avatar.layer.masksToBounds = YES;
  1395. self.avatar.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
  1396. }
  1397. [self.contentView addSubview:_avatar];
  1398. // CGSize genderIconSize = CGSizeMake(20, 20);
  1399. _genderIcon = [[UIImageView alloc] init];
  1400. _genderIcon.contentMode = UIViewContentModeScaleAspectFit;
  1401. _genderIcon.image = self.cardData.genderIconImage;
  1402. [self.contentView addSubview:_genderIcon];
  1403. _name = [[UILabel alloc] init];
  1404. [_name setFont:[UIFont boldSystemFontOfSize:18]];
  1405. [_name setTextColor:TIMCommonDynamicColor(@"form_title_color", @"#000000")];
  1406. [self.contentView addSubview:_name];
  1407. _identifier = [[UILabel alloc] init];
  1408. [_identifier setFont:[UIFont systemFontOfSize:13]];
  1409. [_identifier setTextColor:TIMCommonDynamicColor(@"form_subtitle_color", @"#888888")];
  1410. [self.contentView addSubview:_identifier];
  1411. _signature = [[UILabel alloc] init];
  1412. [_signature setFont:[UIFont systemFontOfSize:14]];
  1413. [_signature setTextColor:TIMCommonDynamicColor(@"form_subtitle_color", @"#888888")];
  1414. [self.contentView addSubview:_signature];
  1415. self.selectionStyle = UITableViewCellSelectionStyleNone;
  1416. }
  1417. - (void)fillWithData:(TUIProfileCardCellData *)data {
  1418. [super fillWithData:data];
  1419. self.cardData = data;
  1420. _signature.hidden = !data.showSignature;
  1421. // set data
  1422. @weakify(self);
  1423. RAC(_signature, text) = [RACObserve(data, signature) takeUntil:self.rac_prepareForReuseSignal];
  1424. [[[RACObserve(data, identifier) takeUntil:self.rac_prepareForReuseSignal] distinctUntilChanged] subscribeNext:^(NSString *x) {
  1425. @strongify(self);
  1426. self.identifier.text = [NSString stringWithFormat:@"%@:%@",TIMCommonLocalizableString(TUIKitIdentity),data.identifier];
  1427. }];
  1428. [[[RACObserve(data, name) takeUntil:self.rac_prepareForReuseSignal] distinctUntilChanged] subscribeNext:^(NSString *x) {
  1429. @strongify(self);
  1430. self.name.text = x;
  1431. [self.name sizeToFit];
  1432. }];
  1433. [[RACObserve(data, avatarUrl) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(NSURL *x) {
  1434. @strongify(self);
  1435. [self.avatar sd_setImageWithURL:x placeholderImage:self.cardData.avatarImage];
  1436. }];
  1437. [[RACObserve(data, genderString) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(NSString *x) {
  1438. @strongify(self);
  1439. if ([x isEqualToString:TIMCommonLocalizableString(Male)]) {
  1440. self.genderIcon.image = TUIContactCommonBundleImage(@"male");
  1441. } else if ([x isEqualToString:TIMCommonLocalizableString(Female)]) {
  1442. self.genderIcon.image = TUIContactCommonBundleImage(@"female");
  1443. } else {
  1444. self.genderIcon.image = nil;
  1445. }
  1446. }];
  1447. if (data.showAccessory) {
  1448. self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  1449. } else {
  1450. self.accessoryType = UITableViewCellAccessoryNone;
  1451. }
  1452. // tell constraints they need updating
  1453. [self setNeedsUpdateConstraints];
  1454. // update constraints now so we can animate the change
  1455. [self updateConstraintsIfNeeded];
  1456. [self layoutIfNeeded];
  1457. }
  1458. - (void)layoutSubviews {
  1459. [super layoutSubviews];
  1460. }
  1461. + (BOOL)requiresConstraintBasedLayout {
  1462. return YES;
  1463. }
  1464. // this is Apple's recommended place for adding/updating constraints
  1465. - (void)updateConstraints {
  1466. [super updateConstraints];
  1467. CGSize headSize = CGSizeMake(kScale390(66), kScale390(66));
  1468. [self.avatar mas_remakeConstraints:^(MASConstraintMaker *make) {
  1469. make.size.mas_equalTo(headSize);
  1470. make.top.mas_equalTo(kScale390(10));
  1471. make.leading.mas_equalTo(kScale390(16));
  1472. }];
  1473. if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
  1474. self.avatar.layer.masksToBounds = YES;
  1475. self.avatar.layer.cornerRadius = headSize.height / 2;
  1476. } else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
  1477. self.avatar.layer.masksToBounds = YES;
  1478. self.avatar.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
  1479. }
  1480. [self.name sizeToFit];
  1481. [self.name mas_remakeConstraints:^(MASConstraintMaker *make) {
  1482. make.top.mas_equalTo(TPersonalCommonCell_Margin);
  1483. make.leading.mas_equalTo(self.avatar.mas_trailing).mas_offset(15);
  1484. make.width.mas_lessThanOrEqualTo(self.name.frame.size.width);
  1485. make.height.mas_greaterThanOrEqualTo(self.name.frame.size.height);
  1486. make.trailing.mas_lessThanOrEqualTo(self.genderIcon.mas_leading).mas_offset(- 1);
  1487. }];
  1488. [self.genderIcon mas_remakeConstraints:^(MASConstraintMaker *make) {
  1489. make.width.height.mas_equalTo(self.name.font.pointSize *0.9);
  1490. make.centerY.mas_equalTo(self.name);
  1491. make.leading.mas_equalTo(self.name.mas_trailing).mas_offset(1);
  1492. make.trailing.mas_lessThanOrEqualTo(self.contentView.mas_trailing).mas_offset(- 10);
  1493. }];
  1494. [self.identifier sizeToFit];
  1495. [self.identifier mas_remakeConstraints:^(MASConstraintMaker *make) {
  1496. make.leading.mas_equalTo(self.name);
  1497. make.top.mas_equalTo(self.name.mas_bottom).mas_offset(5);
  1498. if(self.identifier.frame.size.width > 80) {
  1499. make.width.mas_greaterThanOrEqualTo(self.identifier.frame.size.width);
  1500. }
  1501. else {
  1502. make.width.mas_greaterThanOrEqualTo(@80);
  1503. }
  1504. make.height.mas_greaterThanOrEqualTo(self.identifier.frame.size.height);
  1505. make.trailing.mas_lessThanOrEqualTo(self.contentView.mas_trailing).mas_offset(-1);
  1506. }];
  1507. if (self.cardData.showSignature) {
  1508. [self.signature sizeToFit];
  1509. [self.signature mas_remakeConstraints:^(MASConstraintMaker *make) {
  1510. make.leading.mas_equalTo(self.name);
  1511. make.top.mas_equalTo(self.identifier.mas_bottom).mas_offset(5);
  1512. if(self.signature.frame.size.width > 80) {
  1513. make.width.mas_greaterThanOrEqualTo(self.signature.frame.size.width);
  1514. }
  1515. else {
  1516. make.width.mas_greaterThanOrEqualTo(@80);
  1517. }
  1518. make.height.mas_greaterThanOrEqualTo(self.signature.frame.size.height);
  1519. make.trailing.mas_lessThanOrEqualTo(self.contentView.mas_trailing).mas_offset(-1);
  1520. }];
  1521. } else {
  1522. self.signature.frame = CGRectZero;
  1523. }
  1524. }
  1525. - (void)onTapAvatar {
  1526. if (_delegate && [_delegate respondsToSelector:@selector(didTapOnAvatar:)]) [_delegate didTapOnAvatar:self];
  1527. }
  1528. @end
  1529. @interface TUIAvatarViewController () <UIScrollViewDelegate>
  1530. @property UIImageView *avatarView;
  1531. @property TUIScrollView *avatarScrollView;
  1532. @property UIImage *saveBackgroundImage;
  1533. @property UIImage *saveShadowImage;
  1534. @end
  1535. @implementation TUIAvatarViewController
  1536. - (void)viewDidLoad {
  1537. [super viewDidLoad];
  1538. self.saveBackgroundImage = [self.navigationController.navigationBar backgroundImageForBarMetrics:UIBarMetricsDefault];
  1539. self.saveShadowImage = self.navigationController.navigationBar.shadowImage;
  1540. [self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
  1541. self.navigationController.navigationBar.shadowImage = [UIImage new];
  1542. CGRect rect = self.view.bounds;
  1543. self.avatarScrollView = [[TUIScrollView alloc] initWithFrame:CGRectZero];
  1544. [self.view addSubview:self.avatarScrollView];
  1545. self.avatarScrollView.backgroundColor = [UIColor blackColor];
  1546. self.avatarScrollView.frame = rect;
  1547. self.avatarView = [[UIImageView alloc] initWithImage:self.avatarData.avatarImage];
  1548. self.avatarScrollView.imageView = self.avatarView;
  1549. self.avatarScrollView.maximumZoomScale = 4.0;
  1550. self.avatarScrollView.delegate = self;
  1551. self.avatarView.image = self.avatarData.avatarImage;
  1552. TUIProfileCardCellData *data = self.avatarData;
  1553. /*
  1554. @weakify(self);
  1555. [RACObserve(data, avatarUrl) subscribeNext:^(NSURL *x) {
  1556. @strongify(self);
  1557. [self.avatarView sd_setImageWithURL:x placeholderImage:self.avatarData.avatarImage];
  1558. }];
  1559. */
  1560. @weakify(self);
  1561. [RACObserve(data, avatarUrl) subscribeNext:^(NSURL *x) {
  1562. @strongify(self);
  1563. [self.avatarView sd_setImageWithURL:x placeholderImage:self.avatarData.avatarImage];
  1564. [self.avatarScrollView setNeedsLayout];
  1565. }];
  1566. }
  1567. - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
  1568. return self.avatarView;
  1569. }
  1570. - (void)viewWillAppear:(BOOL)animated {
  1571. [super viewWillAppear:animated];
  1572. [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
  1573. [self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
  1574. self.navigationController.navigationBar.shadowImage = [UIImage new];
  1575. self.navigationController.navigationBar.backgroundColor = [UIColor clearColor];
  1576. }
  1577. - (void)viewWillDisappear:(BOOL)animated {
  1578. [super viewWillDisappear:animated];
  1579. [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
  1580. }
  1581. - (void)willMoveToParentViewController:(UIViewController *)parent {
  1582. if (parent == nil) {
  1583. [self.navigationController.navigationBar setBackgroundImage:self.saveBackgroundImage forBarMetrics:UIBarMetricsDefault];
  1584. self.navigationController.navigationBar.shadowImage = self.saveShadowImage;
  1585. }
  1586. }
  1587. @end
  1588. #define UserAvatarURL(x) [NSString stringWithFormat:@"https://im.sdk.qcloud.com/download/tuikit-resource/avatar/avatar_%d.png", x]
  1589. #define UserAvatarCount 26
  1590. #define GroupAvatarURL(x) [NSString stringWithFormat:@"https://im.sdk.qcloud.com/download/tuikit-resource/group-avatar/group_avatar_%d.png", x]
  1591. #define GroupAvatarCount 24
  1592. #define Community_coverURL(x) [NSString stringWithFormat:@"https://im.sdk.qcloud.com/download/tuikit-resource/community-cover/community_cover_%d.png", x]
  1593. #define Community_coverCount 12
  1594. #define BackGroundCoverURL(x) \
  1595. [NSString stringWithFormat:@"https://im.sdk.qcloud.com/download/tuikit-resource/conversation-backgroundImage/backgroundImage_%d.png", x]
  1596. #define BackGroundCoverURL_full(x) \
  1597. [NSString stringWithFormat:@"https://im.sdk.qcloud.com/download/tuikit-resource/conversation-backgroundImage/backgroundImage_%d_full.png", x]
  1598. #define BackGroundCoverCount 7
  1599. @implementation TUISelectAvatarCardItem
  1600. @end
  1601. @interface TUISelectAvatarCollectionCell : UICollectionViewCell
  1602. @property(nonatomic, strong) UIImageView *imageView;
  1603. @property(nonatomic, strong) UIImageView *selectedView;
  1604. @property(nonatomic, strong) UIView *maskView;
  1605. @property(nonatomic, strong) UILabel *descLabel;
  1606. @property(nonatomic, strong) TUISelectAvatarCardItem *cardItem;
  1607. - (void)updateSelectedUI;
  1608. @end
  1609. @implementation TUISelectAvatarCollectionCell
  1610. - (instancetype)initWithFrame:(CGRect)frame {
  1611. self = [super initWithFrame:frame];
  1612. if (self) {
  1613. self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
  1614. [self.imageView setUserInteractionEnabled:YES];
  1615. self.imageView.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
  1616. self.imageView.layer.borderWidth = 2;
  1617. self.imageView.layer.masksToBounds = YES;
  1618. [self.contentView addSubview:self.imageView];
  1619. [self.imageView addSubview:self.selectedView];
  1620. [self setupMaskView];
  1621. }
  1622. return self;
  1623. }
  1624. - (void)layoutSubviews {
  1625. [self updateCellView];
  1626. self.selectedView.frame = CGRectMake(self.imageView.frame.size.width - 16 - 4, 4, 16, 16);
  1627. }
  1628. - (void)updateCellView {
  1629. [self updateSelectedUI];
  1630. [self updateImageView];
  1631. [self updateMaskView];
  1632. }
  1633. - (void)updateSelectedUI {
  1634. if (self.cardItem.isSelect) {
  1635. self.imageView.layer.borderColor = TIMCommonDynamicColor(@"", @"#006EFF").CGColor;
  1636. self.selectedView.hidden = NO;
  1637. } else {
  1638. if (self.cardItem.isDefaultBackgroundItem) {
  1639. self.imageView.layer.borderColor = [[UIColor grayColor] colorWithAlphaComponent:0.1].CGColor;
  1640. } else {
  1641. self.imageView.layer.borderColor = UIColor.clearColor.CGColor;
  1642. }
  1643. self.selectedView.hidden = YES;
  1644. }
  1645. }
  1646. - (void)updateImageView {
  1647. if (self.cardItem.isGroupGridAvatar) {
  1648. [self updateNormalGroupGridAvatar];
  1649. } else {
  1650. [self.imageView sd_setImageWithURL:[NSURL URLWithString:self.cardItem.posterUrlStr]
  1651. placeholderImage:TIMCommonBundleThemeImage(@"default_c2c_head_img", @"default_c2c_head_img")];
  1652. }
  1653. }
  1654. - (void)updateMaskView {
  1655. if (self.cardItem.isDefaultBackgroundItem) {
  1656. self.maskView.hidden = NO;
  1657. self.maskView.frame = CGRectMake(0, self.imageView.frame.size.height - 28, self.imageView.frame.size.width, 28);
  1658. [self.descLabel sizeToFit];
  1659. self.descLabel.tui_mm_center();
  1660. } else {
  1661. self.maskView.hidden = YES;
  1662. }
  1663. }
  1664. - (void)updateNormalGroupGridAvatar {
  1665. if (TUIConfig.defaultConfig.enableGroupGridAvatar && self.cardItem.cacheGroupGridAvatarImage) {
  1666. [self.imageView sd_setImageWithURL:nil placeholderImage:self.cardItem.cacheGroupGridAvatarImage];
  1667. }
  1668. }
  1669. - (void)setupMaskView {
  1670. self.maskView = [[UIView alloc] initWithFrame:CGRectZero];
  1671. self.maskView.backgroundColor = [UIColor tui_colorWithHex:@"cccccc"];
  1672. [self.imageView addSubview:self.maskView];
  1673. self.descLabel = [[UILabel alloc] initWithFrame:CGRectZero];
  1674. self.descLabel.text = TIMCommonLocalizableString(TUIKitDefaultBackground);
  1675. self.descLabel.textColor = [UIColor whiteColor];
  1676. self.descLabel.font = [UIFont systemFontOfSize:13];
  1677. [self.maskView addSubview:self.descLabel];
  1678. [self.descLabel sizeToFit];
  1679. self.descLabel.tui_mm_center();
  1680. }
  1681. - (void)setCardItem:(TUISelectAvatarCardItem *)cardItem {
  1682. _cardItem = cardItem;
  1683. }
  1684. - (UIImageView *)selectedView {
  1685. if (!_selectedView) {
  1686. _selectedView = [[UIImageView alloc] initWithFrame:CGRectZero];
  1687. _selectedView.image = [UIImage imageNamed:TIMCommonImagePath(@"icon_avatar_selected")];
  1688. }
  1689. return _selectedView;
  1690. }
  1691. @end
  1692. @interface TUISelectAvatarController () <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
  1693. @property(nonatomic, strong) TUINaviBarIndicatorView *titleView;
  1694. @property(nonatomic, strong) UICollectionView *collectionView;
  1695. @property(nonatomic, strong) NSMutableArray *dataArr;
  1696. @property(nonatomic, strong) TUISelectAvatarCardItem *currentSelectCardItem;
  1697. @property(nonatomic, strong) UIButton *rightButton;
  1698. @end
  1699. @implementation TUISelectAvatarController
  1700. static NSString *const reuseIdentifier = @"TUISelectAvatarCollectionCell";
  1701. - (instancetype)init {
  1702. if (self = [super init]) {
  1703. self.selectAvatarType = TUISelectAvatarTypeUserAvatar;
  1704. }
  1705. return self;
  1706. }
  1707. - (void)viewDidLoad {
  1708. [super viewDidLoad];
  1709. UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
  1710. [flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
  1711. CGRect frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
  1712. self.collectionView = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:flowLayout];
  1713. [self.view addSubview:self.collectionView];
  1714. self.collectionView.backgroundColor = TIMCommonDynamicColor(@"controller_bg_color", @"#F2F3F5");
  1715. self.collectionView.dataSource = self;
  1716. self.collectionView.delegate = self;
  1717. // Register cell classes
  1718. [self.collectionView registerClass:[TUISelectAvatarCollectionCell class] forCellWithReuseIdentifier:reuseIdentifier];
  1719. // Do any additional setup after loading the view.
  1720. [self setupNavigator];
  1721. self.dataArr = [NSMutableArray arrayWithCapacity:3];
  1722. [self loadData];
  1723. }
  1724. - (void)loadData {
  1725. if (self.selectAvatarType == TUISelectAvatarTypeUserAvatar) {
  1726. for (int i = 0; i < UserAvatarCount; i++) {
  1727. TUISelectAvatarCardItem *cardItem = [self creatCardItemByURL:UserAvatarURL(i + 1)];
  1728. [self.dataArr addObject:cardItem];
  1729. }
  1730. } else if (self.selectAvatarType == TUISelectAvatarTypeGroupAvatar) {
  1731. if (TUIConfig.defaultConfig.enableGroupGridAvatar && self.cacheGroupGridAvatarImage) {
  1732. TUISelectAvatarCardItem *cardItem = [self creatGroupGridAvatarCardItem];
  1733. [self.dataArr addObject:cardItem];
  1734. }
  1735. for (int i = 0; i < GroupAvatarCount; i++) {
  1736. TUISelectAvatarCardItem *cardItem = [self creatCardItemByURL:GroupAvatarURL(i + 1)];
  1737. [self.dataArr addObject:cardItem];
  1738. }
  1739. } else if (self.selectAvatarType == TUISelectAvatarTypeConversationBackGroundCover) {
  1740. TUISelectAvatarCardItem *cardItem = [self creatCleanCardItem];
  1741. [self.dataArr addObject:cardItem];
  1742. for (int i = 0; i < BackGroundCoverCount; i++) {
  1743. TUISelectAvatarCardItem *cardItem = [self creatCardItemByURL:BackGroundCoverURL(i + 1) fullUrl:BackGroundCoverURL_full(i + 1)];
  1744. [self.dataArr addObject:cardItem];
  1745. }
  1746. }
  1747. else {
  1748. for (int i = 0; i < Community_coverCount; i++) {
  1749. TUISelectAvatarCardItem *cardItem = [self creatCardItemByURL:Community_coverURL(i + 1)];
  1750. [self.dataArr addObject:cardItem];
  1751. }
  1752. }
  1753. [self.collectionView reloadData];
  1754. }
  1755. - (TUISelectAvatarCardItem *)creatCardItemByURL:(NSString *)urlStr {
  1756. TUISelectAvatarCardItem *cardItem = [[TUISelectAvatarCardItem alloc] init];
  1757. cardItem.posterUrlStr = urlStr;
  1758. cardItem.isSelect = NO;
  1759. if ([cardItem.posterUrlStr isEqualToString:self.profilFaceURL]) {
  1760. cardItem.isSelect = YES;
  1761. self.currentSelectCardItem = cardItem;
  1762. }
  1763. return cardItem;
  1764. }
  1765. - (TUISelectAvatarCardItem *)creatGroupGridAvatarCardItem {
  1766. TUISelectAvatarCardItem *cardItem = [[TUISelectAvatarCardItem alloc] init];
  1767. cardItem.posterUrlStr = nil;
  1768. cardItem.isSelect = NO;
  1769. cardItem.isGroupGridAvatar = YES;
  1770. cardItem.createGroupType = self.createGroupType;
  1771. cardItem.cacheGroupGridAvatarImage = self.cacheGroupGridAvatarImage;
  1772. if (!self.profilFaceURL) {
  1773. cardItem.isSelect = YES;
  1774. self.currentSelectCardItem = cardItem;
  1775. }
  1776. return cardItem;
  1777. }
  1778. - (TUISelectAvatarCardItem *)creatCardItemByURL:(NSString *)urlStr fullUrl:(NSString *)fullUrl {
  1779. TUISelectAvatarCardItem *cardItem = [[TUISelectAvatarCardItem alloc] init];
  1780. cardItem.posterUrlStr = urlStr;
  1781. cardItem.fullUrlStr = fullUrl;
  1782. cardItem.isSelect = NO;
  1783. if ([cardItem.posterUrlStr isEqualToString:self.profilFaceURL] || [cardItem.fullUrlStr isEqualToString:self.profilFaceURL]) {
  1784. cardItem.isSelect = YES;
  1785. self.currentSelectCardItem = cardItem;
  1786. }
  1787. return cardItem;
  1788. }
  1789. - (TUISelectAvatarCardItem *)creatCleanCardItem {
  1790. TUISelectAvatarCardItem *cardItem = [[TUISelectAvatarCardItem alloc] init];
  1791. cardItem.posterUrlStr = nil;
  1792. cardItem.isSelect = NO;
  1793. cardItem.isDefaultBackgroundItem = YES;
  1794. if (self.profilFaceURL.length == 0) {
  1795. cardItem.isSelect = YES;
  1796. self.currentSelectCardItem = cardItem;
  1797. }
  1798. return cardItem;
  1799. }
  1800. - (void)setupNavigator {
  1801. _titleView = [[TUINaviBarIndicatorView alloc] init];
  1802. self.navigationItem.titleView = _titleView;
  1803. self.navigationItem.title = @"";
  1804. if (self.selectAvatarType == TUISelectAvatarTypeCover) {
  1805. [self.titleView setTitle:TIMCommonLocalizableString(TUIKitChooseCover)];
  1806. } else if (self.selectAvatarType == TUISelectAvatarTypeConversationBackGroundCover) {
  1807. [self.titleView setTitle:TIMCommonLocalizableString(TUIKitChooseBackground)];
  1808. } else {
  1809. [self.titleView setTitle:TIMCommonLocalizableString(TUIKitChooseAvatar)];
  1810. }
  1811. self.rightButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
  1812. [self.rightButton setTitle:TIMCommonLocalizableString(Save) forState:UIControlStateNormal];
  1813. [self.rightButton addTarget:self action:@selector(rightBarButtonClick) forControlEvents:UIControlEventTouchUpInside];
  1814. self.rightButton.titleLabel.font = [UIFont fontWithName:@"PingFangSC-Regular" size:14];
  1815. [self.rightButton setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
  1816. UIBarButtonItem *rightItem = [[UIBarButtonItem alloc] initWithCustomView:self.rightButton];
  1817. self.navigationItem.rightBarButtonItems = @[ rightItem ];
  1818. }
  1819. - (void)setCurrentSelectCardItem:(TUISelectAvatarCardItem *)currentSelectCardItem {
  1820. _currentSelectCardItem = currentSelectCardItem;
  1821. if (_currentSelectCardItem) {
  1822. [self.rightButton setTitleColor:TIMCommonDynamicColor(@"", @"#006EFF") forState:UIControlStateNormal];
  1823. } else {
  1824. [self.rightButton setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
  1825. }
  1826. }
  1827. - (void)rightBarButtonClick {
  1828. if (!self.currentSelectCardItem) {
  1829. return;
  1830. }
  1831. if (self.selectCallBack) {
  1832. if (self.selectAvatarType == TUISelectAvatarTypeConversationBackGroundCover) {
  1833. if (IS_NOT_EMPTY_NSSTRING(self.currentSelectCardItem.fullUrlStr)) {
  1834. dispatch_async(dispatch_get_main_queue(), ^{
  1835. [TUITool makeToastActivity];
  1836. });
  1837. @weakify(self);
  1838. [[SDWebImagePrefetcher sharedImagePrefetcher]
  1839. prefetchURLs:@[ [NSURL URLWithString:self.currentSelectCardItem.fullUrlStr] ]
  1840. progress:nil
  1841. completed:^(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls) {
  1842. @strongify(self);
  1843. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  1844. dispatch_async(dispatch_get_main_queue(), ^{
  1845. [TUITool hideToastActivity];
  1846. [TUITool makeToast:TIMCommonLocalizableString(TUIKitChooseBackgroundSuccess)];
  1847. if (self.selectCallBack) {
  1848. self.selectCallBack(self.currentSelectCardItem.fullUrlStr);
  1849. [self.navigationController popViewControllerAnimated:YES];
  1850. }
  1851. });
  1852. });
  1853. }];
  1854. } else {
  1855. [TUITool makeToast:TIMCommonLocalizableString(TUIKitChooseBackgroundSuccess)];
  1856. self.selectCallBack(self.currentSelectCardItem.fullUrlStr);
  1857. [self.navigationController popViewControllerAnimated:YES];
  1858. }
  1859. } else {
  1860. self.selectCallBack(self.currentSelectCardItem.posterUrlStr);
  1861. [self.navigationController popViewControllerAnimated:YES];
  1862. }
  1863. }
  1864. }
  1865. #pragma mark - UICollectionViewDelegateFlowLayout
  1866. - (CGSize)collectionView:(UICollectionView *)collectionView
  1867. layout:(UICollectionViewLayout *)collectionViewLayout
  1868. sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
  1869. CGFloat margin = 15;
  1870. CGFloat padding = 13;
  1871. int rowCount = 4.0;
  1872. if (self.selectAvatarType == TUISelectAvatarTypeCover || self.selectAvatarType == TUISelectAvatarTypeConversationBackGroundCover) {
  1873. rowCount = 2.0;
  1874. } else {
  1875. rowCount = 4.0;
  1876. }
  1877. CGFloat width = (self.view.frame.size.width - 2 * margin - (rowCount - 1) * padding) / rowCount;
  1878. CGFloat height = 77;
  1879. if (self.selectAvatarType == TUISelectAvatarTypeConversationBackGroundCover) {
  1880. height = 125;
  1881. }
  1882. return CGSizeMake(width, height);
  1883. }
  1884. - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView
  1885. layout:(UICollectionViewLayout *)collectionViewLayout
  1886. insetForSectionAtIndex:(NSInteger)section {
  1887. return UIEdgeInsetsMake(24, 15, 0, 15);
  1888. }
  1889. #pragma mark <UICollectionViewDataSource>
  1890. - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
  1891. return 1;
  1892. }
  1893. - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
  1894. return [self.dataArr count];
  1895. }
  1896. - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
  1897. TUISelectAvatarCollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
  1898. // Configure the cell
  1899. if (indexPath.row < self.dataArr.count) {
  1900. cell.cardItem = self.dataArr[indexPath.row];
  1901. }
  1902. return cell;
  1903. }
  1904. #pragma mark <UICollectionViewDelegate>
  1905. - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
  1906. [self recoverSelectedStatus];
  1907. TUISelectAvatarCollectionCell *cell = (TUISelectAvatarCollectionCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
  1908. if (cell == nil) {
  1909. [self.collectionView layoutIfNeeded];
  1910. cell = (TUISelectAvatarCollectionCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
  1911. }
  1912. if (self.currentSelectCardItem == cell.cardItem) {
  1913. self.currentSelectCardItem = nil;
  1914. } else {
  1915. cell.cardItem.isSelect = YES;
  1916. [cell updateSelectedUI];
  1917. self.currentSelectCardItem = cell.cardItem;
  1918. }
  1919. }
  1920. - (void)recoverSelectedStatus {
  1921. NSInteger index = 0;
  1922. for (TUISelectAvatarCardItem *card in self.dataArr) {
  1923. if (self.currentSelectCardItem == card) {
  1924. card.isSelect = NO;
  1925. break;
  1926. }
  1927. index++;
  1928. }
  1929. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
  1930. TUISelectAvatarCollectionCell *cell = (TUISelectAvatarCollectionCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
  1931. if (cell == nil) {
  1932. [self.collectionView layoutIfNeeded];
  1933. cell = (TUISelectAvatarCollectionCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
  1934. }
  1935. [cell updateSelectedUI];
  1936. }
  1937. @end
  1938. /////////////////////////////////////////////////////////////////////////////////
  1939. //
  1940. // TUICommonAvatarCell & Data
  1941. //
  1942. /////////////////////////////////////////////////////////////////////////////////
  1943. @implementation TUICommonAvatarCellData
  1944. - (instancetype)init {
  1945. self = [super init];
  1946. if (self) {
  1947. _avatarImage = DefaultAvatarImage;
  1948. }
  1949. return self;
  1950. }
  1951. - (CGFloat)heightOfWidth:(CGFloat)width {
  1952. return TPersonalCommonCell_Image_Size.height + 2 * TPersonalCommonCell_Margin;
  1953. }
  1954. @end
  1955. @interface TUICommonAvatarCell ()
  1956. @property TUICommonAvatarCellData *avatarData;
  1957. @end
  1958. @implementation TUICommonAvatarCell
  1959. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  1960. if (self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier]) {
  1961. [self setupViews];
  1962. self.selectionStyle = UITableViewCellSelectionStyleNone;
  1963. }
  1964. return self;
  1965. }
  1966. - (void)fillWithData:(TUICommonAvatarCellData *)avatarData {
  1967. [super fillWithData:avatarData];
  1968. self.avatarData = avatarData;
  1969. RAC(_keyLabel, text) = [RACObserve(avatarData, key) takeUntil:self.rac_prepareForReuseSignal];
  1970. RAC(_valueLabel, text) = [RACObserve(avatarData, value) takeUntil:self.rac_prepareForReuseSignal];
  1971. @weakify(self);
  1972. [[RACObserve(avatarData, avatarUrl) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(NSURL *x) {
  1973. @strongify(self);
  1974. [self.avatar sd_setImageWithURL:x placeholderImage:self.avatarData.avatarImage];
  1975. }];
  1976. if (avatarData.showAccessory) {
  1977. self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  1978. } else {
  1979. self.accessoryType = UITableViewCellAccessoryNone;
  1980. }
  1981. // tell constraints they need updating
  1982. [self setNeedsUpdateConstraints];
  1983. // update constraints now so we can animate the change
  1984. [self updateConstraintsIfNeeded];
  1985. [self layoutIfNeeded];
  1986. }
  1987. - (void)setupViews {
  1988. _avatar = [[UIImageView alloc] initWithFrame:CGRectZero];
  1989. _avatar.contentMode = UIViewContentModeScaleAspectFit;
  1990. [self addSubview:_avatar];
  1991. _keyLabel = self.textLabel;
  1992. _valueLabel = self.detailTextLabel;
  1993. [self addSubview:_keyLabel];
  1994. [self addSubview:_valueLabel];
  1995. self.keyLabel.textColor = TIMCommonDynamicColor(@"form_key_text_color", @"#444444");
  1996. self.valueLabel.textColor = TIMCommonDynamicColor(@"form_value_text_color", @"#000000");
  1997. self.selectionStyle = UITableViewCellSelectionStyleNone;
  1998. }
  1999. + (BOOL)requiresConstraintBasedLayout {
  2000. return YES;
  2001. }
  2002. // this is Apple's recommended place for adding/updating constraints
  2003. - (void)updateConstraints {
  2004. [super updateConstraints];
  2005. CGSize headSize = TPersonalCommonCell_Image_Size;
  2006. [self.avatar mas_remakeConstraints:^(MASConstraintMaker *make) {
  2007. make.size.mas_equalTo(headSize);
  2008. if (self.avatarData.showAccessory) {
  2009. make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(-10);
  2010. }
  2011. else {
  2012. make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(-20);
  2013. }
  2014. make.centerY.mas_equalTo(self);
  2015. }];
  2016. if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
  2017. self.avatar.layer.masksToBounds = YES;
  2018. self.avatar.layer.cornerRadius = headSize.height / 2;
  2019. } else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
  2020. self.avatar.layer.masksToBounds = YES;
  2021. self.avatar.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
  2022. }
  2023. }
  2024. - (void)layoutSubviews {
  2025. [super layoutSubviews];
  2026. }
  2027. @end
  2028. /////////////////////////////////////////////////////////////////////////////////
  2029. //
  2030. // TUIConversationGroupItem
  2031. //
  2032. /////////////////////////////////////////////////////////////////////////////////
  2033. NSUInteger kConversationMarkStarType = V2TIM_CONVERSATION_MARK_TYPE_STAR;
  2034. @implementation TUIConversationGroupItem
  2035. - (instancetype)init {
  2036. self = [super init];
  2037. if (self) {
  2038. self.unreadCount = 0;
  2039. self.groupIndex = 0;
  2040. self.isShow = YES;
  2041. }
  2042. return self;
  2043. }
  2044. @end
  2045. @implementation TUISendMessageAppendParams
  2046. + (instancetype)defaultConfig {
  2047. TUISendMessageAppendParams *params = [[TUISendMessageAppendParams alloc] init];
  2048. params.priority = V2TIM_PRIORITY_NORMAL;
  2049. return params;
  2050. }
  2051. @end