This commit is contained in:
启星
2025-08-08 10:49:36 +08:00
parent 6400cf78bb
commit b5ce3d580a
8780 changed files with 978183 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
* This document declares that after the group is successfully created, the "xxx create group chat" message cell displayed when jumping to the message
* interface
*/
#import <TIMCommon/TUISystemMessageCell.h>
#import "TUIGroupCreatedCellData.h"
@interface TUIGroupCreatedCell : TUISystemMessageCell
- (void)fillWithData:(TUIGroupCreatedCellData *)data;
@end

View File

@@ -0,0 +1,17 @@
//
// MyCustomCell.m
// TUIKitDemo
//
// Created by annidyfeng on 2019/6/10.
// Copyright © 2019 Tencent. All rights reserved.
//
#import "TUIGroupCreatedCell.h"
@implementation TUIGroupCreatedCell
- (void)fillWithData:(TUIGroupCreatedCellData *)data {
[super fillWithData:data];
}
@end

View File

@@ -0,0 +1,48 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
/**
*
*
* This document declares modules for implementing "more" units.
* More units, that is, the UI interface that appears after clicking the "+" in the lower right corner of the chat interface.
* At present, more units provide four multimedia sending functions of camera, video, picture, and file, and you can also customize it according to your needs.
*/
#import <UIKit/UIKit.h>
#import "TUIInputMoreCellData.h"
/////////////////////////////////////////////////////////////////////////////////
//
// TUIInputMoreCell
//
/////////////////////////////////////////////////////////////////////////////////
@interface TUIInputMoreCell : UICollectionViewCell
/**
* The icons corresponding to more cells are obtained from the image of TUIInputMoreCellData.
* The icons of each unit are different, which are used to visually represent the corresponding functions of the unit.
*/
@property(nonatomic, strong) UIImageView *image;
/**
* The label corresponding to more cells, the text content of which is obtained from the title of TUIInputMoreCellData.
* The names of each unit are different (such as camera, video, file, album, etc.), which are used to display the corresponding functions of the unit in text
* form below the icon.
*/
@property(nonatomic, strong) UILabel *title;
@property(nonatomic, strong) TUIInputMoreCellData *data;
/**
* Whether to disable the default selection behavior encapsulated in TUIKit, such as group live broadcast by default to create live room and other behaviors,
* default: NO
*/
@property(nonatomic, assign) BOOL disableDefaultSelectAction;
- (void)fillWithData:(TUIInputMoreCellData *)data;
+ (CGSize)getSize;
@end

View File

@@ -0,0 +1,67 @@
//
// TMoreCell.m
// UIKit
//
// Created by annidyfeng on 2019/5/22.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIInputMoreCell.h"
#import <TIMCommon/TIMDefine.h>
@implementation TUIInputMoreCell
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setupViews];
}
return self;
}
- (void)setupViews {
_image = [[UIImageView alloc] init];
_image.contentMode = UIViewContentModeScaleAspectFit;
[self addSubview:_image];
_title = [[UILabel alloc] init];
[_title setFont:[UIFont systemFontOfSize:10]];
[_title setTextColor:[UIColor grayColor]];
_title.textAlignment = NSTextAlignmentCenter;
[self addSubview:_title];
}
- (void)fillWithData:(TUIInputMoreCellData *)data {
// set data
_data = data;
self.hidden = (data == nil) ? YES : NO;
_image.image = data.image;
[_title setText:data.title];
// update layout
CGSize menuSize = TMoreCell_Image_Size;
_image.frame = CGRectMake(0, 0, menuSize.width, menuSize.height);
_title.frame = CGRectMake(0, _image.frame.origin.y + _image.frame.size.height, _image.frame.size.width + 10, TMoreCell_Title_Height);
_title.center = CGPointMake(_image.center.x, _title.center.y);
}
+ (CGSize)getSize {
CGSize menuSize = TMoreCell_Image_Size;
return CGSizeMake(menuSize.width, menuSize.height + TMoreCell_Title_Height);
}
@end
@interface IUChatView : UIView
@property(nonatomic, strong) UIView *view;
@end
@implementation IUChatView
- (instancetype)init {
self = [super init];
if (self) {
self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
[self addSubview:self.view];
}
return self;
}
@end

View File

@@ -0,0 +1,16 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <TIMCommon/TIMCommonModel.h>
NS_ASSUME_NONNULL_BEGIN
@class TUIMemberCellData;
@interface TUIMemberCell : TUICommonTableViewCell
- (void)fillWithData:(TUIMemberCellData *)cellData;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,115 @@
//
// TUIMemberCell.m
// TUIChat
//
// Created by xia on 2022/3/11.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIMemberCell.h"
#import <TIMCommon/TIMCommonModel.h>
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIThemeManager.h>
#import "TUIMemberCellData.h"
@interface TUIMemberCell ()
@property(nonatomic, strong) UIImageView *avatarView;
@property(nonatomic, strong) UILabel *titleLabel;
@property(nonatomic, strong) UILabel *detailLabel;
@property(nonatomic, strong) TUIMemberCellData *cellData;
@end
@implementation TUIMemberCell
#pragma mark - Life cycle
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.contentView.backgroundColor = TIMCommonDynamicColor(@"form_bg_color", @"#FFFFFF");
self.avatarView = [[UIImageView alloc] initWithImage:DefaultAvatarImage];
[self.contentView addSubview:self.avatarView];
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.contentView addSubview:self.titleLabel];
self.titleLabel.textColor = TIMCommonDynamicColor(@"form_title_color", @"#000000");
self.detailLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 20)];
[self.contentView addSubview:self.detailLabel];
self.detailLabel.rtlAlignment = TUITextRTLAlignmentTrailing;
self.detailLabel.textColor = TIMCommonDynamicColor(@"form_title_color", @"#000000");
[self setSelectionStyle:UITableViewCellSelectionStyleNone];
self.changeColorWhenTouched = YES;
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
}
#pragma mark - Public
- (void)fillWithData:(TUIMemberCellData *)cellData {
[super fillWithData:cellData];
self.cellData = cellData;
self.titleLabel.text = cellData.title;
[self.avatarView sd_setImageWithURL:cellData.avatarUrL placeholderImage:DefaultAvatarImage];
self.detailLabel.hidden = cellData.detail.length == 0;
self.detailLabel.text = cellData.detail;
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
CGFloat imgWidth = kScale390(34);
[self.avatarView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.height.mas_equalTo(imgWidth);
make.centerY.mas_equalTo(self.contentView.mas_centerY);
make.leading.mas_equalTo(kScale390(12));
}];
if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = imgWidth / 2;
} else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
self.avatarView.layer.masksToBounds = YES;
self.avatarView.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
}
[self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self.avatarView.mas_centerY);
make.leading.mas_equalTo(self.avatarView.mas_trailing).mas_offset(12);
make.height.mas_equalTo(20);
make.trailing.lessThanOrEqualTo(self.detailLabel.mas_leading).mas_offset(-5);
}];
[self.detailLabel sizeToFit];
[self.detailLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self.avatarView.mas_centerY);
make.height.mas_equalTo(20);
make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(-3);
make.width.mas_equalTo(self.detailLabel.frame.size.width);
}];
}
@end

View File

@@ -0,0 +1,75 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <TIMCommon/TUIBubbleMessageCell.h>
#import <TIMCommon/TUITextView.h>
#import "TUIChatDefine.h"
#import "TUITextMessageCellData.h"
@class TUITextView;
typedef void (^TUIChatSelectAllContentCallback)(BOOL);
@interface TUITextMessageCell : TUIBubbleMessageCell <UITextViewDelegate>
/**
*
* TextView for display text message content
*/
@property(nonatomic, strong) TUITextView *textView;
/**
*
* Selected text content
*/
@property(nonatomic, strong) NSString *selectContent;
/**
*
* Callback for selected all text
*/
@property(nonatomic, strong) TUIChatSelectAllContentCallback selectAllContentContent;
/// Data for text message cell.
@property(nonatomic, strong) TUITextMessageCellData *textData;
@property(nonatomic, strong) UIImageView *voiceReadPoint;
- (void)fillWithData:(TUITextMessageCellData *)data;
@end
@interface TUITextMessageCell (TUILayoutConfiguration)
/**
*
* The color of label which displays the text message content.
* Used when the message direction is send.
*/
@property(nonatomic, class) UIColor *outgoingTextColor;
/**
*
* The font of label which displays the text message content.
* Used when the message direction is send.
*/
@property(nonatomic, class) UIFont *outgoingTextFont;
/**
*
* The color of label which displays the text message content.
* Used when the message direction is received.
*/
@property(nonatomic, class) UIColor *incommingTextColor;
/**
*
* The font of label which displays the text message content.
* Used when the message direction is received.
*/
@property(nonatomic, class) UIFont *incommingTextFont;
+ (void)setMaxTextSize:(CGSize)maxTextSz;
@end

View File

@@ -0,0 +1,339 @@
//
// TUITextMessageCell.m
// UIKit
//
// Created by annidyfeng on 2019/5/30.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUITextMessageCell.h"
#import <TIMCommon/TIMCommonModel.h>
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUICore.h>
#import <TUICore/TUIGlobalization.h>
#import "TUIFaceView.h"
#import <TUICore/UIColor+TUIHexColor.h>
#ifndef CGFLOAT_CEIL
#ifdef CGFLOAT_IS_DOUBLE
#define CGFLOAT_CEIL(value) ceil(value)
#else
#define CGFLOAT_CEIL(value) ceilf(value)
#endif
#endif
@interface TUITextMessageCell ()<TUITextViewDelegate>
@end
@implementation TUITextMessageCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.textView = [[TUITextView alloc] init];
self.textView.backgroundColor = [UIColor clearColor];
self.textView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0);
self.textView.textContainer.lineFragmentPadding = 0;
self.textView.scrollEnabled = NO;
self.textView.editable = NO;
self.textView.delegate = self;
self.textView.tuiTextViewDelegate = self;
self.bubbleView.userInteractionEnabled = YES;
[self.bubbleView addSubview:self.textView];
self.bottomContainer = [[UIView alloc] init];
[self.contentView addSubview:self.bottomContainer];
self.voiceReadPoint = [[UIImageView alloc] init];
self.voiceReadPoint.backgroundColor = [UIColor redColor];
self.voiceReadPoint.frame = CGRectMake(0, 0, 5, 5);
self.voiceReadPoint.hidden = YES;
[self.voiceReadPoint.layer setCornerRadius:self.voiceReadPoint.frame.size.width / 2];
[self.voiceReadPoint.layer setMasksToBounds:YES];
[self.bubbleView addSubview:self.voiceReadPoint];
}
return self;
}
- (void)prepareForReuse {
[super prepareForReuse];
for (UIView *view in self.bottomContainer.subviews) {
[view removeFromSuperview];
}
}
- (void)onLongPressTextViewMessage:(UITextView *)textView {
if (self.delegate && [self.delegate respondsToSelector:@selector(onLongPressMessage:)]) {
[self.delegate onLongPressMessage:self];
}
}
// Override
- (void)notifyBottomContainerReadyOfData:(TUIMessageCellData *)cellData {
NSDictionary *param = @{TUICore_TUIChatExtension_BottomContainer_CellData : self.textData};
[TUICore raiseExtension:TUICore_TUIChatExtension_BottomContainer_ClassicExtensionID parentView:self.bottomContainer param:param];
}
- (void)fillWithData:(TUITextMessageCellData *)data {
// set data
[super fillWithData:data];
self.textData = data;
self.selectContent = data.content;
self.voiceReadPoint.hidden = !data.showUnreadPoint;
self.bottomContainer.hidden = CGSizeEqualToSize(data.bottomContainerSize, CGSizeZero);
UIColor *textColor = self.class.incommingTextColor;
UIFont *textFont = self.class.incommingTextFont;
if (data.direction == MsgDirectionIncoming) {
textColor = self.class.incommingTextColor;
textFont = self.class.incommingTextFont;
} else {
textColor = self.class.outgoingTextColor;
textFont = self.class.outgoingTextFont;
}
self.textView.attributedText = [data getContentAttributedString:textFont];
self.textView.textColor = textColor;
self.textView.font = textFont;
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[self layoutIfNeeded];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[super updateConstraints];
[self.textView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self.bubbleView.mas_leading).mas_offset(self.textData.textOrigin.x);
make.top.mas_equalTo(self.bubbleView.mas_top).mas_offset(self.textData.textOrigin.y);
make.size.mas_equalTo(self.textData.textSize);
}];
if (self.voiceReadPoint.hidden == NO) {
[self.voiceReadPoint mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.bubbleView);
make.leading.mas_equalTo(self.bubbleView.mas_trailing).mas_offset(1);
make.size.mas_equalTo(CGSizeMake(5, 5));
}];
}
BOOL hasRiskContent = self.messageData.innerMessage.hasRiskContent;
if (hasRiskContent ) {
[self.securityStrikeView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.textView.mas_bottom);
make.width.mas_equalTo(self.bubbleView);
make.bottom.mas_equalTo(self.container).mas_offset(- self.messageData.messageContainerAppendSize.height);
}];
}
[self layoutBottomContainer];
}
- (void)layoutBottomContainer {
if (CGSizeEqualToSize(self.textData.bottomContainerSize, CGSizeZero)) {
return;
}
CGSize size = self.textData.bottomContainerSize;
[self.bottomContainer mas_remakeConstraints:^(MASConstraintMaker *make) {
if (self.textData.direction == MsgDirectionIncoming) {
make.leading.mas_equalTo(self.container.mas_leading);
}
else {
make.trailing.mas_equalTo(self.container.mas_trailing);
}
make.top.mas_equalTo(self.container.mas_bottom).offset(6);
make.size.mas_equalTo(size);
}];
CGFloat repliesBtnTextWidth = self.messageModifyRepliesButton.frame.size.width;
if (!self.messageModifyRepliesButton.hidden) {
[self.messageModifyRepliesButton mas_remakeConstraints:^(MASConstraintMaker *make) {
if (self.textData.direction == MsgDirectionIncoming) {
make.leading.mas_equalTo(self.container.mas_leading);
} else {
make.trailing.mas_equalTo(self.container.mas_trailing);
}
make.top.mas_equalTo(self.bottomContainer.mas_bottom);
make.size.mas_equalTo(CGSizeMake(repliesBtnTextWidth + 10, 30));
}];
}
}
- (void)textViewDidChangeSelection:(UITextView *)textView {
NSAttributedString *selectedString = [textView.attributedText attributedSubstringFromRange:textView.selectedRange];
if (self.selectAllContentContent && selectedString.length > 0) {
if (selectedString.length == textView.attributedText.length) {
self.selectAllContentContent(YES);
} else {
self.selectAllContentContent(NO);
}
}
if (selectedString.length > 0) {
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];
[attributedString appendAttributedString:selectedString];
NSUInteger offsetLocation = 0;
for (NSDictionary *emojiLocation in self.textData.emojiLocations) {
NSValue *key = emojiLocation.allKeys.firstObject;
NSAttributedString *originStr = emojiLocation[key];
NSRange currentRange = [key rangeValue];
/**
* After each emoji is replaced, the length of the string will change, and the actual location of the emoji will also change accordingly.
*/
currentRange.location += offsetLocation;
if (currentRange.location >= textView.selectedRange.location) {
currentRange.location -= textView.selectedRange.location;
if (currentRange.location + currentRange.length <= attributedString.length) {
[attributedString replaceCharactersInRange:currentRange withAttributedString:originStr];
offsetLocation += originStr.length - currentRange.length;
}
}
}
self.selectContent = attributedString.string;
} else {
self.selectContent = nil;
[[NSNotificationCenter defaultCenter] postNotificationName:@"kTUIChatPopMenuWillHideNotification" object:nil];
}
}
#pragma mark - TUIMessageCellProtocol
+ (CGFloat)getEstimatedHeight:(TUIMessageCellData *)data {
return 60.f;
}
+ (CGFloat)getHeight:(TUIMessageCellData *)data withWidth:(CGFloat)width {
CGFloat height = [super getHeight:data withWidth:width];
if (data.bottomContainerSize.height > 0) {
height += data.bottomContainerSize.height + kScale375(6);
}
return height;
}
static CGSize gMaxTextSize;
+ (void)setMaxTextSize:(CGSize)maxTextSz {
gMaxTextSize = maxTextSz;
}
+ (CGSize)getContentSize:(TUIMessageCellData *)data {
NSAssert([data isKindOfClass:TUITextMessageCellData.class], @"data must be kind of TUITextMessageCellData");
TUITextMessageCellData *textCellData = (TUITextMessageCellData *)data;
NSAttributedString *attributeString = [textCellData getContentAttributedString:
data.direction == MsgDirectionIncoming ? self.incommingTextFont : self.outgoingTextFont];
if (CGSizeEqualToSize(gMaxTextSize, CGSizeZero)) {
gMaxTextSize = CGSizeMake(TTextMessageCell_Text_Width_Max, MAXFLOAT);
}
CGSize contentSize = [textCellData getContentAttributedStringSize:attributeString maxTextSize:gMaxTextSize];
textCellData.textSize = contentSize;
CGPoint textOrigin = CGPointMake(textCellData.cellLayout.bubbleInsets.left,
textCellData.cellLayout.bubbleInsets.top);
textCellData.textOrigin = textOrigin;
CGFloat height = contentSize.height;
CGFloat width = contentSize.width;
height += textCellData.cellLayout.bubbleInsets.top;
height += textCellData.cellLayout.bubbleInsets.bottom;
width += textCellData.cellLayout.bubbleInsets.left;
width += textCellData.cellLayout.bubbleInsets.right;
if (textCellData.direction == MsgDirectionIncoming) {
height = MAX(height, TUIBubbleMessageCell.incommingBubble.size.height);
} else {
height = MAX(height, TUIBubbleMessageCell.outgoingBubble.size.height);
}
BOOL hasRiskContent = textCellData.innerMessage.hasRiskContent;
if (hasRiskContent) {
width = MAX(width, 200);// width must more than TIMCommonLocalizableString(TUIKitMessageTypeSecurityStrike)
height += kTUISecurityStrikeViewTopLineMargin;
height += kTUISecurityStrikeViewTopLineToBottom;
}
CGSize size = CGSizeMake(width, height);
return size;
}
@end
@implementation TUITextMessageCell (TUILayoutConfiguration)
+ (void)initialize {
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(onThemeChanged) name:TUIDidApplyingThemeChangedNotfication object:nil];
}
static UIColor *gOutgoingTextColor;
+ (UIColor *)outgoingTextColor {
if (!gOutgoingTextColor) {
gOutgoingTextColor = TUIChatDynamicColor(@"chat_text_message_send_text_color", @"#000000");
}
return gOutgoingTextColor;
}
+ (void)setOutgoingTextColor:(UIColor *)outgoingTextColor {
gOutgoingTextColor = outgoingTextColor;
}
static UIFont *gOutgoingTextFont;
+ (UIFont *)outgoingTextFont {
if (!gOutgoingTextFont) {
gOutgoingTextFont = [UIFont systemFontOfSize:16];
}
return gOutgoingTextFont;
}
+ (void)setOutgoingTextFont:(UIFont *)outgoingTextFont {
gOutgoingTextFont = outgoingTextFont;
}
static UIColor *gIncommingTextColor;
+ (UIColor *)incommingTextColor {
if (!gIncommingTextColor) {
gIncommingTextColor = TUIChatDynamicColor(@"chat_text_message_receive_text_color", @"#000000");
}
return gIncommingTextColor;
}
+ (void)setIncommingTextColor:(UIColor *)incommingTextColor {
gIncommingTextColor = incommingTextColor;
}
static UIFont *gIncommingTextFont;
+ (UIFont *)incommingTextFont {
if (!gIncommingTextFont) {
gIncommingTextFont = [UIFont systemFontOfSize:16];
}
return gIncommingTextFont;
}
+ (void)setIncommingTextFont:(UIFont *)incommingTextFont {
gIncommingTextFont = incommingTextFont;
}
+ (void)onThemeChanged {
gOutgoingTextColor = nil;
gIncommingTextColor = nil;
}
@end