增加换肤功能
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
//
|
||||
// TUIBotRichTextCell.m
|
||||
// TUICustomerServicePlugin
|
||||
//
|
||||
// Created by lynx on 2024/3/1.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIBotRichTextCell.h"
|
||||
#import <TUIChat/TUITextMessageCell.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
@interface TUIBotRichTextCell ()<WKNavigationDelegate>
|
||||
@property(nonatomic, strong) WKWebView *webView;
|
||||
@property(nonatomic, strong) UIColor *webViewBkColor;
|
||||
@property(nonatomic, strong) UIColor *webViewTextColor;
|
||||
@end
|
||||
|
||||
@implementation TUIBotRichTextCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self) {
|
||||
[self.container addSubview:self.webView];
|
||||
self.webViewTextColor = [TUITextMessageCell incommingTextColor];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
|
||||
self.webViewBkColor = nil;
|
||||
[self.webView evaluateJavaScript:[NSString stringWithFormat:@"document.body.style.backgroundColor=\"%@\"",
|
||||
[self hexStringFromColor:self.webViewBkColor]] completionHandler:nil];
|
||||
[self.webView evaluateJavaScript:[NSString stringWithFormat:@"document.getElementById('content').style.color=\"%@\"",
|
||||
[self hexStringFromColor:self.webViewTextColor]] completionHandler:nil];
|
||||
}
|
||||
|
||||
- (WKWebView *)webView {
|
||||
if (!_webView) {
|
||||
WKWebViewConfiguration *config = [WKWebViewConfiguration new];
|
||||
NSString *script = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
|
||||
WKUserScript *wkUserScript = [[WKUserScript alloc] initWithSource:script injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
|
||||
WKUserContentController *wkUserController = [[WKUserContentController alloc] init];
|
||||
[wkUserController addUserScript: wkUserScript];
|
||||
config.userContentController = wkUserController;
|
||||
_webView = [[WKWebView alloc]initWithFrame:self.bounds configuration:config];
|
||||
_webView.navigationDelegate = self;
|
||||
_webView.backgroundColor = [UIColor clearColor];
|
||||
[_webView.scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
|
||||
_webView.scrollView.scrollEnabled = NO;
|
||||
|
||||
NSString *bundlePath = TUIBundlePath(TUICustomerServicePluginBundle,TUICustomerServicePluginBundle_Key_Class);
|
||||
NSString *path = [bundlePath stringByAppendingPathComponent:@"markdown.html"];
|
||||
if (path) {
|
||||
NSURL *url = [NSURL fileURLWithPath:path];
|
||||
NSURLRequest *requset = [NSURLRequest requestWithURL:url];
|
||||
[_webView loadRequest:requset];
|
||||
}
|
||||
}
|
||||
return _webView;
|
||||
}
|
||||
|
||||
- (UIColor *)webViewBkColor {
|
||||
if (!_webViewBkColor && self.bubbleView.image) {
|
||||
UIImage *image = [self.bubbleView.image.imageAsset imageWithTraitCollection:[UITraitCollection currentTraitCollection]];
|
||||
_webViewBkColor = [self colorFromImage:image point:CGPointMake(trunc(10), trunc(10))];
|
||||
}
|
||||
return _webViewBkColor;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
// this is Apple's recommended place for adding/updating constraints
|
||||
- (void)updateConstraints {
|
||||
[super updateConstraints];
|
||||
[self.webView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(10);
|
||||
make.top.mas_equalTo(2);
|
||||
make.width.mas_equalTo(self.container.mm_w - 10 * 2);
|
||||
make.height.mas_equalTo(self.container.mm_h - 2 * 2);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)fillWithData:(TUIBotRichTextCellData *)data {
|
||||
// set data
|
||||
[super fillWithData:data];
|
||||
self.webViewData = data;
|
||||
[self loadMarkdownContent];
|
||||
|
||||
// tell constraints they need updating
|
||||
[self setNeedsUpdateConstraints];
|
||||
|
||||
// update constraints now so we can animate the change
|
||||
[self updateConstraintsIfNeeded];
|
||||
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (void)loadMarkdownContent {
|
||||
NSString *content = [self getMarkdownContentWithMarkdowString:self.webViewData.content];
|
||||
NSString *js = [NSString stringWithFormat:@"javascript:parseMarkdown(\"%@\",true)", content];
|
||||
[self.webView evaluateJavaScript:js completionHandler:nil];
|
||||
// Set webview background color
|
||||
[self.webView evaluateJavaScript:[NSString stringWithFormat:@"document.body.style.backgroundColor=\"%@\"",
|
||||
[self hexStringFromColor:self.webViewBkColor]] completionHandler:nil];
|
||||
// Set webview text color
|
||||
[self.webView evaluateJavaScript:[NSString stringWithFormat:@"document.getElementById('content').style.color=\"%@\"",
|
||||
[self hexStringFromColor:self.webViewTextColor]] completionHandler:nil];
|
||||
// Webview disables sliding and zooming
|
||||
NSString *injectionJSString = @"var script = document.createElement('meta');script.name = 'viewport';"
|
||||
"script.content=\"width=device-width, user-scalable=no\";"
|
||||
"document.getElementsByTagName('head')[0].appendChild(script);";
|
||||
[self.webView evaluateJavaScript:injectionJSString completionHandler:nil];
|
||||
}
|
||||
|
||||
- (NSString *)getMarkdownContentWithMarkdowString:(NSString *)markdown {
|
||||
markdown = [markdown stringByReplacingOccurrencesOfString:@"\r"withString:@""];
|
||||
markdown = [markdown stringByReplacingOccurrencesOfString:@"\n"withString:@"\\n"];
|
||||
markdown = [markdown stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
|
||||
markdown = [markdown stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"];
|
||||
markdown = [markdown stringByReplacingOccurrencesOfString:@"<br>" withString:@"\\n"];
|
||||
return markdown;
|
||||
}
|
||||
|
||||
#pragma mark - TUIMessageCellProtocol
|
||||
+ (CGSize)getContentSize:(TUIMessageCellData *)data {
|
||||
if ([data isKindOfClass:[TUIBotRichTextCellData class]]) {
|
||||
return CGSizeMake(TRichTextMessageCell_Width_Max, [(TUIBotRichTextCellData *)data cellHeight]);
|
||||
}
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
#pragma mark - WKNavigationDelegate
|
||||
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
|
||||
[self loadMarkdownContent];
|
||||
[self updateCellSize];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
|
||||
if ([keyPath isEqualToString:@"contentSize"]) {
|
||||
[self limitUpdateCellSize];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
|
||||
if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
|
||||
if ([[UIApplication sharedApplication] canOpenURL:navigationAction.request.URL]) {
|
||||
[[UIApplication sharedApplication] openURL:navigationAction.request.URL options:@{} completionHandler:nil];
|
||||
}
|
||||
decisionHandler(WKNavigationActionPolicyCancel);
|
||||
} else {
|
||||
decisionHandler (WKNavigationActionPolicyAllow);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)limitUpdateCellSize {
|
||||
uint64_t curTs = [[NSDate date] timeIntervalSince1970];
|
||||
uint64_t interval = 1; // s
|
||||
if (curTs - self.webViewData.lastUpdateTs >= interval && self.webViewData.lastUpdateTs) {
|
||||
self.webViewData.lastUpdateTs = curTs;
|
||||
[self updateCellSize];
|
||||
} else {
|
||||
if (self.webViewData.delayUpdate) {
|
||||
return;
|
||||
}
|
||||
self.webViewData.delayUpdate = YES;
|
||||
@weakify(self)
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(interval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
@strongify(self)
|
||||
[self updateCellSize];
|
||||
self.webViewData.delayUpdate = NO;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateCellSize {
|
||||
@weakify(self);
|
||||
[self.webView evaluateJavaScript:@"document.body.scrollWidth" completionHandler:^(id _Nullable result,NSError *_Nullable error) {
|
||||
CGFloat scrollWidth= [result doubleValue];
|
||||
[self.webView evaluateJavaScript:@"document.body.scrollHeight"completionHandler:^(id _Nullable result,NSError*_Nullable error) {
|
||||
@strongify(self)
|
||||
CGFloat scrollHeight = [result doubleValue];
|
||||
CGFloat ratio = CGRectGetWidth(self.webView.frame) /scrollWidth;
|
||||
CGFloat webHeight = scrollHeight * ratio;
|
||||
if (self.webViewData.cellHeight != webHeight) {
|
||||
self.webViewData.cellHeight = webHeight;
|
||||
[self notifyCellSizeChanged];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)notifyCellSizeChanged {
|
||||
NSDictionary *param = @{TUICore_TUIPluginNotify_PluginViewSizeChangedSubKey_Message : self.webViewData.innerMessage};
|
||||
[TUICore notifyEvent:TUICore_TUIPluginNotify
|
||||
subKey:TUICore_TUIPluginNotify_PluginViewSizeChangedSubKey
|
||||
object:nil
|
||||
param:param];
|
||||
}
|
||||
|
||||
#pragma mark Util
|
||||
- (UIColor *)colorFromImage:(UIImage *)image point:(CGPoint)point {
|
||||
CGImageRef cgImage = image.CGImage;
|
||||
NSUInteger width = image.size.width;
|
||||
NSUInteger height = image.size.height;
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
int bytesPerPixel = 4;
|
||||
int bytesPerRow = bytesPerPixel * 1;
|
||||
NSUInteger bitsPerComponent = 8;
|
||||
unsigned char pixelData[4] = { 0, 0, 0, 0 };
|
||||
CGContextRef context = CGBitmapContextCreate(pixelData,
|
||||
1,
|
||||
1,
|
||||
bitsPerComponent,
|
||||
bytesPerRow,
|
||||
colorSpace,
|
||||
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
CGContextSetBlendMode(context, kCGBlendModeCopy);
|
||||
|
||||
// Draw the pixel we are interested in onto the bitmap context
|
||||
CGContextTranslateCTM(context, -point.x, point.y-(CGFloat)height);
|
||||
CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, (CGFloat)width, (CGFloat)height), cgImage);
|
||||
CGContextRelease(context);
|
||||
|
||||
// Convert color values [0..255] to floats [0.0..1.0]
|
||||
CGFloat red = (CGFloat)pixelData[0] / 255.0f;
|
||||
CGFloat green = (CGFloat)pixelData[1] / 255.0f;
|
||||
CGFloat blue = (CGFloat)pixelData[2] / 255.0f;
|
||||
CGFloat alpha = (CGFloat)pixelData[3] / 255.0f;
|
||||
return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
|
||||
}
|
||||
|
||||
- (NSString *)hexStringFromColor:(UIColor *)color {
|
||||
if (!color) {
|
||||
return @"";
|
||||
}
|
||||
const CGFloat *components = CGColorGetComponents(color.CGColor);
|
||||
|
||||
CGFloat r = components[0];
|
||||
CGFloat g = components[1];
|
||||
CGFloat b = components[2];
|
||||
|
||||
return [NSString stringWithFormat:@"#%02lX%02lX%02lX",
|
||||
lroundf(r * 255),
|
||||
lroundf(g * 255),
|
||||
lroundf(b * 255)];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user