Files
featherVoice/TUIKit/TUICore/TUICommonModel.m

808 lines
29 KiB
Mathematica
Raw Normal View History

2025-08-08 10:49:36 +08:00
//
// TCommonCell.m
// TXIMSDK_TUIKit_iOS
//
// Created by annidyfeng on 2019/5/6.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUICommonModel.h"
#import <objc/runtime.h>
#import "NSString+TUIUtil.h"
#import "TUIDarkModel.h"
#import "TUIGlobalization.h"
#import "TUIThemeManager.h"
#import "TUITool.h"
#import "UIView+TUILayout.h"
/////////////////////////////////////////////////////////////////////////////////
//
// TUIUserFullInfo
//
/////////////////////////////////////////////////////////////////////////////////
@implementation V2TIMUserFullInfo (TUIUserFullInfo)
- (NSString *)showName {
if ([NSString isEmpty:self.nickName]) return self.userID;
return self.nickName;
}
- (NSString *)showGender {
if (self.gender == V2TIM_GENDER_MALE) return TUIKitLocalizableString(Male);
if (self.gender == V2TIM_GENDER_FEMALE) return TUIKitLocalizableString(Female);
return TUIKitLocalizableString(Unsetted);
}
- (NSString *)showSignature {
if (self.selfSignature == nil) return TUIKitLocalizableString(TUIKitNoSelfSignature);
return [NSString stringWithFormat:TUIKitLocalizableString(TUIKitSelfSignatureFormat), self.selfSignature];
}
- (NSString *)showAllowType {
if (self.allowType == V2TIM_FRIEND_ALLOW_ANY) {
return TUIKitLocalizableString(TUIKitAllowTypeAcceptOne);
}
if (self.allowType == V2TIM_FRIEND_NEED_CONFIRM) {
return TUIKitLocalizableString(TUIKitAllowTypeNeedConfirm);
}
if (self.allowType == V2TIM_FRIEND_DENY_ANY) {
return TUIKitLocalizableString(TUIKitAllowTypeDeclineAll);
}
return nil;
}
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIUserModel
//
/////////////////////////////////////////////////////////////////////////////////
@implementation TUIUserModel
- (id)copyWithZone:(NSZone *)zone {
TUIUserModel *model = [[TUIUserModel alloc] init];
model.userId = self.userId;
model.name = self.name;
model.avatar = self.avatar;
return model;
}
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIScrollView
//
/////////////////////////////////////////////////////////////////////////////////
static void *gScrollViewBoundsChangeNotificationContext = &gScrollViewBoundsChangeNotificationContext;
@interface TUIScrollView ()
@property(nonatomic, readonly) CGFloat imageAspectRatio;
@property(nonatomic) CGRect initialImageFrame;
@property(strong, nonatomic, readonly) UITapGestureRecognizer *tap;
@end
@implementation TUIScrollView
@synthesize tap = _tap;
#pragma mark - Tap to Zoom
- (UITapGestureRecognizer *)tap {
if (_tap == nil) {
_tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapToZoom:)];
_tap.numberOfTapsRequired = 2;
}
return _tap;
}
- (void)tapToZoom:(UIGestureRecognizer *)gestureRecognizer {
if (self.zoomScale > self.minimumZoomScale) {
[self setZoomScale:self.minimumZoomScale animated:YES];
} else {
CGPoint tapLocation = [gestureRecognizer locationInView:self.imageView];
CGFloat zoomRectWidth = self.imageView.frame.size.width / self.maximumZoomScale;
CGFloat zoomRectHeight = self.imageView.frame.size.height / self.maximumZoomScale;
CGFloat zoomRectX = tapLocation.x - zoomRectWidth * 0.5;
CGFloat zoomRectY = tapLocation.y - zoomRectHeight * 0.5;
CGRect zoomRect = CGRectMake(zoomRectX, zoomRectY, zoomRectWidth, zoomRectHeight);
[self zoomToRect:zoomRect animated:YES];
}
}
#pragma mark - Private Methods
- (void)configure {
self.showsVerticalScrollIndicator = NO;
self.showsHorizontalScrollIndicator = NO;
[self startObservingBoundsChange];
}
- (void)setImageView:(UIImageView *)imageView {
if (_imageView.superview == self) {
[_imageView removeGestureRecognizer:self.tap];
[_imageView removeFromSuperview];
}
if (imageView) {
_imageView = imageView;
_initialImageFrame = CGRectNull;
_imageView.userInteractionEnabled = YES;
[_imageView addGestureRecognizer:self.tap];
[self addSubview:imageView];
}
}
- (CGFloat)imageAspectRatio {
if (self.imageView.image) {
return self.imageView.image.size.width / self.imageView.image.size.height;
}
return 1;
}
- (CGSize)rectSizeForAspectRatio:(CGFloat)ratio thatFitsSize:(CGSize)size {
CGFloat containerWidth = size.width;
CGFloat containerHeight = size.height;
CGFloat resultWidth = 0;
CGFloat resultHeight = 0;
if ((ratio <= 0) || (containerHeight <= 0)) {
return size;
}
if (containerWidth / containerHeight >= ratio) {
resultHeight = containerHeight;
resultWidth = containerHeight * ratio;
} else {
resultWidth = containerWidth;
resultHeight = containerWidth / ratio;
}
return CGSizeMake(resultWidth, resultHeight);
}
- (void)scaleImageForScrollViewTransitionFromBounds:(CGRect)oldBounds toBounds:(CGRect)newBounds {
CGPoint oldContentOffset = CGPointMake(oldBounds.origin.x, oldBounds.origin.y);
CGSize oldSize = oldBounds.size;
CGSize newSize = newBounds.size;
CGSize containedImageSizeOld = [self rectSizeForAspectRatio:self.imageAspectRatio thatFitsSize:oldSize];
CGSize containedImageSizeNew = [self rectSizeForAspectRatio:self.imageAspectRatio thatFitsSize:newSize];
if (containedImageSizeOld.height <= 0) {
containedImageSizeOld = containedImageSizeNew;
}
CGFloat orientationRatio = (containedImageSizeNew.height / containedImageSizeOld.height);
CGAffineTransform t = CGAffineTransformMakeScale(orientationRatio, orientationRatio);
self.imageView.frame = CGRectApplyAffineTransform(self.imageView.frame, t);
self.contentSize = self.imageView.frame.size;
CGFloat xOffset = (oldContentOffset.x + oldSize.width * 0.5) * orientationRatio - newSize.width * 0.5;
CGFloat yOffset = (oldContentOffset.y + oldSize.height * 0.5) * orientationRatio - newSize.height * 0.5;
xOffset -= MAX(xOffset + newSize.width - self.contentSize.width, 0);
yOffset -= MAX(yOffset + newSize.height - self.contentSize.height, 0);
xOffset += MAX(-xOffset, 0);
yOffset += MAX(-yOffset, 0);
self.contentOffset = CGPointMake(xOffset, yOffset);
}
- (void)setupInitialImageFrame {
if (self.imageView.image && CGRectEqualToRect(self.initialImageFrame, CGRectNull)) {
CGSize imageViewSize = [self rectSizeForAspectRatio:self.imageAspectRatio thatFitsSize:self.bounds.size];
self.initialImageFrame = CGRectMake(0, 0, imageViewSize.width, imageViewSize.height);
self.imageView.frame = self.initialImageFrame;
self.contentSize = self.initialImageFrame.size;
}
}
#pragma mark - KVO
- (void)startObservingBoundsChange {
[self addObserver:self
forKeyPath:@"bounds"
options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
context:gScrollViewBoundsChangeNotificationContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context {
if (context == gScrollViewBoundsChangeNotificationContext) {
CGRect oldRect = ((NSValue *)change[NSKeyValueChangeOldKey]).CGRectValue;
CGRect newRect = ((NSValue *)change[NSKeyValueChangeNewKey]).CGRectValue;
if (!CGSizeEqualToSize(oldRect.size, newRect.size)) {
[self scaleImageForScrollViewTransitionFromBounds:oldRect toBounds:newRect];
}
}
}
- (void)dealloc {
[self removeObserver:self forKeyPath:@"bounds"];
}
#pragma mark - UIScrollView
- (void)setContentOffset:(CGPoint)contentOffset {
const CGSize contentSize = self.contentSize;
const CGSize scrollViewSize = self.bounds.size;
if (contentSize.width < scrollViewSize.width) {
contentOffset.x = -(scrollViewSize.width - contentSize.width) * 0.5;
}
if (contentSize.height < scrollViewSize.height) {
contentOffset.y = -(scrollViewSize.height - contentSize.height) * 0.5;
}
[super setContentOffset:contentOffset];
}
#pragma mark - UIView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self configure];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self configure];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
[self setupInitialImageFrame];
}
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIGroupAvatar
//
/////////////////////////////////////////////////////////////////////////////////
#define groupAvatarWidth (48 * [[UIScreen mainScreen] scale])
@implementation TUIGroupAvatar
+ (void)createGroupAvatar:(NSArray *)group finished:(void (^)(UIImage *groupAvatar))finished {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSInteger avatarCount = group.count > 9 ? 9 : group.count;
CGFloat width = groupAvatarWidth / 3 * 0.90;
CGFloat space3 = (groupAvatarWidth - width * 3) / 4;
CGFloat space2 = (groupAvatarWidth - width * 2 + space3) / 2;
CGFloat space1 = (groupAvatarWidth - width) / 2;
__block CGFloat y = avatarCount > 6 ? space3 : (avatarCount > 3 ? space2 : space1);
__block CGFloat x = avatarCount % 3 == 0 ? space3 : (avatarCount % 3 == 2 ? space2 : space1);
width = avatarCount > 4 ? width : (avatarCount > 1 ? (groupAvatarWidth - 3 * space3) / 2 : groupAvatarWidth);
if (avatarCount == 1) {
x = 0;
y = 0;
}
if (avatarCount == 2) {
x = space3;
} else if (avatarCount == 3) {
x = (groupAvatarWidth - width) / 2;
y = space3;
} else if (avatarCount == 4) {
x = space3;
y = space3;
}
dispatch_async(dispatch_get_main_queue(), ^{
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, groupAvatarWidth, groupAvatarWidth)];
[view setBackgroundColor:[UIColor colorWithWhite:0.8 alpha:0.6]];
view.layer.cornerRadius = 6;
__block NSInteger count = 0;
for (NSInteger i = avatarCount - 1; i >= 0; i--) {
NSString *avatarUrl = [group objectAtIndex:i];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(x, y, width, width)];
[view addSubview:imageView];
[imageView sd_setImageWithURL:[NSURL URLWithString:avatarUrl]
placeholderImage:DefaultAvatarImage
completed:^(UIImage *_Nullable image, NSError *_Nullable error, SDImageCacheType cacheType, NSURL *_Nullable imageURL) {
count++;
if (count == avatarCount) {
UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 2.0);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndPDFContext();
CGImageRef imageRef = image.CGImage;
CGImageRef imageRefRect =
CGImageCreateWithImageInRect(imageRef, CGRectMake(0, 0, view.frame.size.width * 2, view.frame.size.height * 2));
UIImage *ansImage = [[UIImage alloc] initWithCGImage:imageRefRect];
CGImageRelease(imageRefRect);
dispatch_async(dispatch_get_main_queue(), ^{
if (finished) {
finished(ansImage);
}
});
}
}];
if (avatarCount == 3) {
if (i == 2) {
y = width + space3 * 2;
x = space3;
} else {
x += width + space3;
}
} else if (avatarCount == 4) {
if (i % 2 == 0) {
y += width + space3;
x = space3;
} else {
x += width + space3;
}
} else {
if (i % 3 == 0) {
y += (width + space3);
x = space3;
} else {
x += (width + space3);
}
}
}
});
});
}
+ (void)fetchGroupAvatars:(NSString *)groupID placeholder:(UIImage *)placeholder callback:(void (^)(BOOL success, UIImage *image, NSString *groupID))callback {
@tui_weakify(self);
[[V2TIMManager sharedInstance] getGroupMemberList:groupID
filter:V2TIM_GROUP_MEMBER_FILTER_ALL
nextSeq:0
succ:^(uint64_t nextSeq, NSArray<V2TIMGroupMemberFullInfo *> *memberList) {
@tui_strongify(self);
int i = 0;
NSMutableArray *groupMemberAvatars = [NSMutableArray arrayWithCapacity:1];
for (V2TIMGroupMemberFullInfo *member in memberList) {
if (member.faceURL.length > 0) {
[groupMemberAvatars addObject:member.faceURL];
} else {
[groupMemberAvatars addObject:@"http://placeholder"];
}
if (++i == 9) {
break;
}
}
if (i <= 1) {
[self asyncClearCacheAvatarForGroup:groupID];
if (callback) {
callback(NO, placeholder, groupID);
}
return;
}
NSString *key = [NSString stringWithFormat:@"TUIConversationLastGroupMember_%@", groupID];
[NSUserDefaults.standardUserDefaults setInteger:groupMemberAvatars.count forKey:key];
[NSUserDefaults.standardUserDefaults synchronize];
[TUIGroupAvatar createGroupAvatar:groupMemberAvatars
finished:^(UIImage *groupAvatar) {
@tui_strongify(self);
UIImage *avatar = groupAvatar;
[self cacheGroupAvatar:avatar number:(UInt32)groupMemberAvatars.count groupID:groupID];
if (callback) {
callback(YES, avatar, groupID);
}
}];
}
fail:^(int code, NSString *msg) {
if (callback) {
callback(NO, placeholder, groupID);
}
}];
}
+ (void)cacheGroupAvatar:(UIImage *)avatar number:(UInt32)memberNum groupID:(NSString *)groupID {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
if (groupID == nil || groupID.length == 0) {
return;
}
NSString *tempPath = NSTemporaryDirectory();
NSString *filePath = [NSString stringWithFormat:@"%@groupAvatar_%@_%d.png", tempPath, groupID, memberNum];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// check to delete old file
NSNumber *oldValue = [defaults objectForKey:groupID];
if (oldValue != nil) {
UInt32 oldMemberNum = [oldValue unsignedIntValue];
NSString *oldFilePath = [NSString stringWithFormat:@"%@groupAvatar_%@_%d.png", tempPath, groupID, oldMemberNum];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtPath:oldFilePath error:nil];
}
// Save image.
BOOL success = [UIImagePNGRepresentation(avatar) writeToFile:filePath atomically:YES];
if (success) {
[defaults setObject:@(memberNum) forKey:groupID];
}
});
}
+ (void)getCacheGroupAvatar:(NSString *)groupID callback:(void (^)(UIImage *, NSString *groupID))imageCallBack {
if (groupID == nil || groupID.length == 0) {
if (imageCallBack) {
imageCallBack(nil, groupID);
}
return;
}
[[V2TIMManager sharedInstance] getGroupsInfo:@[ groupID ]
succ:^(NSArray<V2TIMGroupInfoResult *> *groupResultList) {
V2TIMGroupInfoResult *groupInfo = groupResultList.firstObject;
if (!groupInfo) {
imageCallBack(nil, groupID);
return;
}
UInt32 memberNum = groupInfo.info.memberCount;
memberNum = MAX(1, memberNum);
memberNum = MIN(memberNum, 9);
;
NSString *tempPath = NSTemporaryDirectory();
NSString *filePath = [NSString stringWithFormat:@"%@groupAvatar_%@_%u.png", tempPath, groupID, (unsigned int)memberNum];
NSFileManager *fileManager = [NSFileManager defaultManager];
UIImage *avatar = nil;
BOOL success = [fileManager fileExistsAtPath:filePath];
if (success) {
avatar = [[UIImage alloc] initWithContentsOfFile:filePath];
NSString *key = [NSString stringWithFormat:@"TUIConversationLastGroupMember_%@", groupID];
[NSUserDefaults.standardUserDefaults setInteger:memberNum forKey:key];
[NSUserDefaults.standardUserDefaults synchronize];
}
imageCallBack(avatar, groupInfo.info.groupID);
}
fail:^(int code, NSString *msg) {
imageCallBack(nil, groupID);
}];
}
+ (UIImage *)getCacheAvatarForGroup:(NSString *)groupId number:(UInt32)memberNum {
memberNum = MAX(1, memberNum);
memberNum = MIN(memberNum, 9);
;
NSString *tempPath = NSTemporaryDirectory();
NSString *filePath = [NSString stringWithFormat:@"%@groupAvatar_%@_%u.png", tempPath, groupId, (unsigned int)memberNum];
NSFileManager *fileManager = [NSFileManager defaultManager];
UIImage *avatar = nil;
BOOL success = [fileManager fileExistsAtPath:filePath];
if (success) {
avatar = [[UIImage alloc] initWithContentsOfFile:filePath];
}
return avatar;
}
+ (void)asyncClearCacheAvatarForGroup:(NSString *)groupID {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSString *tempPath = NSTemporaryDirectory();
for (int i = 0; i < 9; i++) {
NSString *filePath = [NSString stringWithFormat:@"%@groupAvatar_%@_%d.png", tempPath, groupID, (i + 1)];
if ([NSFileManager.defaultManager fileExistsAtPath:filePath]) {
[NSFileManager.defaultManager removeItemAtPath:filePath error:nil];
}
}
});
}
@end
/////////////////////////////////////////////////////////////////////////////////
//
// TUIImageCache
//
/////////////////////////////////////////////////////////////////////////////////
@interface TUIImageCache ()
@property(nonatomic, strong) NSMutableDictionary *resourceCache;
@property(nonatomic, strong) NSMutableDictionary *faceCache;
@end
@implementation TUIImageCache
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
static TUIImageCache *instance;
dispatch_once(&onceToken, ^{
instance = [[TUIImageCache alloc] init];
[UIImage d_fixResizableImage];
});
return instance;
}
- (instancetype)init {
self = [super init];
if (self) {
_resourceCache = [NSMutableDictionary dictionary];
_faceCache = [NSMutableDictionary dictionary];
}
return self;
}
- (void)addResourceToCache:(NSString *)path {
__weak typeof(self) ws = self;
[TUITool asyncDecodeImage:path
complete:^(NSString *key, UIImage *image) {
__strong __typeof(ws) strongSelf = ws;
if (key && image) {
[strongSelf.resourceCache setValue:image forKey:key];
}
}];
}
- (UIImage *)getResourceFromCache:(NSString *)path {
if (path.length == 0) {
return nil;
}
UIImage *image = [_resourceCache objectForKey:path];
if (!image) {
image = [UIImage d_imagePath:path];
}
return image;
}
- (void)addFaceToCache:(NSString *)path {
__weak typeof(self) ws = self;
[TUITool asyncDecodeImage:path
complete:^(NSString *key, UIImage *image) {
__strong __typeof(ws) strongSelf = ws;
if (key && image) {
[strongSelf.faceCache setValue:image forKey:key];
}
}];
}
- (UIImage *)getFaceFromCache:(NSString *)path {
if (path.length == 0) {
return nil;
}
UIImage *image = [_faceCache objectForKey:path];
if (!image) {
// gif extion
if ([path tui_containsString:@".gif"]) {
image = [UIImage sd_imageWithGIFData:[NSData dataWithContentsOfFile:path]];
}
else {
image = [UIImage imageWithContentsOfFile:path];
if (!image) {
// gif
NSString *formatPath = [path stringByAppendingString:@".gif"];
image = [UIImage sd_imageWithGIFData:[NSData dataWithContentsOfFile:formatPath]];
}
}
if (!image) {
image = [_faceCache objectForKey:TUIChatFaceImagePath(@"ic_unknown_image")];
}
}
return image;
}
@end
@interface IUCoreView : UIView
@property(nonatomic, strong) UIView *view;
@end
@implementation IUCoreView
- (instancetype)init {
self = [super init];
if (self) {
self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
[self addSubview:self.view];
}
return self;
}
@end
@implementation TUINavigationController
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController {
if (self = [super initWithRootViewController:rootViewController]) {
self.interactivePopGestureRecognizer.delegate = self;
self.delegate = self;
}
return self;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (@available(iOS 13.0, *)) {
UINavigationBarAppearance *appearance = [UINavigationBarAppearance new];
[appearance configureWithDefaultBackground];
appearance.shadowColor = nil;
appearance.backgroundEffect = nil;
appearance.backgroundColor = self.navigationBackColor;
self.navigationBar.backgroundColor = self.navigationBackColor;
self.navigationBar.barTintColor = self.navigationBackColor;
self.navigationBar.shadowImage = [UIImage new];
self.navigationBar.standardAppearance = appearance;
self.navigationBar.scrollEdgeAppearance = appearance;
} else {
self.navigationBar.backgroundColor = self.navigationBackColor;
self.navigationBar.barTintColor = self.navigationBackColor;
self.navigationBar.shadowImage = [UIImage new];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
self.delegate = self;
}
- (void)back {
if ([self.uiNaviDelegate respondsToSelector:@selector(navigationControllerDidClickLeftButton:)]) {
[self.uiNaviDelegate navigationControllerDidClickLeftButton:self];
}
[self popViewControllerAnimated:YES];
}
- (UIColor *)navigationBackColor {
if (!_navigationBackColor) {
_navigationBackColor = [self tintColor];
}
return _navigationBackColor;
}
- (UIColor *)tintColor {
return TUICoreDynamicColor(@"head_bg_gradient_start_color", @"#EBF0F6");
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
if (self.viewControllers.count != 0) {
viewController.hidesBottomBarWhenPushed = YES;
self.tabBarController.tabBar.hidden = YES;
UIImage *image = self.navigationItemBackArrowImage;
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
UIBarButtonItem *back = [[UIBarButtonItem alloc] initWithImage:image style:UIBarButtonItemStylePlain target:self action:@selector(back)];
viewController.navigationItem.leftBarButtonItems = @[ back ];
viewController.navigationItem.leftItemsSupplementBackButton = NO;
}
[super pushViewController:viewController animated:animated];
}
- (UIImage *)navigationItemBackArrowImage {
if (!_navigationItemBackArrowImage) {
_navigationItemBackArrowImage = TUICoreDynamicImage(@"nav_back_img", [UIImage imageNamed:TUICoreImagePath(@"nav_back")]);
}
return _navigationItemBackArrowImage;
}
// fix: https://developer.apple.com/forums/thread/660750
- (NSArray<__kindof UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated {
if (@available(iOS 14.0, *)) {
for (UIViewController *vc in self.viewControllers) {
vc.hidesBottomBarWhenPushed = NO;
self.tabBarController.tabBar.hidden = NO;
}
}
return [super popToRootViewControllerAnimated:animated];
}
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
if (navigationController.viewControllers.count == 1) {
/**
* If the number of view controllers in the stack is 1, it means only the root controller, clear the currentShowVC, and disable the swipe gesture for
* the following method
*/
self.currentShowVC = Nil;
} else {
/**
* Assign the pushed view controller to currentShowVC
*/
self.currentShowVC = viewController;
}
if ([navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
if (self.viewControllers.count == 1) {
/**
* Forbid the sliding back of the home page
*/
navigationController.interactivePopGestureRecognizer.enabled = NO;
} else {
navigationController.interactivePopGestureRecognizer.enabled = YES;
}
}
}
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
/**
* Listen to the event returned by the side slide
*/
__weak typeof(self) weakSelf = self;
if (@available(iOS 10.0, *)) {
[viewController.transitionCoordinator notifyWhenInteractionChangesUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf handleSideSlideReturnIfNeeded:context];
}];
} else {
[viewController.transitionCoordinator notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf handleSideSlideReturnIfNeeded:context];
}];
}
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer == self.interactivePopGestureRecognizer) {
if (self.currentShowVC == self.topViewController) {
/**
* If currentShowVC exists, it means that the number of controllers in the stack is greater than 1, allowing the side slide gesture to be activated
*/
return YES;
}
return NO;
}
return YES;
}
- (void)handleSideSlideReturnIfNeeded:(id<UIViewControllerTransitionCoordinatorContext>)context {
if (context.isCancelled) {
return;
}
UIViewController *fromVc = [context viewControllerForKey:UITransitionContextFromViewControllerKey];
if ([self.uiNaviDelegate respondsToSelector:@selector(navigationControllerDidSideSlideReturn:fromViewController:)]) {
[self.uiNaviDelegate navigationControllerDidSideSlideReturn:self fromViewController:fromVc];
}
}
@end
@implementation UIAlertController (TUITheme)
- (void)tuitheme_addAction:(UIAlertAction *)action {
if (action.style == UIAlertActionStyleDefault || action.style == UIAlertActionStyleCancel) {
UIColor *tempColor = TUICoreDynamicColor(@"primary_theme_color", @"#147AFF");
[action setValue:tempColor forKey:@"_titleTextColor"];
}
[self addAction:action];
}
@end
static const void *tui_valueCallbackKey = @"tui_valueCallbackKey";
static const void *tui_nonValueCallbackKey = @"tui_nonValueCallbackKey";
static const void *tui_extValueObjKey = @"tui_extValueObjKey";
@implementation NSObject (TUIExtValue)
- (void)setTui_valueCallback:(TUIValueCallbck)tui_valueCallback {
objc_setAssociatedObject(self, tui_valueCallbackKey, tui_valueCallback, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (TUIValueCallbck)tui_valueCallback {
return objc_getAssociatedObject(self, tui_valueCallbackKey);
}
- (void)setTui_nonValueCallback:(TUINonValueCallbck)tui_nonValueCallback {
objc_setAssociatedObject(self, tui_nonValueCallbackKey, tui_nonValueCallback, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (TUINonValueCallbck)tui_nonValueCallback {
return objc_getAssociatedObject(self, tui_nonValueCallbackKey);
}
- (void)setTui_extValueObj:(id)tui_extValueObj {
objc_setAssociatedObject(self, tui_extValueObjKey, tui_extValueObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)tui_extValueObj {
return objc_getAssociatedObject(self, tui_extValueObjKey);
}
@end