提交
This commit is contained in:
17
TUIKit/TIMCommon/CommonModel/NSTimer+TUISafe.h
Normal file
17
TUIKit/TIMCommon/CommonModel/NSTimer+TUISafe.h
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// NSTimer+TUISafe.h
|
||||
// TUICore
|
||||
//
|
||||
// Created by wyl on 2022/7/5.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSTimer (TUISafe)
|
||||
+ (NSTimer *)tui_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
21
TUIKit/TIMCommon/CommonModel/NSTimer+TUISafe.m
Normal file
21
TUIKit/TIMCommon/CommonModel/NSTimer+TUISafe.m
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// NSTimer+TUISafe.m
|
||||
// TUICore
|
||||
//
|
||||
// Created by wyl on 2022/7/5.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSTimer+TUISafe.h"
|
||||
|
||||
@implementation NSTimer (TUISafe)
|
||||
+ (NSTimer *)tui_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block {
|
||||
return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(tui_callBlock:) userInfo:[block copy] repeats:repeats];
|
||||
}
|
||||
|
||||
+ (void)tui_callBlock:(NSTimer *)timer {
|
||||
void (^block)(NSTimer *timer) = timer.userInfo;
|
||||
!block ?: block(timer);
|
||||
}
|
||||
|
||||
@end
|
||||
27
TUIKit/TIMCommon/CommonModel/TIMCommonMediator.h
Normal file
27
TUIKit/TIMCommon/CommonModel/TIMCommonMediator.h
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// TIMCommonMediator.h
|
||||
// TUIEmojiPlugin
|
||||
//
|
||||
// Created by cologne on 2023/11/14.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TIMCommonMediator : NSObject
|
||||
|
||||
+ (instancetype)share;
|
||||
|
||||
/// Protocol : Class
|
||||
/// Register Protocol : Class
|
||||
- (void)registerService:(Protocol *)service class:(Class)cls;
|
||||
|
||||
/// Protocol [Class new]
|
||||
/// get [class new] by Protocol
|
||||
- (id)getObject:(Protocol *)service;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
41
TUIKit/TIMCommon/CommonModel/TIMCommonMediator.m
Normal file
41
TUIKit/TIMCommon/CommonModel/TIMCommonMediator.m
Normal file
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// TIMCommonMediator.m
|
||||
// TUIEmojiPlugin
|
||||
//
|
||||
// Created by cologne on 2023/11/14.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TIMCommonMediator.h"
|
||||
|
||||
@interface TIMCommonMediator()
|
||||
@property (nonatomic, strong) NSMutableDictionary *map;
|
||||
@end
|
||||
|
||||
@implementation TIMCommonMediator
|
||||
|
||||
+ (instancetype)share {
|
||||
static TIMCommonMediator *mediator = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
mediator = [TIMCommonMediator new];
|
||||
mediator.map = [NSMutableDictionary new];
|
||||
});
|
||||
return mediator;
|
||||
}
|
||||
|
||||
- (void)registerService:(Protocol *)service class:(Class)cls {
|
||||
if (!service || !cls) return;
|
||||
self.map[NSStringFromProtocol(service)] = cls;
|
||||
}
|
||||
|
||||
- (id)getObject:(Protocol *)service {
|
||||
if (!service) return nil;
|
||||
Class cls = self.map[NSStringFromProtocol(service)];
|
||||
id obj = [cls new];
|
||||
if ([obj conformsToProtocol:service]) {
|
||||
return obj;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@end
|
||||
587
TUIKit/TIMCommon/CommonModel/TIMCommonModel.h
Normal file
587
TUIKit/TIMCommon/CommonModel/TIMCommonModel.h
Normal file
@@ -0,0 +1,587 @@
|
||||
//
|
||||
// TIMCommonModel.h
|
||||
// TIMCommon
|
||||
//
|
||||
// Created by cologne on 2023/3/14.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <TUICore/TUICommonModel.h>
|
||||
#import "TIMDefine.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIPopView
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
@class TUIPopView;
|
||||
@protocol TUIPopViewDelegate <NSObject>
|
||||
- (void)popView:(TUIPopView *)popView didSelectRowAtIndex:(NSInteger)index;
|
||||
@end
|
||||
|
||||
@interface TUIPopView : UIView
|
||||
@property(nonatomic, strong) UITableView *tableView;
|
||||
@property(nonatomic, assign) CGPoint arrowPoint;
|
||||
@property(nonatomic, weak) id<TUIPopViewDelegate> delegate;
|
||||
- (void)setData:(NSMutableArray *)data;
|
||||
- (void)showInWindow:(UIWindow *)window;
|
||||
@end
|
||||
|
||||
@interface TUIPopCellData : NSObject
|
||||
@property(nonatomic, strong) UIImage *image;
|
||||
@property(nonatomic, strong) NSString *title;
|
||||
@end
|
||||
|
||||
@interface TUIPopCell : UITableViewCell
|
||||
@property(nonatomic, strong) UIImageView *image;
|
||||
@property(nonatomic, strong) UILabel *title;
|
||||
+ (CGFloat)getHeight;
|
||||
- (void)setData:(TUIPopCellData *)data;
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIModifyView
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
@class TUIModifyView;
|
||||
@protocol TUIModifyViewDelegate <NSObject>
|
||||
- (void)modifyView:(TUIModifyView *)modifyView didModiyContent:(NSString *)content;
|
||||
@end
|
||||
|
||||
@interface TUIModifyViewData : NSObject
|
||||
@property(nonatomic, strong) NSString *title;
|
||||
@property(nonatomic, strong) NSString *content;
|
||||
@property(nonatomic, strong) NSString *desc;
|
||||
@property(nonatomic, assign) BOOL enableNull;
|
||||
@end
|
||||
|
||||
@interface TUIModifyView : UIView
|
||||
@property(nonatomic, strong) UIView *container;
|
||||
@property(nonatomic, strong) UILabel *title;
|
||||
@property(nonatomic, strong) UITextField *content;
|
||||
@property(nonatomic, strong) UILabel *descLabel;
|
||||
@property(nonatomic, strong) UIButton *confirm;
|
||||
@property(nonatomic, strong) UIView *hLine;
|
||||
@property(nonatomic, weak) id<TUIModifyViewDelegate> delegate;
|
||||
- (void)setData:(TUIModifyViewData *)data;
|
||||
- (void)showInWindow:(UIWindow *)window;
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUINaviBarIndicatorView
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
@interface TUINaviBarIndicatorView : UIView
|
||||
|
||||
@property(nonatomic, strong) UIActivityIndicatorView *indicator;
|
||||
|
||||
@property(nonatomic, strong) UILabel *label;
|
||||
|
||||
@property(nonatomic, assign) CGFloat maxLabelLength;
|
||||
|
||||
- (void)setTitle:(NSString *)title;
|
||||
|
||||
- (void)startAnimating;
|
||||
|
||||
- (void)stopAnimating;
|
||||
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUICommonCell & data
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@interface TUICommonCellData : NSObject
|
||||
@property(strong) NSString *reuseId;
|
||||
@property(nonatomic, assign) SEL cselector;
|
||||
@property(nonatomic, strong) NSDictionary *ext;
|
||||
- (CGFloat)heightOfWidth:(CGFloat)width;
|
||||
- (CGFloat)estimatedHeight;
|
||||
@end
|
||||
|
||||
@interface TUICommonTableViewCell : UITableViewCell
|
||||
|
||||
@property(readonly) TUICommonCellData *data;
|
||||
@property UIColor *colorWhenTouched;
|
||||
@property BOOL changeColorWhenTouched;
|
||||
|
||||
- (void)fillWithData:(TUICommonCellData *)data;
|
||||
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUICommonTextCell & data
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
@interface TUICommonTextCellData : TUICommonCellData
|
||||
|
||||
@property NSString *key;
|
||||
@property NSString *value;
|
||||
@property BOOL showAccessory;
|
||||
@property UIColor *keyColor;
|
||||
@property UIColor *valueColor;
|
||||
@property BOOL enableMultiLineValue;
|
||||
|
||||
@property(nonatomic, assign) UIEdgeInsets keyEdgeInsets;
|
||||
|
||||
@end
|
||||
|
||||
@interface TUICommonTextCell : TUICommonTableViewCell
|
||||
@property UILabel *keyLabel;
|
||||
@property UILabel *valueLabel;
|
||||
@property(readonly) TUICommonTextCellData *textData;
|
||||
|
||||
- (void)fillWithData:(TUICommonTextCellData *)data;
|
||||
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUICommonSwitchCell & data
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
@interface TUICommonSwitchCellData : TUICommonCellData
|
||||
|
||||
@property NSString *title;
|
||||
@property NSString *desc;
|
||||
@property(getter=isOn) BOOL on;
|
||||
@property CGFloat margin;
|
||||
@property SEL cswitchSelector;
|
||||
|
||||
@property(nonatomic, assign) BOOL displaySeparatorLine;
|
||||
|
||||
@property(nonatomic, assign) BOOL disableChecked;
|
||||
|
||||
@end
|
||||
|
||||
@interface TUICommonSwitchCell : TUICommonTableViewCell
|
||||
@property UILabel *titleLabel; // main title label
|
||||
@property UILabel *descLabel; // detail title label below the main title label, used for explaining details
|
||||
@property UISwitch *switcher;
|
||||
|
||||
@property(readonly) TUICommonSwitchCellData *switchData;
|
||||
|
||||
- (void)fillWithData:(TUICommonSwitchCellData *)data;
|
||||
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIButtonCell & data
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
typedef enum : NSUInteger {
|
||||
ButtonGreen,
|
||||
ButtonWhite,
|
||||
ButtonRedText,
|
||||
ButtonBule,
|
||||
} TUIButtonStyle;
|
||||
|
||||
@interface TUIButtonCellData : TUICommonCellData
|
||||
@property(nonatomic, strong) NSString *title;
|
||||
@property SEL cbuttonSelector;
|
||||
@property TUIButtonStyle style;
|
||||
@property(nonatomic, strong) UIColor *textColor;
|
||||
@property(nonatomic, assign) BOOL hideSeparatorLine;
|
||||
@end
|
||||
|
||||
@interface TUIButtonCell : TUICommonTableViewCell
|
||||
@property(nonatomic, strong) UIButton *button;
|
||||
@property TUIButtonCellData *buttonData;
|
||||
|
||||
- (void)fillWithData:(TUIButtonCellData *)data;
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIGroupPendencyCell & data
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
#define TUIGroupPendencyCellData_onPendencyChanged @"TUIGroupPendencyCellData_onPendencyChanged"
|
||||
|
||||
@interface TUIGroupPendencyCellData : TUICommonCellData
|
||||
|
||||
@property(nonatomic, strong) NSString *groupId;
|
||||
|
||||
@property(nonatomic, strong) NSString *fromUser;
|
||||
|
||||
@property(nonatomic, strong) NSString *toUser;
|
||||
|
||||
@property(readonly) V2TIMGroupApplication *pendencyItem;
|
||||
|
||||
@property NSURL *avatarUrl;
|
||||
|
||||
@property NSString *title;
|
||||
|
||||
/**
|
||||
* The joining group introduction of the requester. Such as "Xiao Ming applied to join the group".
|
||||
*/
|
||||
@property NSString *requestMsg;
|
||||
|
||||
/**
|
||||
* Agree or Not
|
||||
* YES: Agree; NO: Indicates that the current request was not granted, but does not mean that the request has been denied.
|
||||
*/
|
||||
@property BOOL isAccepted;
|
||||
|
||||
/**
|
||||
*
|
||||
* Refuse or Not
|
||||
* YES: Refuse; NO: Indicates that the current request is not denied, but does not mean that the request has been granted.
|
||||
*/
|
||||
@property BOOL isRejectd;
|
||||
@property SEL cbuttonSelector;
|
||||
|
||||
- (instancetype)initWithPendency:(V2TIMGroupApplication *)args;
|
||||
|
||||
typedef void (^TUIGroupPendencyCellDataSuccessCallback)(void);
|
||||
typedef void (^TUIGroupPendencyCellDataFailureCallback)(int code, NSString *msg);
|
||||
|
||||
- (void)agreeWithSuccess:(TUIGroupPendencyCellDataSuccessCallback)success
|
||||
failure:(TUIGroupPendencyCellDataFailureCallback)failure;
|
||||
|
||||
- (void)rejectWithSuccess:(TUIGroupPendencyCellDataSuccessCallback)success
|
||||
failure:(TUIGroupPendencyCellDataFailureCallback)failure;
|
||||
- (void)accept;
|
||||
- (void)reject;
|
||||
|
||||
@end
|
||||
|
||||
@interface TUIGroupPendencyCell : TUICommonTableViewCell
|
||||
|
||||
@property UIImageView *avatarView;
|
||||
|
||||
@property UILabel *titleLabel;
|
||||
|
||||
@property UILabel *addWordingLabel;
|
||||
|
||||
@property UIButton *agreeButton;
|
||||
|
||||
@property TUIGroupPendencyCellData *pendencyData;
|
||||
|
||||
- (void)fillWithData:(TUIGroupPendencyCellData *)pendencyData;
|
||||
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIFaceCell & data
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* 【Module name】 TUIFaceCellData
|
||||
* 【Function description]】The name and local storage path of the stored emoticon.
|
||||
*/
|
||||
@interface TUIFaceCellData : NSObject
|
||||
|
||||
/**
|
||||
* The name of emoticon
|
||||
*/
|
||||
@property(nonatomic, strong) NSString *name;
|
||||
|
||||
/**
|
||||
* The localized name of the emoticon (the attribute used for internationalization, if it is empty or the length is 0, the name is displayed by default)
|
||||
*/
|
||||
@property(nonatomic, copy) NSString *localizableName;
|
||||
|
||||
/**
|
||||
* The storage path of the emoticon cached locally.
|
||||
*/
|
||||
@property(nonatomic, strong) NSString *path;
|
||||
@end
|
||||
|
||||
/**
|
||||
* 【Module name】TUIFaceCell
|
||||
* 【Function description】 Store the image of the emoticon, and initialize the Cell according to TUIFaceCellData.
|
||||
* In the emoticon view, TUIFaceCell is the unit displayed on the interface.
|
||||
*/
|
||||
@interface TUIFaceCell : UICollectionViewCell
|
||||
|
||||
/**
|
||||
* The image view for displaying emoticon
|
||||
*/
|
||||
@property(nonatomic, strong) UIImageView *face;
|
||||
@property(nonatomic, strong) UIImage *staicImage;
|
||||
@property(nonatomic, strong) UIImage *gifImage;
|
||||
@property(nonatomic, copy) void(^longPressCallback)(UILongPressGestureRecognizer *recognizer);
|
||||
- (void)setData:(TUIFaceCellData *)data;
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIFaceGroup
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* 【Module name】 TUIFaceGroup
|
||||
* 【Function description】 It is used to realize the grouping of emoticon, which is convenient for users to browse and select under different emoticon themes.
|
||||
* This class stores the index of each emoticon group, so that FaceView can locate each emoticon group.
|
||||
* At the same time, this class stores the path of all emoticon pictures in an emoticon group, and provides data such as the number of lines, the number of
|
||||
* emoticons in each line, etc., to locate specific emoticons
|
||||
*/
|
||||
@interface TUIFaceGroup : NSObject
|
||||
|
||||
/**
|
||||
* Index of emoticons group, begining with zero.
|
||||
*/
|
||||
@property(nonatomic, assign) int groupIndex;
|
||||
|
||||
/**
|
||||
* The resource path of the entire expression group
|
||||
*/
|
||||
@property(nonatomic, strong) NSString *groupPath;
|
||||
|
||||
/**
|
||||
* The number of lines of emoticons in the emoticon group
|
||||
*/
|
||||
@property(nonatomic, assign) int rowCount;
|
||||
|
||||
/**
|
||||
* The number of emoticons contained in each line
|
||||
*/
|
||||
@property(nonatomic, assign) int itemCountPerRow;
|
||||
|
||||
@property(nonatomic, strong) NSMutableArray *faces;
|
||||
|
||||
@property(nonatomic, strong) NSDictionary *facesMap;
|
||||
|
||||
/**
|
||||
* The flag of indicating whether to display the delete button
|
||||
* When set to YES, FaceView will display a "delete" icon in the lower right corner of the emoticon view. Clicking the icon can delete the entered emoticon
|
||||
* directly without evoking the keyboard.
|
||||
*/
|
||||
@property(nonatomic, assign) BOOL needBackDelete;
|
||||
|
||||
/**
|
||||
* The path to the cover image of the emoticon group
|
||||
*/
|
||||
@property(nonatomic, strong) NSString *menuPath;
|
||||
|
||||
@property(nonatomic, strong) TUIFaceGroup *recentGroup;
|
||||
|
||||
@property(nonatomic, assign) BOOL isNeedAddInInputBar;
|
||||
|
||||
@property(nonatomic, copy) NSString *groupName;
|
||||
|
||||
@end
|
||||
|
||||
@interface TUIEmojiTextAttachment : NSTextAttachment
|
||||
|
||||
@property(nonatomic, strong) TUIFaceCellData *faceCellData;
|
||||
|
||||
@property(nonatomic, copy) NSString *emojiTag;
|
||||
|
||||
@property(nonatomic, assign) CGSize emojiSize; // For emoji image size
|
||||
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIUnReadView
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
@interface TUIUnReadView : UIView
|
||||
|
||||
/**
|
||||
* The label of displaying unread message count
|
||||
*/
|
||||
@property(nonatomic, strong) UILabel *unReadLabel;
|
||||
|
||||
/**
|
||||
* Set the unread message count
|
||||
*/
|
||||
- (void)setNum:(NSInteger)num;
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIConversationPin
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
extern NSString *kTopConversationListChangedNotification;
|
||||
|
||||
@interface TUIConversationPin : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
/**
|
||||
*
|
||||
* Getting the list of pinned conversations
|
||||
*/
|
||||
- (NSArray *)topConversationList;
|
||||
|
||||
/**
|
||||
*
|
||||
* Pin the conversation
|
||||
*/
|
||||
- (void)addTopConversation:(NSString *)conv callback:(void (^__nullable)(BOOL success, NSString *__nullable errorMessage))callback;
|
||||
/**
|
||||
*
|
||||
* Remove pinned conversations
|
||||
*/
|
||||
- (void)removeTopConversation:(NSString *)conv callback:(void (^__nullable)(BOOL success, NSString *__nullable errorMessage))callback;
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUICommonContactSelectCellData
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
@interface TUICommonContactSelectCellData : TUICommonCellData
|
||||
|
||||
@property(nonatomic, strong) NSString *identifier;
|
||||
@property(nonatomic, strong) NSString *title;
|
||||
@property(nonatomic, strong) NSURL *avatarUrl;
|
||||
@property(nonatomic, strong) UIImage *avatarImage;
|
||||
|
||||
@property(nonatomic, getter=isSelected) BOOL selected;
|
||||
@property(nonatomic, getter=isEnabled) BOOL enabled;
|
||||
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUICommonContactListPickerCell
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
@interface TUICommonContactListPickerCell : UICollectionViewCell
|
||||
|
||||
@property UIImageView *avatar;
|
||||
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIContactListPickerOnCancel
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
typedef void (^TUIContactListPickerOnCancel)(TUICommonContactSelectCellData *data);
|
||||
|
||||
@interface TUIContactListPicker : UIControl
|
||||
|
||||
@property(nonatomic, strong, readonly) UIButton *accessoryBtn;
|
||||
@property(nonatomic, strong) NSArray<TUICommonContactSelectCellData *> *selectArray;
|
||||
@property(nonatomic, copy) TUIContactListPickerOnCancel onCancel;
|
||||
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIProfileCardCell & vc
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
@class TUIProfileCardCell;
|
||||
@protocol TUIProfileCardDelegate <NSObject>
|
||||
- (void)didTapOnAvatar:(TUIProfileCardCell *)cell;
|
||||
@end
|
||||
|
||||
@interface TUIProfileCardCellData : TUICommonCellData
|
||||
@property(nonatomic, strong) UIImage *avatarImage;
|
||||
@property(nonatomic, strong) NSURL *avatarUrl;
|
||||
@property(nonatomic, strong) NSString *name;
|
||||
@property(nonatomic, strong) NSString *identifier;
|
||||
@property(nonatomic, strong) NSString *signature;
|
||||
@property(nonatomic, strong) UIImage *genderIconImage;
|
||||
@property(nonatomic, strong) NSString *genderString;
|
||||
@property BOOL showAccessory;
|
||||
@property BOOL showSignature;
|
||||
@end
|
||||
|
||||
@interface TUIProfileCardCell : TUICommonTableViewCell
|
||||
@property(nonatomic, strong) UIImageView *avatar;
|
||||
@property(nonatomic, strong) UILabel *name;
|
||||
@property(nonatomic, strong) UILabel *identifier;
|
||||
@property(nonatomic, strong) UILabel *signature;
|
||||
@property(nonatomic, strong) UIImageView *genderIcon;
|
||||
@property(nonatomic, strong) TUIProfileCardCellData *cardData;
|
||||
@property(nonatomic, weak) id<TUIProfileCardDelegate> delegate;
|
||||
- (void)fillWithData:(TUIProfileCardCellData *)data;
|
||||
@end
|
||||
|
||||
@interface TUIAvatarViewController : UIViewController
|
||||
|
||||
@property(nonatomic, strong) TUIProfileCardCellData *avatarData;
|
||||
|
||||
@end
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TUISelectAvatarType) {
|
||||
TUISelectAvatarTypeUserAvatar,
|
||||
TUISelectAvatarTypeGroupAvatar,
|
||||
TUISelectAvatarTypeCover,
|
||||
TUISelectAvatarTypeConversationBackGroundCover,
|
||||
};
|
||||
|
||||
@interface TUISelectAvatarCardItem : NSObject
|
||||
@property(nonatomic, strong) NSString *posterUrlStr;
|
||||
@property(nonatomic, assign) BOOL isSelect;
|
||||
@property(nonatomic, copy) NSString *fullUrlStr;
|
||||
@property(nonatomic, assign) BOOL isDefaultBackgroundItem;
|
||||
@property(nonatomic, assign) BOOL isGroupGridAvatar;
|
||||
@property(nonatomic, copy) NSString *createGroupType;
|
||||
@property(nonatomic, strong) UIImage *cacheGroupGridAvatarImage;
|
||||
@end
|
||||
|
||||
@interface TUISelectAvatarController : UIViewController
|
||||
@property(nonatomic, copy) void (^selectCallBack)(NSString *urlStr);
|
||||
@property(nonatomic, assign) TUISelectAvatarType selectAvatarType;
|
||||
@property(nonatomic, copy) NSString *profilFaceURL;
|
||||
@property(nonatomic, strong) UIImage *cacheGroupGridAvatarImage;
|
||||
@property(nonatomic, copy) NSString *createGroupType;
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUICommonAvatarCell & Data
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
@interface TUICommonAvatarCellData : TUICommonCellData
|
||||
;
|
||||
@property(nonatomic, strong) NSString *key;
|
||||
@property(nonatomic, strong) NSString *value;
|
||||
@property BOOL showAccessory;
|
||||
@property(nonatomic, strong) UIImage *avatarImage;
|
||||
@property(nonatomic, strong) NSURL *avatarUrl;
|
||||
|
||||
@end
|
||||
|
||||
@interface TUICommonAvatarCell : TUICommonTableViewCell
|
||||
@property UILabel *keyLabel;
|
||||
@property UILabel *valueLabel;
|
||||
@property UIImageView *avatar;
|
||||
@property(readonly) TUICommonAvatarCellData *avatarData;
|
||||
|
||||
- (void)fillWithData:(TUICommonAvatarCellData *)avatarData;
|
||||
|
||||
@end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIConversationGroupItem
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
extern NSUInteger kConversationMarkStarType;
|
||||
@interface TUIConversationGroupItem : NSObject
|
||||
@property(nonatomic, strong) NSString *groupName;
|
||||
@property(nonatomic, assign) NSInteger unreadCount;
|
||||
@property(nonatomic, assign) NSInteger groupIndex;
|
||||
@property(nonatomic, assign) BOOL isShow;
|
||||
@property(nonatomic, strong) UIButton *groupBtn;
|
||||
@end
|
||||
|
||||
|
||||
@interface TUISendMessageAppendParams : NSObject
|
||||
@property (nonatomic, assign) BOOL isSendPushInfo;
|
||||
@property (nonatomic, assign) BOOL isOnlineUserOnly;
|
||||
@property (nonatomic, assign) V2TIMMessagePriority priority;
|
||||
+ (instancetype)defaultConfig;
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
2437
TUIKit/TIMCommon/CommonModel/TIMCommonModel.m
Normal file
2437
TUIKit/TIMCommon/CommonModel/TIMCommonModel.m
Normal file
File diff suppressed because it is too large
Load Diff
44
TUIKit/TIMCommon/CommonModel/TIMConfig.h
Normal file
44
TUIKit/TIMCommon/CommonModel/TIMConfig.h
Normal file
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// TIMConfig.h
|
||||
// Pods
|
||||
//
|
||||
// Created by cologne on 2023/3/14.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <TUICore/TUIConfig.h>
|
||||
#import "TIMCommonModel.h"
|
||||
#import "TIMDefine.h"
|
||||
|
||||
@class TUIFaceCellData;
|
||||
@class TUIFaceGroup;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TIMConfig : NSObject
|
||||
|
||||
+ (TIMConfig *)defaultConfig;
|
||||
/**
|
||||
* In respect for the copyright of the emoji design, the Chat Demo/TUIKit project does not include the cutouts of large emoji elements. Please replace them
|
||||
* with your own designed or copyrighted emoji packs before the official launch for commercial use. The default small yellow face emoji pack is copyrighted by
|
||||
* Tencent Cloud and can be authorized for a fee. If you wish to obtain authorization, please submit a ticket to contact us.
|
||||
*
|
||||
* submit a ticket url:https://console.cloud.tencent.com/workorder/category?level1_id=29&level2_id=40&source=14&data_title=%E5%8D%B3%E6%97%B6%E9%80%9A%E4%BF%A1%20IM&step=1 (China mainland)
|
||||
* submit a ticket url:https://console.tencentcloud.com/workorder/category?level1_id=29&level2_id=40&source=14&data_title=Chat&step=1 (Other regions)
|
||||
*/
|
||||
@property(nonatomic, strong) NSArray<TUIFaceGroup *> *faceGroups;
|
||||
|
||||
/**
|
||||
*
|
||||
* The list of emoticons displayed after long-pressing the message on the chat interface
|
||||
*/
|
||||
@property(nonatomic, strong) NSArray<TUIFaceGroup *> *chatPopDetailGroups;
|
||||
|
||||
|
||||
@property(nonatomic, assign) BOOL enableMessageBubble;
|
||||
|
||||
+ (BOOL)isClassicEntrance;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
80
TUIKit/TIMCommon/CommonModel/TIMConfig.m
Normal file
80
TUIKit/TIMCommon/CommonModel/TIMConfig.m
Normal file
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// TIMConfig.m
|
||||
// Pods
|
||||
//
|
||||
// Created by cologne on 2023/3/14.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TIMConfig.h"
|
||||
#import "TIMCommonMediator.h"
|
||||
#import "TUIEmojiMeditorProtocol.h"
|
||||
#define kTUIKitFirstInitAppStyleID @"Classic"; // Classic / Minimalist
|
||||
|
||||
typedef NS_OPTIONS(NSInteger, emojiFaceType) {
|
||||
emojiFaceTypeKeyBoard = 1 << 0,
|
||||
emojiFaceTypePopDetail = 1 << 1,
|
||||
};
|
||||
|
||||
@interface TIMConfig ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation TIMConfig
|
||||
|
||||
+ (void)load {
|
||||
TUIRegisterThemeResourcePath(TIMCommonThemePath, TUIThemeModuleTIMCommon);
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.enableMessageBubble = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (id)defaultConfig {
|
||||
static dispatch_once_t onceToken;
|
||||
static TIMConfig *config;
|
||||
dispatch_once(&onceToken, ^{
|
||||
config = [[TIMConfig alloc] init];
|
||||
});
|
||||
return config;
|
||||
}
|
||||
|
||||
- (NSArray<TUIFaceGroup *> *)faceGroups {
|
||||
id<TUIEmojiMeditorProtocol> service = [[TIMCommonMediator share] getObject:@protocol(TUIEmojiMeditorProtocol)];
|
||||
return [service getFaceGroup];
|
||||
}
|
||||
|
||||
- (NSArray<TUIFaceGroup *> *)chatPopDetailGroups {
|
||||
id<TUIEmojiMeditorProtocol> service = [[TIMCommonMediator share] getObject:@protocol(TUIEmojiMeditorProtocol)];
|
||||
return [service getChatPopDetailGroups];
|
||||
}
|
||||
|
||||
+ (NSString *)getCurrentStyleSelectID {
|
||||
NSString *styleID = [[NSUserDefaults standardUserDefaults] objectForKey:@"StyleSelectkey"];
|
||||
if (IS_NOT_EMPTY_NSSTRING(styleID)) {
|
||||
return styleID;
|
||||
} else {
|
||||
// First Init
|
||||
NSString *initStyleID = kTUIKitFirstInitAppStyleID;
|
||||
[[NSUserDefaults standardUserDefaults] setValue:initStyleID forKey:@"StyleSelectkey"];
|
||||
[NSUserDefaults.standardUserDefaults synchronize];
|
||||
return initStyleID;
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)isClassicEntrance {
|
||||
NSString *styleID = [self.class getCurrentStyleSelectID];
|
||||
if ([styleID isKindOfClass:NSString.class]) {
|
||||
if (styleID.length > 0) {
|
||||
if ([styleID isEqualToString:@"Classic"]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
@end
|
||||
26
TUIKit/TIMCommon/CommonModel/TIMDefine.h
Normal file
26
TUIKit/TIMCommon/CommonModel/TIMDefine.h
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// TIMDefine.h
|
||||
// Pods
|
||||
//
|
||||
// Created by cologne on 2023/3/14.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef TIMDefine_h
|
||||
#define TIMDefine_h
|
||||
|
||||
#import <ReactiveObjC/ReactiveObjC.h>
|
||||
#import <TUICore/TUIDefine.h>
|
||||
#import <Masonry/Masonry.h>
|
||||
#import "TIMConfig.h"
|
||||
#import "TIMCommonModel.h"
|
||||
#import "TIMRTLUtil.h"
|
||||
|
||||
#define kEnableAllRotationOrientationNotification @"kEnableAllRotationOrientationNotification"
|
||||
#define kDisableAllRotationOrientationNotification @"kDisableAllRotationOrientationNotification"
|
||||
#define TUIMessageMediaViewDeviceOrientationChangeNotification @"TUIMessageMediaViewDeviceOrientationChangeNotification"
|
||||
|
||||
//Provide customers with the ability to modify the default emoji expression size in various input behaviors
|
||||
#define kTIMDefaultEmojiSize CGSizeMake(23, 23)
|
||||
|
||||
#endif /* TIMDefine_h */
|
||||
20
TUIKit/TIMCommon/CommonModel/TIMGroupInfo+TUIDataProvider.h
Normal file
20
TUIKit/TIMCommon/CommonModel/TIMGroupInfo+TUIDataProvider.h
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
#import <Foundation/Foundation.h>
|
||||
@import ImSDK_Plus;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface V2TIMGroupInfo (TUIDataProvider)
|
||||
|
||||
- (BOOL)isMeOwner;
|
||||
- (BOOL)isPrivate;
|
||||
- (BOOL)canInviteMember;
|
||||
- (BOOL)canRemoveMember;
|
||||
- (BOOL)canDismissGroup;
|
||||
- (BOOL)canSupportSetAdmain;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
46
TUIKit/TIMCommon/CommonModel/TIMGroupInfo+TUIDataProvider.m
Normal file
46
TUIKit/TIMCommon/CommonModel/TIMGroupInfo+TUIDataProvider.m
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
|
||||
#import <TUICore/TUIGlobalization.h>
|
||||
#import "TIMGroupInfo+TUIDataProvider.h"
|
||||
|
||||
@implementation V2TIMGroupInfo (TUIDataProvider)
|
||||
|
||||
- (BOOL)isMeOwner {
|
||||
return [self.owner isEqualToString:[[V2TIMManager sharedInstance] getLoginUser]] || (self.role == V2TIM_GROUP_MEMBER_ROLE_ADMIN);
|
||||
}
|
||||
|
||||
- (BOOL)isPrivate {
|
||||
return [self.groupType isEqualToString:@"Work"];
|
||||
}
|
||||
|
||||
- (BOOL)canInviteMember {
|
||||
return self.groupApproveOpt != V2TIM_GROUP_ADD_FORBID;
|
||||
}
|
||||
|
||||
- (BOOL)canRemoveMember {
|
||||
return [self isMeOwner] && (self.memberCount > 1);
|
||||
}
|
||||
|
||||
- (BOOL)canDismissGroup {
|
||||
if ([self isPrivate]) {
|
||||
return NO;
|
||||
} else {
|
||||
if ([self.owner isEqualToString:[[V2TIMManager sharedInstance] getLoginUser]] || (self.role == V2TIM_GROUP_MEMBER_ROLE_SUPER)) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)canSupportSetAdmain {
|
||||
BOOL isMeSuper = [self.owner isEqualToString:[[V2TIMManager sharedInstance] getLoginUser]] || (self.role == V2TIM_GROUP_MEMBER_ROLE_SUPER);
|
||||
|
||||
BOOL isCurrentGroupTypeSupportSetAdmain = ([self.groupType isEqualToString:@"Public"] || [self.groupType isEqualToString:@"Meeting"] ||
|
||||
[self.groupType isEqualToString:@"Community"] || [self.groupType isEqualToString:@"Private"]);
|
||||
|
||||
return isMeSuper && isCurrentGroupTypeSupportSetAdmain && (self.memberCount > 1);
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TIMInputViewMoreActionProtocol.h
|
||||
// TIMCommon
|
||||
//
|
||||
// Created by wyl on 2023/5/5.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol TIMInputViewMoreActionProtocol <NSObject>
|
||||
|
||||
- (void)sendMessage:(V2TIMMessage *)message;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
33
TUIKit/TIMCommon/CommonModel/TIMPopActionProtocol.h
Normal file
33
TUIKit/TIMCommon/CommonModel/TIMPopActionProtocol.h
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// TIMPopActionProtocol.h
|
||||
// TIMCommon
|
||||
//
|
||||
// Created by wyl on 2023/4/3.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol TIMPopActionProtocol <NSObject>
|
||||
|
||||
- (void)onDelete:(id)sender;
|
||||
|
||||
- (void)onCopyMsg:(id)sender;
|
||||
|
||||
- (void)onRevoke:(id)sender;
|
||||
|
||||
- (void)onReSend:(id)sender;
|
||||
|
||||
- (void)onMulitSelect:(id)sender;
|
||||
|
||||
- (void)onForward:(id)sender;
|
||||
|
||||
- (void)onReply:(id)sender;
|
||||
|
||||
- (void)onReference:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
51
TUIKit/TIMCommon/CommonModel/TIMRTLUtil.h
Normal file
51
TUIKit/TIMCommon/CommonModel/TIMRTLUtil.h
Normal file
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// TIMRTLUtil.h
|
||||
// TIMCommon
|
||||
//
|
||||
// Created by cologne on 2023/7/21.
|
||||
// Copyright © 2023 Tencent. All rights reserved
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@interface TIMRTLUtil : NSObject
|
||||
|
||||
@end
|
||||
|
||||
@interface UIView (TUIRTL)
|
||||
- (void)resetFrameToFitRTL;
|
||||
|
||||
@end
|
||||
|
||||
@interface UIImage (TUIRTL)
|
||||
|
||||
- (UIImage *_Nonnull)checkOverturn;
|
||||
- (UIImage *)rtl_imageFlippedForRightToLeftLayoutDirection;
|
||||
@end
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TUITextRTLAlignment) {
|
||||
TUITextRTLAlignmentUndefine,
|
||||
TUITextRTLAlignmentLeading,
|
||||
TUITextRTLAlignmentTrailing,
|
||||
TUITextRTLAlignmentCenter,
|
||||
};
|
||||
@interface UILabel (TUIRTL)
|
||||
@property (nonatomic, assign) TUITextRTLAlignment rtlAlignment;
|
||||
@end
|
||||
|
||||
@interface NSMutableAttributedString (TUIRTL)
|
||||
@property (nonatomic, assign) TUITextRTLAlignment rtlAlignment;
|
||||
@end
|
||||
|
||||
BOOL isRTLString(NSString *string);
|
||||
NSString * rtlString(NSString *string);
|
||||
NSAttributedString *rtlAttributeString(NSAttributedString *attributeString ,NSTextAlignment textAlignment );
|
||||
UIEdgeInsets rtlEdgeInsetsWithInsets(UIEdgeInsets insets);
|
||||
|
||||
@interface TUICollectionRTLFitFlowLayout : UICollectionViewFlowLayout
|
||||
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
291
TUIKit/TIMCommon/CommonModel/TIMRTLUtil.m
Normal file
291
TUIKit/TIMCommon/CommonModel/TIMRTLUtil.m
Normal file
@@ -0,0 +1,291 @@
|
||||
//
|
||||
// TIMRTLUtil.m
|
||||
// TIMCommon
|
||||
//
|
||||
// Created by cologne on 2023/7/21.
|
||||
// Copyright © 2023 Tencent. All rights reserved
|
||||
//
|
||||
|
||||
#import "TIMRTLUtil.h"
|
||||
#import <objc/runtime.h>
|
||||
#import <TUICore/TUIGlobalization.h>
|
||||
|
||||
@implementation TIMRTLUtil
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface UIView (TUIRTL)
|
||||
|
||||
@end
|
||||
@implementation UIView (TUIRTL)
|
||||
- (void)setRTLFrame:(CGRect)frame width:(CGFloat)width {
|
||||
if (isRTL()) {
|
||||
if (self.superview == nil) {
|
||||
NSAssert(0, @"must invoke after have superView");
|
||||
}
|
||||
CGFloat x = width - frame.origin.x - frame.size.width;
|
||||
frame.origin.x = x;
|
||||
}
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (void)setRTLFrame:(CGRect)frame {
|
||||
[self setRTLFrame:frame width:self.superview.frame.size.width];
|
||||
}
|
||||
|
||||
- (void)resetFrameToFitRTL {
|
||||
[self setRTLFrame:self.frame];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface UIImage (TUIRTL)
|
||||
|
||||
@end
|
||||
@implementation UIImage (TUIRTL)
|
||||
- (UIImage *_Nonnull)checkOverturn{
|
||||
if (isRTL()) {
|
||||
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale);
|
||||
CGContextRef bitmap = UIGraphicsGetCurrentContext();
|
||||
CGContextTranslateCTM(bitmap, self.size.width / 2, self.size.height / 2);
|
||||
CGContextScaleCTM(bitmap, -1.0, -1.0);
|
||||
CGContextTranslateCTM(bitmap, -self.size.width / 2, -self.size.height / 2);
|
||||
CGContextDrawImage(bitmap, CGRectMake(0, 0, self.size.width, self.size.height), self.CGImage);
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
return image;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (UIImage *)_imageFlippedForRightToLeftLayoutDirection {
|
||||
if (isRTL()) {
|
||||
return [UIImage imageWithCGImage:self.CGImage
|
||||
scale:self.scale
|
||||
orientation:UIImageOrientationUpMirrored];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (UIImage *)rtl_imageFlippedForRightToLeftLayoutDirection {
|
||||
if (isRTL()) {
|
||||
if (@available(iOS 13.0, *)) {
|
||||
UITraitCollection *const scaleTraitCollection = [UITraitCollection currentTraitCollection];
|
||||
UITraitCollection *const darkUnscaledTraitCollection = [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
|
||||
UITraitCollection *const darkScaledTraitCollection =
|
||||
[UITraitCollection traitCollectionWithTraitsFromCollections:@[ scaleTraitCollection, darkUnscaledTraitCollection ]];
|
||||
|
||||
UIImage *lightImg = [[self.imageAsset imageWithTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]]
|
||||
_imageFlippedForRightToLeftLayoutDirection];
|
||||
|
||||
UIImage *darkImage = [[self.imageAsset imageWithTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]]
|
||||
_imageFlippedForRightToLeftLayoutDirection];
|
||||
|
||||
UIImage *image =
|
||||
[lightImg imageWithConfiguration:[self.configuration
|
||||
configurationWithTraitCollection:[UITraitCollection
|
||||
traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]]];
|
||||
[image.imageAsset registerImage:darkImage withTraitCollection:darkScaledTraitCollection];
|
||||
return image;
|
||||
} else {
|
||||
return [UIImage imageWithCGImage:self.CGImage scale:self.scale orientation:UIImageOrientationUpMirrored];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@interface UINavigationController (TUIRTL)
|
||||
@end
|
||||
@implementation UINavigationController (TUIRTL)
|
||||
+ (void)load {
|
||||
Method oldMethod = class_getInstanceMethod(self, @selector(initWithRootViewController:));
|
||||
Method newMethod = class_getInstanceMethod(self, @selector(rtl_initWithRootViewController:));
|
||||
method_exchangeImplementations(oldMethod, newMethod);
|
||||
}
|
||||
|
||||
- (instancetype)rtl_initWithRootViewController:(UIViewController *)rootViewController {
|
||||
if ([self rtl_initWithRootViewController:rootViewController]) {
|
||||
if (@available(iOS 9.0, *)) {
|
||||
if (isRTL()) {
|
||||
self.navigationBar.semanticContentAttribute = [UIView appearance].semanticContentAttribute;
|
||||
self.view.semanticContentAttribute = [UIView appearance].semanticContentAttribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
UIEdgeInsets rtlEdgeInsetsWithInsets(UIEdgeInsets insets) {
|
||||
if (insets.left != insets.right && isRTL()) {
|
||||
CGFloat temp = insets.left;
|
||||
insets.left = insets.right;
|
||||
insets.right = temp;
|
||||
}
|
||||
return insets;
|
||||
|
||||
}
|
||||
|
||||
@implementation UIButton (TUIRTL)
|
||||
|
||||
void swizzleInstanceMethod(Class cls, SEL originSelector, SEL swizzleSelector){
|
||||
if (!cls) {
|
||||
return;
|
||||
}
|
||||
/* if current class not exist selector, then get super*/
|
||||
Method originalMethod = class_getInstanceMethod(cls, originSelector);
|
||||
Method swizzledMethod = class_getInstanceMethod(cls, swizzleSelector);
|
||||
|
||||
/* add selector if not exist, implement append with method */
|
||||
if (class_addMethod(cls,
|
||||
originSelector,
|
||||
method_getImplementation(swizzledMethod),
|
||||
method_getTypeEncoding(swizzledMethod)) ) {
|
||||
/* replace class instance method, added if selector not exist */
|
||||
/* for class cluster , it always add new selector here */
|
||||
class_replaceMethod(cls,
|
||||
swizzleSelector,
|
||||
method_getImplementation(originalMethod),
|
||||
method_getTypeEncoding(originalMethod));
|
||||
|
||||
} else {
|
||||
/* swizzleMethod maybe belong to super */
|
||||
class_replaceMethod(cls,
|
||||
swizzleSelector,
|
||||
class_replaceMethod(cls,
|
||||
originSelector,
|
||||
method_getImplementation(swizzledMethod),
|
||||
method_getTypeEncoding(swizzledMethod)),
|
||||
method_getTypeEncoding(originalMethod));
|
||||
}
|
||||
}
|
||||
+ (void)load
|
||||
{
|
||||
swizzleInstanceMethod(self, @selector(setContentEdgeInsets:), @selector(rtl_setContentEdgeInsets:));
|
||||
swizzleInstanceMethod(self, @selector(setImageEdgeInsets:), @selector(rtl_setImageEdgeInsets:));
|
||||
swizzleInstanceMethod(self, @selector(setTitleEdgeInsets:), @selector(rtl_setTitleEdgeInsets:));
|
||||
}
|
||||
|
||||
- (void)rtl_setContentEdgeInsets:(UIEdgeInsets)contentEdgeInsets {
|
||||
[self rtl_setContentEdgeInsets:rtlEdgeInsetsWithInsets(contentEdgeInsets)];
|
||||
}
|
||||
|
||||
- (void)rtl_setImageEdgeInsets:(UIEdgeInsets)imageEdgeInsets {
|
||||
[self rtl_setImageEdgeInsets:rtlEdgeInsetsWithInsets(imageEdgeInsets)];
|
||||
}
|
||||
|
||||
- (void)rtl_setTitleEdgeInsets:(UIEdgeInsets)titleEdgeInsets {
|
||||
[self rtl_setTitleEdgeInsets:rtlEdgeInsetsWithInsets(titleEdgeInsets)];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation UILabel (TUIRTL)
|
||||
|
||||
- (void)setRtlAlignment:(TUITextRTLAlignment)rtlAlignment {
|
||||
objc_setAssociatedObject(self, @selector(rtlAlignment), @(rtlAlignment), OBJC_ASSOCIATION_ASSIGN);
|
||||
switch (rtlAlignment) {
|
||||
case TUITextRTLAlignmentLeading:
|
||||
self.textAlignment = (isRTL() ? NSTextAlignmentRight : NSTextAlignmentLeft);
|
||||
break;
|
||||
case TUITextRTLAlignmentTrailing:
|
||||
self.textAlignment = (isRTL() ? NSTextAlignmentLeft : NSTextAlignmentRight);
|
||||
break;
|
||||
case TUITextRTLAlignmentCenter:
|
||||
self.textAlignment = NSTextAlignmentCenter;
|
||||
case TUITextRTLAlignmentUndefine:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (TUITextRTLAlignment)rtlAlignment {
|
||||
NSNumber *identifier = objc_getAssociatedObject(self, @selector(rtlAlignment));
|
||||
if (identifier) {
|
||||
return identifier.integerValue;
|
||||
}
|
||||
return TUITextRTLAlignmentUndefine;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation NSMutableAttributedString (TUIRTL)
|
||||
- (void)setRtlAlignment:(TUITextRTLAlignment)rtlAlignment {
|
||||
switch (rtlAlignment) {
|
||||
case TUITextRTLAlignmentLeading:
|
||||
self.rtlAlignment = (isRTL() ? NSTextAlignmentRight : NSTextAlignmentLeft);
|
||||
break;
|
||||
case TUITextRTLAlignmentTrailing:
|
||||
self.rtlAlignment = (isRTL() ? NSTextAlignmentLeft : NSTextAlignmentRight);
|
||||
break;
|
||||
case TUITextRTLAlignmentCenter:
|
||||
self.rtlAlignment = NSTextAlignmentCenter;
|
||||
case TUITextRTLAlignmentUndefine:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
BOOL isRTLString(NSString *string) {
|
||||
if ([string hasPrefix:@"\u202B"] || [string hasPrefix:@"\u202A"]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString * rtlString(NSString *string) {
|
||||
if (string.length == 0 || isRTLString(string)) {
|
||||
return string;
|
||||
}
|
||||
if (isRTL()) {
|
||||
string = [@"\u202B" stringByAppendingString:string];
|
||||
} else {
|
||||
string = [@"\u202A" stringByAppendingString:string];
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
NSAttributedString *rtlAttributeString(NSAttributedString *attributeString ,NSTextAlignment textAlignment ){
|
||||
if (attributeString.length == 0) {
|
||||
return attributeString;
|
||||
}
|
||||
NSRange range;
|
||||
NSDictionary *originAttributes = [attributeString attributesAtIndex:0 effectiveRange:&range];
|
||||
NSParagraphStyle *style = [originAttributes objectForKey:NSParagraphStyleAttributeName];
|
||||
|
||||
if (style && isRTLString(attributeString.string)) {
|
||||
return attributeString;
|
||||
}
|
||||
|
||||
NSMutableDictionary *attributes = originAttributes ? [originAttributes mutableCopy] : [NSMutableDictionary new];
|
||||
if (!style) {
|
||||
NSMutableParagraphStyle *mutableParagraphStyle = [[NSMutableParagraphStyle alloc] init];
|
||||
UILabel *test = [UILabel new];
|
||||
test.textAlignment = textAlignment;
|
||||
mutableParagraphStyle.alignment = test.textAlignment;
|
||||
style = mutableParagraphStyle;
|
||||
[attributes setValue:mutableParagraphStyle forKey:NSParagraphStyleAttributeName];
|
||||
}
|
||||
NSString *string = rtlString(attributeString.string);
|
||||
return [[NSAttributedString alloc] initWithString:string attributes:attributes];
|
||||
}
|
||||
|
||||
@implementation TUICollectionRTLFitFlowLayout
|
||||
- (UIUserInterfaceLayoutDirection)effectiveUserInterfaceLayoutDirection {
|
||||
if (isRTL()) {
|
||||
return UIUserInterfaceLayoutDirectionRightToLeft;
|
||||
}
|
||||
return UIUserInterfaceLayoutDirectionLeftToRight;
|
||||
}
|
||||
|
||||
- (BOOL)flipsHorizontallyInOppositeLayoutDirection{
|
||||
|
||||
return isRTL()? YES:NO;
|
||||
}
|
||||
@end
|
||||
661
TUIKit/TIMCommon/CommonModel/TUIAttributedLabel.h
Normal file
661
TUIKit/TIMCommon/CommonModel/TUIAttributedLabel.h
Normal file
@@ -0,0 +1,661 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
// TUIAttributedLabel.h
|
||||
|
||||
#import <CoreText/CoreText.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
//! Project version number for TUIAttributedLabel.
|
||||
FOUNDATION_EXPORT double TUIAttributedLabelVersionNumber;
|
||||
|
||||
//! Project version string for TUIAttributedLabel.
|
||||
FOUNDATION_EXPORT const unsigned char TUIAttributedLabelVersionString[];
|
||||
|
||||
@class TUIAttributedLabelLink;
|
||||
|
||||
/**
|
||||
Vertical alignment for text in a label whose bounds are larger than its text bounds
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, TUIAttributedLabelVerticalAlignment) {
|
||||
TUIAttributedLabelVerticalAlignmentCenter = 0,
|
||||
TUIAttributedLabelVerticalAlignmentTop = 1,
|
||||
TUIAttributedLabelVerticalAlignmentBottom = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
Determines whether the text to which this attribute applies has a strikeout drawn through itself.
|
||||
*/
|
||||
extern NSString *const kTUIStrikeOutAttributeName;
|
||||
|
||||
/**
|
||||
The background fill color. Value must be a `CGColorRef`. Default value is `nil` (no fill).
|
||||
*/
|
||||
extern NSString *const kTUIBackgroundFillColorAttributeName;
|
||||
|
||||
/**
|
||||
The padding for the background fill. Value must be a `UIEdgeInsets`. Default value is `UIEdgeInsetsZero` (no padding).
|
||||
*/
|
||||
extern NSString *const kTUIBackgroundFillPaddingAttributeName;
|
||||
|
||||
/**
|
||||
The background stroke color. Value must be a `CGColorRef`. Default value is `nil` (no stroke).
|
||||
*/
|
||||
extern NSString *const kTUIBackgroundStrokeColorAttributeName;
|
||||
|
||||
/**
|
||||
The background stroke line width. Value must be an `NSNumber`. Default value is `1.0f`.
|
||||
*/
|
||||
extern NSString *const kTUIBackgroundLineWidthAttributeName;
|
||||
|
||||
/**
|
||||
The background corner radius. Value must be an `NSNumber`. Default value is `5.0f`.
|
||||
*/
|
||||
extern NSString *const kTUIBackgroundCornerRadiusAttributeName;
|
||||
|
||||
@protocol TUIAttributedLabelDelegate;
|
||||
|
||||
// Override UILabel @property to accept both NSString and NSAttributedString
|
||||
@protocol TUIAttributedLabel <NSObject>
|
||||
@property(nonatomic, copy) IBInspectable id text;
|
||||
@end
|
||||
|
||||
IB_DESIGNABLE
|
||||
|
||||
/**
|
||||
`TUIAttributedLabel` is a drop-in replacement for `UILabel` that supports `NSAttributedString`, as well as automatically-detected and manually-added links to
|
||||
URLs, addresses, phone numbers, and dates.
|
||||
|
||||
## Differences Between `TUIAttributedLabel` and `UILabel`
|
||||
|
||||
For the most part, `TUIAttributedLabel` behaves just like `UILabel`. The following are notable exceptions, in which `TUIAttributedLabel` may act differently:
|
||||
|
||||
- `text` - This property now takes an `id` type argument, which can either be a kind of `NSString` or `NSAttributedString` (mutable or immutable in both cases)
|
||||
- `attributedText` - Do not set this property directly. Instead, pass an `NSAttributedString` to `text`.
|
||||
- `lineBreakMode` - This property displays only the first line when the value is `UILineBreakModeHeadTruncation`, `UILineBreakModeTailTruncation`, or
|
||||
`UILineBreakModeMiddleTruncation`
|
||||
- `adjustsFontsizeToFitWidth` - Supported in iOS 5 and greater, this property is effective for any value of `numberOfLines` greater than zero. In iOS 4,
|
||||
setting `numberOfLines` to a value greater than 1 with `adjustsFontSizeToFitWidth` set to `YES` may cause `sizeToFit` to execute indefinitely.
|
||||
- `baselineAdjustment` - This property has no affect.
|
||||
- `textAlignment` - This property does not support justified alignment.
|
||||
- `NSTextAttachment` - This string attribute is not supported.
|
||||
|
||||
Any properties affecting text or paragraph styling, such as `firstLineIndent` will only apply when text is set with an `NSString`. If the text is set with an
|
||||
`NSAttributedString`, these properties will not apply.
|
||||
|
||||
### NSCoding
|
||||
|
||||
`TUIAttributedLabel`, like `UILabel`, conforms to `NSCoding`. However, if the build target is set to less than iOS 6.0, `linkAttributes` and
|
||||
`activeLinkAttributes` will not be encoded or decoded. This is due to an runtime exception thrown when attempting to copy non-object CoreText values in
|
||||
dictionaries.
|
||||
|
||||
@warning Any properties changed on the label after setting the text will not be reflected until a subsequent call to `setText:` or
|
||||
`setText:afterInheritingLabelAttributesAndConfiguringWithBlock:`. This is to say, order of operations matters in this case. For example, if the label text
|
||||
color is originally black when the text is set, changing the text color to red will have no effect on the display of the label until the text is set once
|
||||
again.
|
||||
|
||||
@bug Setting `attributedText` directly is not recommended, as it may cause a crash when attempting to access any links previously set. Instead, call
|
||||
`setText:`, passing an `NSAttributedString`.
|
||||
*/
|
||||
@interface TUIAttributedLabel : UILabel <TUIAttributedLabel, UIGestureRecognizerDelegate>
|
||||
|
||||
/**
|
||||
* The designated initializers are @c initWithFrame: and @c initWithCoder:.
|
||||
* init will not properly initialize many required properties and other configuration.
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
///-----------------------------
|
||||
/// @name Accessing the Delegate
|
||||
///-----------------------------
|
||||
|
||||
/**
|
||||
The receiver's delegate.
|
||||
|
||||
@discussion A `TUIAttributedLabel` delegate responds to messages sent by tapping on links in the label. You can use the delegate to respond to links
|
||||
referencing a URL, address, phone number, date, or date with a specified time zone and duration.
|
||||
*/
|
||||
@property(nonatomic, unsafe_unretained) IBOutlet id<TUIAttributedLabelDelegate> delegate;
|
||||
|
||||
///--------------------------------------------
|
||||
/// @name Detecting, Accessing, & Styling Links
|
||||
///--------------------------------------------
|
||||
|
||||
/**
|
||||
A bitmask of `NSTextCheckingType` which are used to automatically detect links in the label text.
|
||||
|
||||
@warning You must specify `enabledTextCheckingTypes` before setting the `text`, with either `setText:` or
|
||||
`setText:afterInheritingLabelAttributesAndConfiguringWithBlock:`.
|
||||
*/
|
||||
@property(nonatomic, assign) NSTextCheckingTypes enabledTextCheckingTypes;
|
||||
|
||||
/**
|
||||
An array of `NSTextCheckingResult` objects for links detected or manually added to the label text.
|
||||
*/
|
||||
@property(readonly, nonatomic, strong) NSArray *links;
|
||||
|
||||
/**
|
||||
A dictionary containing the default `NSAttributedString` attributes to be applied to links detected or manually added to the label text. The default link style
|
||||
is blue and underlined.
|
||||
|
||||
@warning You must specify `linkAttributes` before setting autodecting or manually-adding links for these attributes to be applied.
|
||||
*/
|
||||
@property(nonatomic, strong) NSDictionary *linkAttributes;
|
||||
|
||||
/**
|
||||
A dictionary containing the default `NSAttributedString` attributes to be applied to links when they are in the active state. If `nil` or an empty
|
||||
`NSDictionary`, active links will not be styled. The default active link style is red and underlined.
|
||||
*/
|
||||
@property(nonatomic, strong) NSDictionary *activeLinkAttributes;
|
||||
|
||||
/**
|
||||
A dictionary containing the default `NSAttributedString` attributes to be applied to links when they are in the inactive state, which is triggered by a change
|
||||
in `tintColor` in iOS 7 and later. If `nil` or an empty `NSDictionary`, inactive links will not be styled. The default inactive link style is gray and
|
||||
unadorned.
|
||||
*/
|
||||
@property(nonatomic, strong) NSDictionary *inactiveLinkAttributes;
|
||||
|
||||
/**
|
||||
The edge inset for the background of a link. The default value is `{0, -1, 0, -1}`.
|
||||
*/
|
||||
@property(nonatomic, assign) UIEdgeInsets linkBackgroundEdgeInset;
|
||||
|
||||
/**
|
||||
Indicates if links will be detected within an extended area around the touch
|
||||
to emulate the link detection behaviour of WKWebView.
|
||||
Default value is NO. Enabling this may adversely impact performance.
|
||||
*/
|
||||
@property(nonatomic, assign) BOOL extendsLinkTouchArea;
|
||||
|
||||
///---------------------------------------
|
||||
/// @name Acccessing Text Style Attributes
|
||||
///---------------------------------------
|
||||
|
||||
/**
|
||||
The shadow blur radius for the label. A value of 0 indicates no blur, while larger values produce correspondingly larger blurring. This value must not be
|
||||
negative. The default value is 0.
|
||||
*/
|
||||
@property(nonatomic, assign) IBInspectable CGFloat shadowRadius;
|
||||
|
||||
/**
|
||||
The shadow blur radius for the label when the label's `highlighted` property is `YES`. A value of 0 indicates no blur, while larger values produce
|
||||
correspondingly larger blurring. This value must not be negative. The default value is 0.
|
||||
*/
|
||||
@property(nonatomic, assign) IBInspectable CGFloat highlightedShadowRadius;
|
||||
/**
|
||||
The shadow offset for the label when the label's `highlighted` property is `YES`. A size of {0, 0} indicates no offset, with positive values extending down and
|
||||
to the right. The default size is {0, 0}.
|
||||
*/
|
||||
@property(nonatomic, assign) IBInspectable CGSize highlightedShadowOffset;
|
||||
/**
|
||||
The shadow color for the label when the label's `highlighted` property is `YES`. The default value is `nil` (no shadow color).
|
||||
*/
|
||||
@property(nonatomic, strong) IBInspectable UIColor *highlightedShadowColor;
|
||||
|
||||
/**
|
||||
The amount to kern the next character. Default is standard kerning. If this attribute is set to 0.0, no kerning is done at all.
|
||||
*/
|
||||
@property(nonatomic, assign) IBInspectable CGFloat kern;
|
||||
|
||||
///--------------------------------------------
|
||||
/// @name Acccessing Paragraph Style Attributes
|
||||
///--------------------------------------------
|
||||
|
||||
/**
|
||||
The distance, in points, from the leading margin of a frame to the beginning of the
|
||||
paragraph's first line. This value is always nonnegative, and is 0.0 by default.
|
||||
This applies to the full text, rather than any specific paragraph metrics.
|
||||
*/
|
||||
@property(nonatomic, assign) IBInspectable CGFloat firstLineIndent;
|
||||
|
||||
/**
|
||||
The space in points added between lines within the paragraph. This value is always nonnegative and is 0.0 by default.
|
||||
*/
|
||||
@property(nonatomic, assign) IBInspectable CGFloat lineSpacing;
|
||||
|
||||
/**
|
||||
The minimum line height within the paragraph. If the value is 0.0, the minimum line height is set to the line height of the `font`. 0.0 by default.
|
||||
*/
|
||||
@property(nonatomic, assign) IBInspectable CGFloat minimumLineHeight;
|
||||
|
||||
/**
|
||||
The maximum line height within the paragraph. If the value is 0.0, the maximum line height is set to the line height of the `font`. 0.0 by default.
|
||||
*/
|
||||
@property(nonatomic, assign) IBInspectable CGFloat maximumLineHeight;
|
||||
|
||||
/**
|
||||
The line height multiple. This value is 1.0 by default.
|
||||
*/
|
||||
@property(nonatomic, assign) IBInspectable CGFloat lineHeightMultiple;
|
||||
|
||||
/**
|
||||
The distance, in points, from the margin to the text container. This value is `UIEdgeInsetsZero` by default.
|
||||
sizeThatFits: will have its returned size increased by these margins.
|
||||
drawTextInRect: will inset all drawn text by these margins.
|
||||
*/
|
||||
@property(nonatomic, assign) IBInspectable UIEdgeInsets textInsets;
|
||||
|
||||
/**
|
||||
The vertical text alignment for the label, for when the frame size is greater than the text rect size. The vertical alignment is
|
||||
`TUIAttributedLabelVerticalAlignmentCenter` by default.
|
||||
*/
|
||||
@property(nonatomic, assign) TUIAttributedLabelVerticalAlignment verticalAlignment;
|
||||
|
||||
///--------------------------------------------
|
||||
/// @name Accessing Truncation Token Appearance
|
||||
///--------------------------------------------
|
||||
|
||||
/**
|
||||
The attributed string to apply to the truncation token at the end of a truncated line.
|
||||
*/
|
||||
@property(nonatomic, strong) IBInspectable NSAttributedString *attributedTruncationToken;
|
||||
|
||||
///--------------------------
|
||||
/// @name Long press gestures
|
||||
///--------------------------
|
||||
|
||||
/**
|
||||
* The long-press gesture recognizer used internally by the label.
|
||||
*/
|
||||
@property(nonatomic, strong, readonly) UILongPressGestureRecognizer *longPressGestureRecognizer;
|
||||
|
||||
///--------------------------------------------
|
||||
/// @name Calculating Size of Attributed String
|
||||
///--------------------------------------------
|
||||
|
||||
/**
|
||||
Calculate and return the size that best fits an attributed string, given the specified constraints on size and number of lines.
|
||||
|
||||
@param attributedString The attributed string.
|
||||
@param size The maximum dimensions used to calculate size.
|
||||
@param numberOfLines The maximum number of lines in the text to draw, if the constraining size cannot accomodate the full attributed string.
|
||||
|
||||
@return The size that fits the attributed string within the specified constraints.
|
||||
*/
|
||||
+ (CGSize)sizeThatFitsAttributedString:(NSAttributedString *)attributedString withConstraints:(CGSize)size limitedToNumberOfLines:(NSUInteger)numberOfLines;
|
||||
|
||||
///----------------------------------
|
||||
/// @name Setting the Text Attributes
|
||||
///----------------------------------
|
||||
|
||||
/**
|
||||
Sets the text displayed by the label.
|
||||
|
||||
@param text An `NSString` or `NSAttributedString` object to be displayed by the label. If the specified text is an `NSString`, the label will display the text
|
||||
like a `UILabel`, inheriting the text styles of the label. If the specified text is an `NSAttributedString`, the label text styles will be overridden by the
|
||||
styles specified in the attributed string.
|
||||
|
||||
@discussion This method overrides `UILabel -setText:` to accept both `NSString` and `NSAttributedString` objects. This string is `nil` by default.
|
||||
*/
|
||||
- (void)setText:(id)text;
|
||||
|
||||
/**
|
||||
Sets the text displayed by the label, after configuring an attributed string containing the text attributes inherited from the label in a block.
|
||||
|
||||
@param text An `NSString` or `NSAttributedString` object to be displayed by the label.
|
||||
@param block A block object that returns an `NSMutableAttributedString` object and takes a single argument, which is an `NSMutableAttributedString` object with
|
||||
the text from the first parameter, and the text attributes inherited from the label text styles. For example, if you specified the `font` of the label to be
|
||||
`[UIFont boldSystemFontOfSize:14]` and `textColor` to be `[UIColor redColor]`, the `NSAttributedString` argument of the block would be contain the
|
||||
`NSAttributedString` attribute equivalents of those properties. In this block, you can set further attributes on particular ranges.
|
||||
|
||||
@discussion This string is `nil` by default.
|
||||
*/
|
||||
- (void)setText:(id)text
|
||||
afterInheritingLabelAttributesAndConfiguringWithBlock:(NSMutableAttributedString * (^)(NSMutableAttributedString *mutableAttributedString))block;
|
||||
|
||||
///------------------------------------
|
||||
/// @name Accessing the Text Attributes
|
||||
///------------------------------------
|
||||
|
||||
/**
|
||||
A copy of the label's current attributedText. This returns `nil` if an attributed string has never been set on the label.
|
||||
|
||||
@warning Do not set this property directly. Instead, set @c text to an @c NSAttributedString.
|
||||
*/
|
||||
@property(readwrite, nonatomic, copy) NSAttributedString *attributedText;
|
||||
|
||||
///-------------------
|
||||
/// @name Adding Links
|
||||
///-------------------
|
||||
|
||||
/**
|
||||
Adds a link. You can customize an individual link's appearance and accessibility value by creating your own @c TUIAttributedLabelLink and passing it to this
|
||||
method. The other methods for adding links will use the label's default attributes.
|
||||
|
||||
@warning Modifying the link's attribute dictionaries must be done before calling this method.
|
||||
|
||||
@param link A @c TUIAttributedLabelLink object.
|
||||
*/
|
||||
- (void)addLink:(TUIAttributedLabelLink *)link;
|
||||
|
||||
/**
|
||||
Adds a link to an @c NSTextCheckingResult.
|
||||
|
||||
@param result An @c NSTextCheckingResult representing the link's location and type.
|
||||
|
||||
@return The newly added link object.
|
||||
*/
|
||||
- (TUIAttributedLabelLink *)addLinkWithTextCheckingResult:(NSTextCheckingResult *)result;
|
||||
|
||||
/**
|
||||
Adds a link to an @c NSTextCheckingResult.
|
||||
|
||||
@param result An @c NSTextCheckingResult representing the link's location and type.
|
||||
@param attributes The attributes to be added to the text in the range of the specified link. If set, the label's @c activeAttributes and @c inactiveAttributes
|
||||
will be applied to the link. If `nil`, no attributes are added to the link.
|
||||
|
||||
@return The newly added link object.
|
||||
*/
|
||||
- (TUIAttributedLabelLink *)addLinkWithTextCheckingResult:(NSTextCheckingResult *)result attributes:(NSDictionary *)attributes;
|
||||
|
||||
/**
|
||||
Adds a link to a URL for a specified range in the label text.
|
||||
|
||||
@param url The url to be linked to
|
||||
@param range The range in the label text of the link. The range must not exceed the bounds of the receiver.
|
||||
|
||||
@return The newly added link object.
|
||||
*/
|
||||
- (TUIAttributedLabelLink *)addLinkToURL:(NSURL *)url withRange:(NSRange)range;
|
||||
|
||||
/**
|
||||
Adds a link to an address for a specified range in the label text.
|
||||
|
||||
@param addressComponents A dictionary of address components for the address to be linked to
|
||||
@param range The range in the label text of the link. The range must not exceed the bounds of the receiver.
|
||||
|
||||
@discussion The address component dictionary keys are described in `NSTextCheckingResult`'s "Keys for Address Components."
|
||||
|
||||
@return The newly added link object.
|
||||
*/
|
||||
- (TUIAttributedLabelLink *)addLinkToAddress:(NSDictionary *)addressComponents withRange:(NSRange)range;
|
||||
|
||||
/**
|
||||
Adds a link to a phone number for a specified range in the label text.
|
||||
|
||||
@param phoneNumber The phone number to be linked to.
|
||||
@param range The range in the label text of the link. The range must not exceed the bounds of the receiver.
|
||||
|
||||
@return The newly added link object.
|
||||
*/
|
||||
- (TUIAttributedLabelLink *)addLinkToPhoneNumber:(NSString *)phoneNumber withRange:(NSRange)range;
|
||||
|
||||
/**
|
||||
Adds a link to a date for a specified range in the label text.
|
||||
|
||||
@param date The date to be linked to.
|
||||
@param range The range in the label text of the link. The range must not exceed the bounds of the receiver.
|
||||
|
||||
@return The newly added link object.
|
||||
*/
|
||||
- (TUIAttributedLabelLink *)addLinkToDate:(NSDate *)date withRange:(NSRange)range;
|
||||
|
||||
/**
|
||||
Adds a link to a date with a particular time zone and duration for a specified range in the label text.
|
||||
|
||||
@param date The date to be linked to.
|
||||
@param timeZone The time zone of the specified date.
|
||||
@param duration The duration, in seconds from the specified date.
|
||||
@param range The range in the label text of the link. The range must not exceed the bounds of the receiver.
|
||||
|
||||
@return The newly added link object.
|
||||
*/
|
||||
- (TUIAttributedLabelLink *)addLinkToDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone duration:(NSTimeInterval)duration withRange:(NSRange)range;
|
||||
|
||||
/**
|
||||
Adds a link to transit information for a specified range in the label text.
|
||||
|
||||
@param components A dictionary containing the transit components. The currently supported keys are `NSTextCheckingAirlineKey` and `NSTextCheckingFlightKey`.
|
||||
@param range The range in the label text of the link. The range must not exceed the bounds of the receiver.
|
||||
|
||||
@return The newly added link object.
|
||||
*/
|
||||
- (TUIAttributedLabelLink *)addLinkToTransitInformation:(NSDictionary *)components withRange:(NSRange)range;
|
||||
|
||||
/**
|
||||
Returns whether an @c NSTextCheckingResult is found at the give point.
|
||||
|
||||
@discussion This can be used together with @c UITapGestureRecognizer to tap interactions with overlapping views.
|
||||
|
||||
@param point The point inside the label.
|
||||
*/
|
||||
- (BOOL)containslinkAtPoint:(CGPoint)point;
|
||||
|
||||
/**
|
||||
Returns the @c TUIAttributedLabelLink at the give point if it exists.
|
||||
|
||||
@discussion This can be used together with @c UIViewControllerPreviewingDelegate to peek into links.
|
||||
|
||||
@param point The point inside the label.
|
||||
*/
|
||||
- (TUIAttributedLabelLink *)linkAtPoint:(CGPoint)point;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
The `TUIAttributedLabelDelegate` protocol defines the messages sent to an attributed label delegate when links are tapped. All of the methods of this protocol
|
||||
are optional.
|
||||
*/
|
||||
@protocol TUIAttributedLabelDelegate <NSObject>
|
||||
|
||||
///-----------------------------------
|
||||
/// @name Responding to Link Selection
|
||||
///-----------------------------------
|
||||
@optional
|
||||
|
||||
/**
|
||||
Tells the delegate that the user did select a link to a URL.
|
||||
|
||||
@param label The label whose link was selected.
|
||||
@param url The URL for the selected link.
|
||||
*/
|
||||
- (void)attributedLabel:(TUIAttributedLabel *)label didSelectLinkWithURL:(NSURL *)url;
|
||||
|
||||
/**
|
||||
Tells the delegate that the user did select a link to an address.
|
||||
|
||||
@param label The label whose link was selected.
|
||||
@param addressComponents The components of the address for the selected link.
|
||||
*/
|
||||
- (void)attributedLabel:(TUIAttributedLabel *)label didSelectLinkWithAddress:(NSDictionary *)addressComponents;
|
||||
|
||||
/**
|
||||
Tells the delegate that the user did select a link to a phone number.
|
||||
|
||||
@param label The label whose link was selected.
|
||||
@param phoneNumber The phone number for the selected link.
|
||||
*/
|
||||
- (void)attributedLabel:(TUIAttributedLabel *)label didSelectLinkWithPhoneNumber:(NSString *)phoneNumber;
|
||||
|
||||
/**
|
||||
Tells the delegate that the user did select a link to a date.
|
||||
|
||||
@param label The label whose link was selected.
|
||||
@param date The datefor the selected link.
|
||||
*/
|
||||
- (void)attributedLabel:(TUIAttributedLabel *)label didSelectLinkWithDate:(NSDate *)date;
|
||||
|
||||
/**
|
||||
Tells the delegate that the user did select a link to a date with a time zone and duration.
|
||||
|
||||
@param label The label whose link was selected.
|
||||
@param date The date for the selected link.
|
||||
@param timeZone The time zone of the date for the selected link.
|
||||
@param duration The duration, in seconds from the date for the selected link.
|
||||
*/
|
||||
- (void)attributedLabel:(TUIAttributedLabel *)label didSelectLinkWithDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone duration:(NSTimeInterval)duration;
|
||||
|
||||
/**
|
||||
Tells the delegate that the user did select a link to transit information
|
||||
|
||||
@param label The label whose link was selected.
|
||||
@param components A dictionary containing the transit components. The currently supported keys are `NSTextCheckingAirlineKey` and `NSTextCheckingFlightKey`.
|
||||
*/
|
||||
- (void)attributedLabel:(TUIAttributedLabel *)label didSelectLinkWithTransitInformation:(NSDictionary *)components;
|
||||
|
||||
/**
|
||||
Tells the delegate that the user did select a link to a text checking result.
|
||||
|
||||
@discussion This method is called if no other delegate method was called, which can occur by either now implementing the method in `TUIAttributedLabelDelegate`
|
||||
corresponding to a particular link, or the link was added by passing an instance of a custom `NSTextCheckingResult` subclass into
|
||||
`-addLinkWithTextCheckingResult:`.
|
||||
|
||||
@param label The label whose link was selected.
|
||||
@param result The custom text checking result.
|
||||
*/
|
||||
- (void)attributedLabel:(TUIAttributedLabel *)label didSelectLinkWithTextCheckingResult:(NSTextCheckingResult *)result;
|
||||
|
||||
///---------------------------------
|
||||
/// @name Responding to Long Presses
|
||||
///---------------------------------
|
||||
|
||||
/**
|
||||
* Long-press delegate methods include the CGPoint tapped within the label's coordinate space.
|
||||
* This may be useful on iPad to present a popover from a specific origin point.
|
||||
*/
|
||||
|
||||
/**
|
||||
Tells the delegate that the user long-pressed a link to a URL.
|
||||
|
||||
@param label The label whose link was long pressed.
|
||||
@param url The URL for the link.
|
||||
@param point the point pressed, in the label's coordinate space
|
||||
*/
|
||||
- (void)attributedLabel:(TUIAttributedLabel *)label didLongPressLinkWithURL:(NSURL *)url atPoint:(CGPoint)point;
|
||||
|
||||
/**
|
||||
Tells the delegate that the user long-pressed a link to an address.
|
||||
|
||||
@param label The label whose link was long pressed.
|
||||
@param addressComponents The components of the address for the link.
|
||||
@param point the point pressed, in the label's coordinate space
|
||||
*/
|
||||
- (void)attributedLabel:(TUIAttributedLabel *)label didLongPressLinkWithAddress:(NSDictionary *)addressComponents atPoint:(CGPoint)point;
|
||||
|
||||
/**
|
||||
Tells the delegate that the user long-pressed a link to a phone number.
|
||||
|
||||
@param label The label whose link was long pressed.
|
||||
@param phoneNumber The phone number for the link.
|
||||
@param point the point pressed, in the label's coordinate space
|
||||
*/
|
||||
- (void)attributedLabel:(TUIAttributedLabel *)label didLongPressLinkWithPhoneNumber:(NSString *)phoneNumber atPoint:(CGPoint)point;
|
||||
|
||||
/**
|
||||
Tells the delegate that the user long-pressed a link to a date.
|
||||
|
||||
@param label The label whose link was long pressed.
|
||||
@param date The date for the selected link.
|
||||
@param point the point pressed, in the label's coordinate space
|
||||
*/
|
||||
- (void)attributedLabel:(TUIAttributedLabel *)label didLongPressLinkWithDate:(NSDate *)date atPoint:(CGPoint)point;
|
||||
|
||||
/**
|
||||
Tells the delegate that the user long-pressed a link to a date with a time zone and duration.
|
||||
|
||||
@param label The label whose link was long pressed.
|
||||
@param date The date for the link.
|
||||
@param timeZone The time zone of the date for the link.
|
||||
@param duration The duration, in seconds from the date for the link.
|
||||
@param point the point pressed, in the label's coordinate space
|
||||
*/
|
||||
- (void)attributedLabel:(TUIAttributedLabel *)label
|
||||
didLongPressLinkWithDate:(NSDate *)date
|
||||
timeZone:(NSTimeZone *)timeZone
|
||||
duration:(NSTimeInterval)duration
|
||||
atPoint:(CGPoint)point;
|
||||
|
||||
/**
|
||||
Tells the delegate that the user long-pressed a link to transit information.
|
||||
|
||||
@param label The label whose link was long pressed.
|
||||
@param components A dictionary containing the transit components. The currently supported keys are `NSTextCheckingAirlineKey` and `NSTextCheckingFlightKey`.
|
||||
@param point the point pressed, in the label's coordinate space
|
||||
*/
|
||||
- (void)attributedLabel:(TUIAttributedLabel *)label didLongPressLinkWithTransitInformation:(NSDictionary *)components atPoint:(CGPoint)point;
|
||||
|
||||
/**
|
||||
Tells the delegate that the user long-pressed a link to a text checking result.
|
||||
|
||||
@discussion Similar to `-attributedLabel:didSelectLinkWithTextCheckingResult:`, this method is called if a link is long pressed and the delegate does not
|
||||
implement the method corresponding to this type of link.
|
||||
|
||||
@param label The label whose link was long pressed.
|
||||
@param result The custom text checking result.
|
||||
@param point the point pressed, in the label's coordinate space
|
||||
*/
|
||||
- (void)attributedLabel:(TUIAttributedLabel *)label didLongPressLinkWithTextCheckingResult:(NSTextCheckingResult *)result atPoint:(CGPoint)point;
|
||||
|
||||
@end
|
||||
|
||||
@interface TUIAttributedLabelLink : NSObject <NSCoding>
|
||||
|
||||
typedef void (^TUIAttributedLabelLinkBlock)(TUIAttributedLabel *, TUIAttributedLabelLink *);
|
||||
|
||||
/**
|
||||
An `NSTextCheckingResult` representing the link's location and type.
|
||||
*/
|
||||
@property(readonly, nonatomic, strong) NSTextCheckingResult *result;
|
||||
|
||||
/**
|
||||
A dictionary containing the @c NSAttributedString attributes to be applied to the link.
|
||||
*/
|
||||
@property(readonly, nonatomic, copy) NSDictionary *attributes;
|
||||
|
||||
/**
|
||||
A dictionary containing the @c NSAttributedString attributes to be applied to the link when it is in the active state.
|
||||
*/
|
||||
@property(readonly, nonatomic, copy) NSDictionary *activeAttributes;
|
||||
|
||||
/**
|
||||
A dictionary containing the @c NSAttributedString attributes to be applied to the link when it is in the inactive state, which is triggered by a change in
|
||||
`tintColor` in iOS 7 and later.
|
||||
*/
|
||||
@property(readonly, nonatomic, copy) NSDictionary *inactiveAttributes;
|
||||
|
||||
/**
|
||||
Additional information about a link for VoiceOver users. Has default values if the link's @c result is @c NSTextCheckingTypeLink, @c
|
||||
NSTextCheckingTypePhoneNumber, or @c NSTextCheckingTypeDate.
|
||||
*/
|
||||
@property(nonatomic, copy) NSString *accessibilityValue;
|
||||
|
||||
/**
|
||||
A block called when this link is tapped.
|
||||
If non-nil, tapping on this link will call this block instead of the
|
||||
@c TUIAttributedLabelDelegate tap methods, which will not be called for this link.
|
||||
*/
|
||||
@property(nonatomic, copy) TUIAttributedLabelLinkBlock linkTapBlock;
|
||||
|
||||
/**
|
||||
A block called when this link is long-pressed.
|
||||
If non-nil, long pressing on this link will call this block instead of the
|
||||
@c TUIAttributedLabelDelegate long press methods, which will not be called for this link.
|
||||
*/
|
||||
@property(nonatomic, copy) TUIAttributedLabelLinkBlock linkLongPressBlock;
|
||||
|
||||
/**
|
||||
Initializes a link using the attribute dictionaries specified.
|
||||
|
||||
@param attributes The @c attributes property for the link.
|
||||
@param activeAttributes The @c activeAttributes property for the link.
|
||||
@param inactiveAttributes The @c inactiveAttributes property for the link.
|
||||
@param result An @c NSTextCheckingResult representing the link's location and type.
|
||||
|
||||
@return The initialized link object.
|
||||
*/
|
||||
- (instancetype)initWithAttributes:(NSDictionary *)attributes
|
||||
activeAttributes:(NSDictionary *)activeAttributes
|
||||
inactiveAttributes:(NSDictionary *)inactiveAttributes
|
||||
textCheckingResult:(NSTextCheckingResult *)result;
|
||||
|
||||
/**
|
||||
Initializes a link using the attribute dictionaries set on a specified label.
|
||||
|
||||
@param label The attributed label from which to inherit attribute dictionaries.
|
||||
@param result An @c NSTextCheckingResult representing the link's location and type.
|
||||
|
||||
@return The initialized link object.
|
||||
*/
|
||||
- (instancetype)initWithAttributesFromLabel:(TUIAttributedLabel *)label textCheckingResult:(NSTextCheckingResult *)result;
|
||||
|
||||
@end
|
||||
1797
TUIKit/TIMCommon/CommonModel/TUIAttributedLabel.m
Normal file
1797
TUIKit/TIMCommon/CommonModel/TUIAttributedLabel.m
Normal file
File diff suppressed because it is too large
Load Diff
27
TUIKit/TIMCommon/CommonModel/TUIEmojiMeditorProtocol.h
Normal file
27
TUIKit/TIMCommon/CommonModel/TUIEmojiMeditorProtocol.h
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// TUIEmojiMeditorProtocol.h
|
||||
// TUIEmojiPlugin
|
||||
//
|
||||
// Created by wyl on 2023/11/14.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "TIMDefine.h"
|
||||
#import "TIMCommonModel.h"
|
||||
@class V2TIMMessage;
|
||||
@class TUIFaceGroup;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol TUIEmojiMeditorProtocol <NSObject>
|
||||
- (void)updateEmojiGroups;
|
||||
- (id)getFaceGroup;
|
||||
- (void)appendFaceGroup:(TUIFaceGroup *)faceGroup;
|
||||
- (id)getChatPopDetailGroups;
|
||||
- (id)getChatContextEmojiDetailGroups;
|
||||
- (id)getChatPopMenuRecentQueue;
|
||||
- (void)updateRecentMenuQueue:(NSString *)faceName;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
29
TUIKit/TIMCommon/CommonModel/TUIFitButton.h
Normal file
29
TUIKit/TIMCommon/CommonModel/TUIFitButton.h
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// TUIFitButton.h
|
||||
// TUICore
|
||||
//
|
||||
// Created by wyl on 2022/5/24.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIFitButton : UIButton
|
||||
@property(nonatomic, assign) CGRect titleRect;
|
||||
@property(nonatomic, assign) CGRect imageRect;
|
||||
|
||||
@property(nonatomic, assign) CGSize imageSize;
|
||||
@property(nonatomic, assign) CGSize titleSize;
|
||||
|
||||
@property(nonatomic, strong) UIImage* hoverImage;
|
||||
@property(nonatomic, strong) UIImage* normalImage;
|
||||
|
||||
@end
|
||||
|
||||
@interface TUIBlockButton : TUIFitButton
|
||||
@property(nonatomic, copy) void (^clickCallBack)(id button);
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
66
TUIKit/TIMCommon/CommonModel/TUIFitButton.m
Normal file
66
TUIKit/TIMCommon/CommonModel/TUIFitButton.m
Normal file
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// TUIFitButton.m
|
||||
// TUICore
|
||||
//
|
||||
// Created by wyl on 2022/5/24.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIFitButton.h"
|
||||
|
||||
@implementation TUIFitButton
|
||||
|
||||
- (CGRect)titleRectForContentRect:(CGRect)contentRect {
|
||||
if (!CGRectEqualToRect(self.titleRect, CGRectZero)) {
|
||||
return self.titleRect;
|
||||
} else if (!CGSizeEqualToSize(self.titleSize, CGSizeZero)) {
|
||||
CGRect oldrect = [super titleRectForContentRect:contentRect];
|
||||
CGRect newrect = CGRectZero;
|
||||
newrect.origin.x = oldrect.origin.x + (oldrect.size.width - self.titleSize.width) / 2;
|
||||
newrect.origin.y = oldrect.origin.y + (oldrect.size.height - self.titleSize.height) / 2;
|
||||
newrect.size.width = self.titleSize.width;
|
||||
newrect.size.height = self.titleSize.height;
|
||||
|
||||
return newrect;
|
||||
}
|
||||
return [super titleRectForContentRect:contentRect];
|
||||
}
|
||||
- (CGRect)imageRectForContentRect:(CGRect)contentRect {
|
||||
if (!CGRectEqualToRect(self.imageRect, CGRectZero)) {
|
||||
return self.imageRect;
|
||||
} else if (!CGSizeEqualToSize(self.imageSize, CGSizeZero)) {
|
||||
CGRect oldrect = [super imageRectForContentRect:contentRect];
|
||||
CGRect newrect = CGRectZero;
|
||||
newrect.origin.x = oldrect.origin.x + (oldrect.size.width - self.imageSize.width) / 2;
|
||||
newrect.origin.y = oldrect.origin.y + (oldrect.size.height - self.imageSize.height) / 2;
|
||||
newrect.size.width = self.imageSize.width;
|
||||
newrect.size.height = self.imageSize.height;
|
||||
|
||||
return newrect;
|
||||
}
|
||||
return [super imageRectForContentRect:contentRect];
|
||||
}
|
||||
|
||||
- (void)setNormalImage:(UIImage *)normalImage {
|
||||
_normalImage = normalImage;
|
||||
[self setImage:normalImage forState:UIControlStateNormal];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TUIBlockButton
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self addTarget:self action:@selector(buttonTap:) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)buttonTap:(id)button {
|
||||
if (self.clickCallBack) {
|
||||
self.clickCallBack(self);
|
||||
}
|
||||
}
|
||||
- (void)dealloc {
|
||||
}
|
||||
@end
|
||||
54
TUIKit/TIMCommon/CommonModel/TUIFloatViewController.h
Normal file
54
TUIKit/TIMCommon/CommonModel/TUIFloatViewController.h
Normal file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// TUIFloatViewController.h
|
||||
// TIMCommon
|
||||
//
|
||||
// Created by wyl on 2023/1/16.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol TUIFloatSubViewControllerProtocol <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
@property(nonatomic, copy) void (^floatDataSourceChanged)(NSArray *arr);
|
||||
|
||||
- (void)floatControllerLeftButtonClick;
|
||||
|
||||
- (void)floatControllerRightButtonClick;
|
||||
|
||||
@end
|
||||
|
||||
@interface TUIFloatTitleView : UIView
|
||||
|
||||
@property(nonatomic, strong) UIButton *leftButton;
|
||||
@property(nonatomic, strong) UIButton *rightButton;
|
||||
@property(nonatomic, copy) void (^leftButtonClickCallback)(void);
|
||||
@property(nonatomic, copy) void (^rightButtonClickCallback)(void);
|
||||
@property(nonatomic, strong) UILabel *titleLabel;
|
||||
@property(nonatomic, strong) UILabel *subTitleLabel;
|
||||
|
||||
- (void)setTitleText:(NSString *)mainText subTitleText:(NSString *)secondText leftBtnText:(NSString *)leftBtnText rightBtnText:(NSString *)rightBtnText;
|
||||
@end
|
||||
|
||||
@interface TUIFloatViewController : UIViewController
|
||||
@property(nonatomic, strong) TUIFloatTitleView *topGestureView;
|
||||
@property(nonatomic, strong) UIImageView *topImgView;
|
||||
@property(nonatomic, strong) UIView *containerView;
|
||||
@property(nonatomic, strong) UIViewController<TUIFloatSubViewControllerProtocol> *childVC;
|
||||
|
||||
- (void)updateSubContainerView;
|
||||
|
||||
- (void)setnormalTop;
|
||||
|
||||
- (void)setNormalBottom;
|
||||
|
||||
- (void)appendChildViewController:(UIViewController<TUIFloatSubViewControllerProtocol> *)vc topMargin:(CGFloat)topMargin;
|
||||
|
||||
- (void)floatDismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
283
TUIKit/TIMCommon/CommonModel/TUIFloatViewController.m
Normal file
283
TUIKit/TIMCommon/CommonModel/TUIFloatViewController.m
Normal file
@@ -0,0 +1,283 @@
|
||||
//
|
||||
// TUIFloatTitleView.m
|
||||
// TUI
|
||||
//
|
||||
// Created by wyl on 2023/1/16.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIFloatViewController.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
|
||||
typedef enum : NSUInteger {
|
||||
FLEX_TOP,
|
||||
FLEX_Bottom,
|
||||
} FLEX_Location;
|
||||
|
||||
@interface TUIFloatTitleView ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIFloatTitleView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self setupView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupView {
|
||||
self.titleLabel = [[UILabel alloc] init];
|
||||
self.titleLabel.text = @"";
|
||||
self.titleLabel.font = [UIFont boldSystemFontOfSize:kScale390(20)];
|
||||
[self addSubview:self.titleLabel];
|
||||
|
||||
self.subTitleLabel = [[UILabel alloc] init];
|
||||
self.subTitleLabel.text = @"";
|
||||
self.subTitleLabel.font = [UIFont systemFontOfSize:kScale390(12)];
|
||||
self.subTitleLabel.tintColor = [UIColor grayColor];
|
||||
[self addSubview:self.subTitleLabel];
|
||||
|
||||
self.leftButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[self addSubview:self.leftButton];
|
||||
[self.leftButton setTitle:TIMCommonLocalizableString(TUIKitCreateCancel) forState:UIControlStateNormal];
|
||||
self.leftButton.titleLabel.font = [UIFont systemFontOfSize:kScale390(16)];
|
||||
[self.leftButton addTarget:self action:@selector(leftButtonClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.leftButton setTitleColor:[UIColor tui_colorWithHex:@"#0365F9"] forState:UIControlStateNormal];
|
||||
|
||||
self.rightButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[self addSubview:self.rightButton];
|
||||
[self.rightButton setTitle:TIMCommonLocalizableString(TUIKitCreateNext) forState:UIControlStateNormal];
|
||||
self.rightButton.titleLabel.font = [UIFont systemFontOfSize:kScale390(16)];
|
||||
[self.rightButton addTarget:self action:@selector(rightButtonClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.rightButton setTitleColor:[UIColor tui_colorWithHex:@"#0365F9"] forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
[self.titleLabel sizeToFit];
|
||||
[self.subTitleLabel sizeToFit];
|
||||
|
||||
if (self.subTitleLabel.isHidden || self.subTitleLabel.text.length == 0) {
|
||||
self.titleLabel.frame = CGRectMake((self.frame.size.width - self.titleLabel.frame.size.width) * 0.5, kScale390(23.5), self.titleLabel.frame.size.width,
|
||||
self.titleLabel.frame.size.height);
|
||||
} else {
|
||||
self.titleLabel.frame = CGRectMake((self.frame.size.width - self.titleLabel.frame.size.width) * 0.5, kScale390(17.5), self.titleLabel.frame.size.width,
|
||||
self.titleLabel.frame.size.height);
|
||||
|
||||
self.subTitleLabel.frame = CGRectMake((self.frame.size.width - self.subTitleLabel.frame.size.width) * 0.5,
|
||||
self.titleLabel.frame.origin.y + self.titleLabel.frame.size.height + kScale390(1),
|
||||
self.subTitleLabel.frame.size.width, self.subTitleLabel.frame.size.height);
|
||||
}
|
||||
|
||||
[self.leftButton sizeToFit];
|
||||
self.leftButton.frame = CGRectMake(kScale390(15), kScale390(23.5), self.leftButton.frame.size.width, self.leftButton.frame.size.height);
|
||||
|
||||
[self.rightButton sizeToFit];
|
||||
self.rightButton.frame = CGRectMake(self.frame.size.width - self.rightButton.frame.size.width - kScale390(14), kScale390(23.5),
|
||||
self.rightButton.frame.size.width, self.rightButton.frame.size.height);
|
||||
if (isRTL()){
|
||||
[self.leftButton resetFrameToFitRTL];
|
||||
[self.rightButton resetFrameToFitRTL];
|
||||
}
|
||||
}
|
||||
- (void)leftButtonClick {
|
||||
if (self.leftButtonClickCallback) {
|
||||
self.leftButtonClickCallback();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)rightButtonClick {
|
||||
if (self.rightButtonClickCallback) {
|
||||
self.rightButtonClickCallback();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setTitleText:(NSString *)mainText subTitleText:(NSString *)secondText leftBtnText:(NSString *)leftBtnText rightBtnText:(NSString *)rightBtnText {
|
||||
self.titleLabel.text = mainText;
|
||||
self.subTitleLabel.text = secondText;
|
||||
[self.leftButton setTitle:leftBtnText forState:UIControlStateNormal];
|
||||
[self.rightButton setTitle:rightBtnText forState:UIControlStateNormal];
|
||||
}
|
||||
@end
|
||||
|
||||
@interface TUIFloatViewController ()
|
||||
|
||||
@property(nonatomic, assign) CGFloat topMargin;
|
||||
|
||||
@property(nonatomic, assign) FLEX_Location currentLoaction;
|
||||
|
||||
@property(nonatomic, strong) UIPanGestureRecognizer *panCover;
|
||||
|
||||
@property(nonatomic, strong) UITapGestureRecognizer *singleTap;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIFloatViewController
|
||||
|
||||
- (void)appendChildViewController:(UIViewController<TUIFloatSubViewControllerProtocol> *)vc topMargin:(CGFloat)topMargin {
|
||||
self.childVC = vc;
|
||||
self.topMargin = topMargin;
|
||||
|
||||
[self addChildViewController:vc];
|
||||
[self.containerView addSubview:vc.view];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = [UIColor tui_colorWithHex:@"#000000" alpha:0.6];
|
||||
self.modalPresentationStyle = UIModalPresentationCustom;
|
||||
|
||||
self.containerView.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
self.topImgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:TIMCommonImagePath(@"icon_flex_arrow")]];
|
||||
[self.topGestureView addSubview:self.topImgView];
|
||||
self.topImgView.hidden = YES;
|
||||
|
||||
@weakify(self);
|
||||
self.topGestureView.leftButtonClickCallback = ^{
|
||||
@strongify(self);
|
||||
if ([self.childVC respondsToSelector:@selector(floatControllerLeftButtonClick)]) {
|
||||
[self.childVC performSelector:@selector(floatControllerLeftButtonClick)];
|
||||
}
|
||||
};
|
||||
self.topGestureView.rightButtonClickCallback = ^{
|
||||
@strongify(self);
|
||||
if ([self.childVC respondsToSelector:@selector(floatControllerRightButtonClick)]) {
|
||||
[self.childVC performSelector:@selector(floatControllerRightButtonClick)];
|
||||
}
|
||||
};
|
||||
[self addSingleTapGesture];
|
||||
|
||||
if (!_currentLoaction) {
|
||||
self.currentLoaction = FLEX_TOP;
|
||||
}
|
||||
|
||||
[self updateSubContainerView];
|
||||
}
|
||||
|
||||
- (void)addSingleTapGesture {
|
||||
// When clicking on the shadow, the page disappears
|
||||
self.view.userInteractionEnabled = YES;
|
||||
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
|
||||
singleTap.cancelsTouchesInView = NO;
|
||||
[self.view addGestureRecognizer:singleTap];
|
||||
}
|
||||
|
||||
- (void)singleTap:(UITapGestureRecognizer *)tap {
|
||||
CGPoint translation = [tap locationInView:self.containerView];
|
||||
|
||||
if (translation.x < 0 || translation.y < 0) {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
} else if (translation.x > self.containerView.frame.size.width || translation.y > self.containerView.frame.size.height) {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setnormalTop {
|
||||
self.currentLoaction = FLEX_TOP;
|
||||
}
|
||||
|
||||
- (void)setNormalBottom {
|
||||
self.currentLoaction = FLEX_Bottom;
|
||||
}
|
||||
|
||||
- (void)setCurrentLoaction:(FLEX_Location)currentLoaction {
|
||||
_currentLoaction = currentLoaction;
|
||||
if (currentLoaction == FLEX_TOP) {
|
||||
self.containerView.frame = CGRectMake(0, self.topMargin, self.view.frame.size.width, self.view.frame.size.height - self.topMargin);
|
||||
} else if (currentLoaction == FLEX_Bottom) {
|
||||
self.containerView.frame = CGRectMake(0, self.view.frame.size.height - kScale390(393), self.view.frame.size.width, kScale390(393));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)floatDismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
|
||||
[self dismissViewControllerAnimated:flag completion:completion];
|
||||
}
|
||||
#pragma mark - lazy
|
||||
|
||||
- (UIView *)containerView {
|
||||
if (_containerView == nil) {
|
||||
_containerView = [[UIView alloc] init];
|
||||
_containerView.layer.cornerRadius = kScale390(12);
|
||||
[self.view addSubview:_containerView];
|
||||
}
|
||||
return _containerView;
|
||||
}
|
||||
|
||||
- (UIView *)topGestureView {
|
||||
if (_topGestureView == nil) {
|
||||
_topGestureView = [[TUIFloatTitleView alloc] init];
|
||||
// [_topGestureView addGestureRecognizer:self.panCover];
|
||||
[self.containerView addSubview:_topGestureView];
|
||||
}
|
||||
return _topGestureView;
|
||||
}
|
||||
|
||||
- (UIPanGestureRecognizer *)panCover {
|
||||
if (_panCover == nil) {
|
||||
_panCover = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onPanCover:)];
|
||||
}
|
||||
return _panCover;
|
||||
}
|
||||
|
||||
- (void)onPanCover:(UIPanGestureRecognizer *)pan {
|
||||
CGPoint translation = [pan translationInView:self.topGestureView];
|
||||
CGFloat absX = fabs(translation.x);
|
||||
CGFloat absY = fabs(translation.y);
|
||||
|
||||
if (MAX(absX, absY) < 2) return;
|
||||
if (absX > absY) {
|
||||
if (translation.x < 0) {
|
||||
// scroll left
|
||||
} else {
|
||||
// scroll right
|
||||
}
|
||||
} else if (absY > absX) {
|
||||
if (translation.y < 0) {
|
||||
// scroll up
|
||||
[self.topGestureView removeGestureRecognizer:self.panCover];
|
||||
[UIView animateWithDuration:0.3
|
||||
animations:^{
|
||||
self.currentLoaction = FLEX_TOP;
|
||||
[self.topGestureView addGestureRecognizer:self.panCover];
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
[self updateSubContainerView];
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
// scroll down
|
||||
if (self.currentLoaction == FLEX_Bottom) {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
[self.topGestureView removeGestureRecognizer:self.panCover];
|
||||
[UIView animateWithDuration:0.3
|
||||
animations:^{
|
||||
self.currentLoaction = FLEX_Bottom;
|
||||
[self.topGestureView addGestureRecognizer:self.panCover];
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[self updateSubContainerView];
|
||||
});
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateSubContainerView {
|
||||
self.topGestureView.frame = CGRectMake(0, 0, self.containerView.frame.size.width, kScale390(68.5));
|
||||
self.topImgView.frame = CGRectMake((self.topGestureView.frame.size.width - kScale390(24)) * 0.5, kScale390(22), kScale390(24), kScale390(6));
|
||||
|
||||
self.childVC.view.frame = CGRectMake(0, self.topGestureView.frame.origin.y + self.topGestureView.frame.size.height, self.containerView.frame.size.width,
|
||||
self.containerView.frame.size.height - self.topGestureView.frame.size.height);
|
||||
}
|
||||
@end
|
||||
21
TUIKit/TIMCommon/CommonModel/TUIGroupAvatar+Helper.h
Normal file
21
TUIKit/TIMCommon/CommonModel/TUIGroupAvatar+Helper.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// TUIGroupAvatar+Helper.h
|
||||
// TIMCommon
|
||||
//
|
||||
// Created by wyl on 2023/4/27.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <TUICore/TUIDefine.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIGroupAvatar (Helper)
|
||||
|
||||
+ (UIImage *)getNormalGroupCacheAvatar:(NSString *)groupID groupType:(NSString *)groupType;
|
||||
|
||||
+ (void)configAvatarByParam:(NSDictionary *)param targetView:(UIImageView *)targetView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
125
TUIKit/TIMCommon/CommonModel/TUIGroupAvatar+Helper.m
Normal file
125
TUIKit/TIMCommon/CommonModel/TUIGroupAvatar+Helper.m
Normal file
@@ -0,0 +1,125 @@
|
||||
//
|
||||
// TUIGroupAvatar+Helper.m
|
||||
// TIMCommon
|
||||
//
|
||||
// Created by wyl on 2023/4/27.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <TUICore/TUIConfig.h>
|
||||
#import "TUIGroupAvatar+Helper.h"
|
||||
|
||||
@implementation TUIGroupAvatar (Helper)
|
||||
|
||||
+ (UIImage *)getNormalGroupCacheAvatar:(NSString *)groupID groupType:(NSString *)groupType {
|
||||
/**
|
||||
*
|
||||
* Setup default avatar
|
||||
*/
|
||||
UIImage *avatarImage = nil;
|
||||
if (groupID.length > 0) {
|
||||
/**
|
||||
* If it is a group, change the group default avatar to the last used avatar
|
||||
*/
|
||||
UIImage *avatar = nil;
|
||||
if (TUIConfig.defaultConfig.enableGroupGridAvatar) {
|
||||
NSString *key = [NSString stringWithFormat:@"TUIConversationLastGroupMember_%@", groupID];
|
||||
NSInteger member = [NSUserDefaults.standardUserDefaults integerForKey:key];
|
||||
avatar = [TUIGroupAvatar getCacheAvatarForGroup:groupID number:(UInt32)member];
|
||||
}
|
||||
avatarImage = avatar ? avatar : DefaultGroupAvatarImageByGroupType(groupType);
|
||||
;
|
||||
return avatarImage;
|
||||
}
|
||||
return avatarImage;
|
||||
}
|
||||
+ (void)configAvatarByParam:(NSDictionary *)param targetView:(UIImageView *)targetView {
|
||||
NSString *groupID = param[@"groupID"];
|
||||
NSString *faceUrl = param[@"faceUrl"];
|
||||
NSString *groupType = param[@"groupType"];
|
||||
UIImage *originAvatarImage = param[@"originAvatarImage"];
|
||||
if (groupID.length > 0) {
|
||||
/**
|
||||
*
|
||||
* Group avatar
|
||||
*/
|
||||
if (IS_NOT_EMPTY_NSSTRING(faceUrl)) {
|
||||
/**
|
||||
*
|
||||
* The group avatar has been manually set externally
|
||||
*/
|
||||
[targetView sd_setImageWithURL:[NSURL URLWithString:faceUrl] placeholderImage:originAvatarImage];
|
||||
} else {
|
||||
/**
|
||||
* The group avatar has not been set externally. If the synthetic avatar is allowed, the synthetic avatar will be used; otherwise, the default
|
||||
* avatar will be used.
|
||||
*/
|
||||
if (TUIConfig.defaultConfig.enableGroupGridAvatar) {
|
||||
/**
|
||||
*
|
||||
* If the synthetic avatar is allowed, the synthetic avatar will be used
|
||||
* 1. Asynchronously obtain the cached synthetic avatar according to the number of group members
|
||||
* 2. If the cache is hit, use the cached synthetic avatar directly
|
||||
* 3. If the cache is not hit, recompose a new avatar
|
||||
*
|
||||
* Note:
|
||||
* 1. Since "asynchronously obtaining cached avatars" and "synthesizing avatars" take a long time, it is easy to cause cell reuse problems, so
|
||||
* it is necessary to confirm whether to assign values directly according to groupID.
|
||||
* 2. Use SDWebImage to implement placeholder, because SDWebImage has already dealt with the problem of cell reuse
|
||||
*/
|
||||
|
||||
// 1. Obtain group avatar from cache
|
||||
|
||||
// fix: The getCacheGroupAvatar needs to request the
|
||||
// network. When the network is disconnected, since the headImageView is not set, the current conversation sends a message, the conversation is
|
||||
// moved up, and the avatar of the first conversation is reused, resulting in confusion of the avatar.
|
||||
[targetView sd_setImageWithURL:nil placeholderImage:originAvatarImage];
|
||||
[TUIGroupAvatar getCacheGroupAvatar:groupID
|
||||
callback:^(UIImage *avatar, NSString *groupID) {
|
||||
if ([groupID isEqualToString:groupID]) {
|
||||
// 1.1 When the callback is invoked, the cell is not reused
|
||||
|
||||
if (avatar != nil) {
|
||||
// 2. Hit the cache and assign directly
|
||||
[targetView sd_setImageWithURL:nil placeholderImage:avatar];
|
||||
} else {
|
||||
// 3. Synthesize new avatars asynchronously without hitting cache
|
||||
|
||||
[targetView sd_setImageWithURL:nil placeholderImage:originAvatarImage];
|
||||
[TUIGroupAvatar
|
||||
fetchGroupAvatars:groupID
|
||||
placeholder:originAvatarImage
|
||||
callback:^(BOOL success, UIImage *image, NSString *groupID) {
|
||||
if ([groupID isEqualToString:groupID]) {
|
||||
// When the callback is invoked, the cell is not reused
|
||||
[targetView
|
||||
sd_setImageWithURL:nil
|
||||
placeholderImage:success ? image : DefaultGroupAvatarImageByGroupType(groupType)];
|
||||
} else {
|
||||
// callback, When the callback is invoked, the cell has
|
||||
// been reused to other groupIDs. Since a new callback will be triggered when the new
|
||||
// groupID synthesizes new avatar, it is ignored here
|
||||
}
|
||||
}];
|
||||
}
|
||||
} else {
|
||||
// 1.2 callback ,cell groupID。 groupID
|
||||
// callback, 1.2 When the callback is invoked, the cell has been reused to other groupIDs. Since a new
|
||||
// callback will be triggered when the new groupID gets the cache, it is ignored here
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
/**
|
||||
* Synthetic avatars are not allowed, use the default avatar directly
|
||||
*/
|
||||
[targetView sd_setImageWithURL:nil placeholderImage:originAvatarImage];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* Personal avatar
|
||||
*/
|
||||
[targetView sd_setImageWithURL:[NSURL URLWithString:faceUrl] placeholderImage:originAvatarImage];
|
||||
}
|
||||
}
|
||||
@end
|
||||
22
TUIKit/TIMCommon/CommonModel/TUISecondConfirm.h
Normal file
22
TUIKit/TIMCommon/CommonModel/TUISecondConfirm.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// TUISecondConfirm.h
|
||||
// TIMCommon
|
||||
//
|
||||
// Created by xiangzhang on 2023/5/15.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
typedef void (^TUISecondConfirmBtnClickCallback)(void);
|
||||
@interface TUISecondConfirmBtnInfo : NSObject
|
||||
@property(nonatomic, strong) NSString *tile;
|
||||
@property(nonatomic, copy) TUISecondConfirmBtnClickCallback click;
|
||||
@end
|
||||
|
||||
@interface TUISecondConfirm : NSObject
|
||||
+ (void)show:(NSString *)title cancelBtnInfo:(TUISecondConfirmBtnInfo *)cancelBtnInfo confirmBtnInfo:(TUISecondConfirmBtnInfo *)confirmBtnInfo;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
105
TUIKit/TIMCommon/CommonModel/TUISecondConfirm.m
Normal file
105
TUIKit/TIMCommon/CommonModel/TUISecondConfirm.m
Normal file
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// TUISecondConfirm.m
|
||||
// TIMCommon
|
||||
//
|
||||
// Created by xiangzhang on 2023/5/15.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUISecondConfirm.h"
|
||||
#import <TUICore/TUIDefine.h>
|
||||
#import <TUICore/UIView+TUILayout.h>
|
||||
|
||||
@implementation TUISecondConfirmBtnInfo
|
||||
|
||||
@end
|
||||
|
||||
static UIWindow *gSecondWindow = nil;
|
||||
static TUISecondConfirmBtnInfo *gCancelBtnInfo = nil;
|
||||
static TUISecondConfirmBtnInfo *gConfirmBtnInfo = nil;
|
||||
|
||||
@implementation TUISecondConfirm
|
||||
|
||||
+ (void)show:(NSString *)title cancelBtnInfo:(TUISecondConfirmBtnInfo *)cancelBtnInfo confirmBtnInfo:(TUISecondConfirmBtnInfo *)confirmBtnInfo {
|
||||
gCancelBtnInfo = cancelBtnInfo;
|
||||
gConfirmBtnInfo = confirmBtnInfo;
|
||||
|
||||
gSecondWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
gSecondWindow.windowLevel = UIWindowLevelAlert - 1;
|
||||
gSecondWindow.backgroundColor = [UIColor clearColor];
|
||||
gSecondWindow.hidden = NO;
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
for (UIWindowScene *windowScene in [UIApplication sharedApplication].connectedScenes) {
|
||||
if (windowScene.activationState == UISceneActivationStateForegroundActive) {
|
||||
gSecondWindow.windowScene = windowScene;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UIView *backgroupView = [[UIView alloc] initWithFrame:gSecondWindow.bounds];
|
||||
backgroupView.backgroundColor = RGBA(0, 0, 0, 0.4);
|
||||
[gSecondWindow addSubview:backgroupView];
|
||||
|
||||
UIView *confimView = [[UIView alloc] init];
|
||||
confimView.backgroundColor = TIMCommonDynamicColor(@"second_confirm_bg_color", @"#FFFFFF");
|
||||
confimView.layer.cornerRadius = 13;
|
||||
confimView.layer.masksToBounds = YES;
|
||||
[gSecondWindow addSubview:confimView];
|
||||
confimView.mm_width(gSecondWindow.mm_w - kScale375(32) * 2).mm_height(183).mm__centerX(gSecondWindow.mm_centerX).mm__centerY(gSecondWindow.mm_h / 2);
|
||||
|
||||
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, confimView.mm_w, 123)];
|
||||
titleLabel.text = title;
|
||||
titleLabel.textColor = TIMCommonDynamicColor(@"second_confirm_title_color", @"#000000");
|
||||
titleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
titleLabel.font = [UIFont systemFontOfSize:16];
|
||||
titleLabel.numberOfLines = 0;
|
||||
[confimView addSubview:titleLabel];
|
||||
|
||||
UIView *line1 = [[UIView alloc] initWithFrame:CGRectMake(0, titleLabel.mm_maxY, titleLabel.mm_w, 0.5)];
|
||||
line1.backgroundColor = TIMCommonDynamicColor(@"second_confirm_line_color", @"#DDDDDD");
|
||||
[confimView addSubview:line1];
|
||||
|
||||
UIButton *cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[cancelBtn setFrame:CGRectMake(0, line1.mm_maxY, line1.mm_w / 2, confimView.mm_h - line1.mm_maxY)];
|
||||
[cancelBtn setTitle:gCancelBtnInfo.tile forState:UIControlStateNormal];
|
||||
[cancelBtn.titleLabel setFont:[UIFont systemFontOfSize:16]];
|
||||
[cancelBtn setTitleColor:TIMCommonDynamicColor(@"second_confirm_cancel_btn_title_color", @"#000000") forState:UIControlStateNormal];
|
||||
[cancelBtn addTarget:self action:@selector(onCancelBtnClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
[confimView addSubview:cancelBtn];
|
||||
|
||||
UIView *line2 = [[UIView alloc] initWithFrame:CGRectMake(cancelBtn.mm_maxX, cancelBtn.mm_y, 0.5, cancelBtn.mm_h)];
|
||||
line2.backgroundColor = TIMCommonDynamicColor(@"second_confirm_line_color", @"#DDDDDD");
|
||||
[confimView addSubview:line2];
|
||||
|
||||
UIButton *confirmBtn = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[confirmBtn setFrame:CGRectMake(line2.mm_maxX, cancelBtn.mm_y, cancelBtn.mm_w, cancelBtn.mm_h)];
|
||||
[confirmBtn setTitle:gConfirmBtnInfo.tile forState:UIControlStateNormal];
|
||||
[confirmBtn.titleLabel setFont:[UIFont systemFontOfSize:16]];
|
||||
[confirmBtn setTitleColor:TIMCommonDynamicColor(@"second_confirm_confirm_btn_title_color", @"#FF584C") forState:UIControlStateNormal];
|
||||
[confirmBtn addTarget:self action:@selector(onConfirmBtnBtnClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
[confimView addSubview:confirmBtn];
|
||||
}
|
||||
|
||||
+ (void)onCancelBtnClick {
|
||||
if (gCancelBtnInfo && gCancelBtnInfo.click) {
|
||||
gCancelBtnInfo.click();
|
||||
}
|
||||
[self dismiss];
|
||||
}
|
||||
|
||||
+ (void)onConfirmBtnBtnClick {
|
||||
if (gConfirmBtnInfo && gConfirmBtnInfo.click) {
|
||||
gConfirmBtnInfo.click();
|
||||
}
|
||||
[self dismiss];
|
||||
}
|
||||
|
||||
+ (void)dismiss {
|
||||
gSecondWindow = nil;
|
||||
gCancelBtnInfo = nil;
|
||||
gConfirmBtnInfo = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
25
TUIKit/TIMCommon/CommonModel/TUITextView.h
Normal file
25
TUIKit/TIMCommon/CommonModel/TUITextView.h
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// TUITextView.h
|
||||
// Masonry
|
||||
//
|
||||
// Created by xiangzhang on 2022/10/14.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol TUITextViewDelegate <NSObject>
|
||||
- (void)onLongPressTextViewMessage:(UITextView *)textView;
|
||||
@end
|
||||
|
||||
@interface TUITextView : UITextView
|
||||
@property (nonatomic, strong) UILongPressGestureRecognizer *longPressGesture;
|
||||
@property (nonatomic, weak) id<TUITextViewDelegate> tuiTextViewDelegate;
|
||||
|
||||
- (void)disableHighlightLink;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
65
TUIKit/TIMCommon/CommonModel/TUITextView.m
Normal file
65
TUIKit/TIMCommon/CommonModel/TUITextView.m
Normal file
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// TUITextView.m
|
||||
// Masonry
|
||||
//
|
||||
// Created by xiangzhang on 2022/10/14.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUITextView.h"
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
|
||||
@implementation TUITextView
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.dataDetectorTypes = UIDataDetectorTypeLink | UIDataDetectorTypePhoneNumber;
|
||||
[self setupLongPressGesture];
|
||||
self.tintColor = TIMCommonDynamicColor(@"chat_highlight_link_color", @"#6495ED");
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeFirstResponder {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)buildMenuWithBuilder:(id<UIMenuBuilder>)builder API_AVAILABLE(ios(13.0)) {
|
||||
if (@available(iOS 16.0, *)) {
|
||||
[builder removeMenuForIdentifier:UIMenuLookup];
|
||||
}
|
||||
[super buildMenuWithBuilder:builder];
|
||||
}
|
||||
|
||||
- (void)disableHighlightLink {
|
||||
self.dataDetectorTypes = UIDataDetectorTypeNone;
|
||||
}
|
||||
|
||||
- (void)setupLongPressGesture {
|
||||
self.longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
|
||||
[self addGestureRecognizer:self.longPressGesture];
|
||||
}
|
||||
|
||||
- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture {
|
||||
if ([gesture isKindOfClass:[UILongPressGestureRecognizer class]] && gesture.state == UIGestureRecognizerStateBegan) {
|
||||
if (self.tuiTextViewDelegate && [self.tuiTextViewDelegate respondsToSelector:@selector(onLongPressTextViewMessage:)]) {
|
||||
[self.tuiTextViewDelegate onLongPressTextViewMessage:self];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
|
||||
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
|
||||
if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] &&
|
||||
gestureRecognizer != self.longPressGesture) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
25
TUIKit/TIMCommon/CommonModel/TUIUserAuthorizationCenter.h
Normal file
25
TUIKit/TIMCommon/CommonModel/TUIUserAuthorizationCenter.h
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// TUIUserAuthorizationCenter.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by wyl on 2022/2/16.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_OPTIONS(NSUInteger, TUIChatAuthControlType) {
|
||||
TUIChatAuthControlTypeMicro = 1 << 0,
|
||||
TUIChatAuthControlTypeCamera = 1 << 1,
|
||||
TUIChatAuthControlTypePhoto = 1 << 2,
|
||||
};
|
||||
@interface TUIUserAuthorizationCenter : NSObject
|
||||
@property(nonatomic, assign, class, readonly) BOOL isEnableCameraAuthorization;
|
||||
@property(nonatomic, assign, class, readonly) BOOL isEnableMicroAuthorization;
|
||||
|
||||
+ (void)cameraStateActionWithPopCompletion:(void (^)(void))completion API_AVAILABLE(ios(8.0));
|
||||
+ (void)microStateActionWithPopCompletion:(void (^)(void))completion API_AVAILABLE(ios(8.0));
|
||||
|
||||
+ (void)openSettingPage;
|
||||
+ (void)showAlert:(TUIChatAuthControlType)type;
|
||||
@end
|
||||
136
TUIKit/TIMCommon/CommonModel/TUIUserAuthorizationCenter.m
Normal file
136
TUIKit/TIMCommon/CommonModel/TUIUserAuthorizationCenter.m
Normal file
@@ -0,0 +1,136 @@
|
||||
//
|
||||
// TUIUserAuthorizationCenter.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by wyl on 2022/2/16.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIUserAuthorizationCenter.h"
|
||||
#import <AVFoundation/AVCaptureDevice.h>
|
||||
#import <CoreLocation/CLLocationManager.h>
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
#import <EventKit/EventKit.h>
|
||||
#import <Photos/Photos.h>
|
||||
#import <Speech/Speech.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
|
||||
#import <TUICore/TUIGlobalization.h>
|
||||
@implementation TUIUserAuthorizationCenter
|
||||
|
||||
+ (BOOL)isEnableCameraAuthorization {
|
||||
if (@available(iOS 7.0, *)) {
|
||||
return [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] == AVAuthorizationStatusAuthorized;
|
||||
} else {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)cameraStateActionWithPopCompletion:(void (^)(void))completion {
|
||||
if ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] == AVAuthorizationStatusNotDetermined) {
|
||||
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo
|
||||
completionHandler:^(BOOL granted) {
|
||||
if (granted && completion) {
|
||||
completion();
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
[self showAlert:TUIChatAuthControlTypeCamera];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)openSettingPage {
|
||||
if (@available(iOS 8.0, *)) {
|
||||
NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
|
||||
if ([[UIApplication sharedApplication] canOpenURL:url]) {
|
||||
if (@available(iOS 10.0, *)) {
|
||||
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
|
||||
} else {
|
||||
[[UIApplication sharedApplication] openURL:url];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)isEnableMicroAuthorization {
|
||||
if (@available(iOS 7.0, *)) {
|
||||
return [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio] == AVAuthorizationStatusAuthorized;
|
||||
} else {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)microStateActionWithPopCompletion:(void (^)(void))completion {
|
||||
#if !TARGET_OS_MACCATALYST
|
||||
if ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio] == AVAuthorizationStatusNotDetermined) {
|
||||
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
|
||||
completionHandler:^(BOOL granted) {
|
||||
if (granted && completion) {
|
||||
completion();
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
[self showAlert:TUIChatAuthControlTypeMicro];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (BOOL)isEnableLocationAuthorization {
|
||||
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
|
||||
if (@available(iOS 8.0, *)) {
|
||||
return status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse;
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)showAlert:(TUIChatAuthControlType)type {
|
||||
NSString *title = @"";
|
||||
NSString *message = @"";
|
||||
NSString *laterMessage = @"";
|
||||
NSString *openSettingMessage = @"";
|
||||
|
||||
if (TUIChatAuthControlTypeMicro == type) {
|
||||
title = TIMCommonLocalizableString(TUIKitInputNoMicTitle);
|
||||
message = TIMCommonLocalizableString(TUIKitInputNoMicTips);
|
||||
laterMessage = TIMCommonLocalizableString(TUIKitInputNoMicOperateLater);
|
||||
openSettingMessage = TIMCommonLocalizableString(TUIKitInputNoMicOperateEnable);
|
||||
} else if (TUIChatAuthControlTypeCamera == type) {
|
||||
title = TIMCommonLocalizableString(TUIKitInputNoCameraTitle);
|
||||
message = TIMCommonLocalizableString(TUIKitInputNoCameraTips);
|
||||
laterMessage = TIMCommonLocalizableString(TUIKitInputNoCameraOperateLater);
|
||||
openSettingMessage = TIMCommonLocalizableString(TUIKitInputNoCameraOperateEnable);
|
||||
} else if (TUIChatAuthControlTypePhoto == type) {
|
||||
title = TIMCommonLocalizableString(TUIKitInputNoPhotoTitle);
|
||||
message = TIMCommonLocalizableString(TUIKitInputNoPhotoTips);
|
||||
laterMessage = TIMCommonLocalizableString(TUIKitInputNoPhotoOperateLater);
|
||||
openSettingMessage = TIMCommonLocalizableString(TUIKitInputNoPhotoerateEnable);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (@available(iOS 8.0, *)) {
|
||||
UIAlertController *ac = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
|
||||
[ac tuitheme_addAction:[UIAlertAction actionWithTitle:laterMessage style:UIAlertActionStyleCancel handler:nil]];
|
||||
[ac tuitheme_addAction:[UIAlertAction actionWithTitle:openSettingMessage
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
UIApplication *app = [UIApplication sharedApplication];
|
||||
NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
|
||||
if ([app canOpenURL:settingsURL]) {
|
||||
[app openURL:settingsURL];
|
||||
}
|
||||
}]];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[UIApplication.sharedApplication.keyWindow.rootViewController presentViewController:ac animated:YES completion:nil];
|
||||
// [self presentViewController:ac animated:YES completion:nil];
|
||||
});
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user