引言
- 富文本编辑器的应用场景:编辑商品详情
预览:
- 设计思路:编辑器基于WKWebview实现,Editor使用WKWebview加载一个本地editor.html文件,Editor使用
evaluateJavaScript
执行JS往本地html添加标签代码,编辑器最终输出富文本字符串(html代码)传输给服务器。
"remark":"<p>商品详情看看</p>n<p style="text-align: right;">jjjj<img src="http://bug.xxx.com:7000/zentao/file-read-6605.png" alt="dddd" width="750" height="4052" /></p>n<p style="text-align: right;"> </p>n<p style="text-align: right;"> </p>n<p style="text-align: right;"> </p>n<p style="text-align: right;"> </p>n<p style="text-align: right;"> </p>n<p style="text-align: right;"> </p>n<p style="text-align: right;"><img src="http://bug.xxx.com:7000/zentao/file-read-6605.png" alt="" width="750" height="4052" /></p>"
- 使用IQKeyboardManager 键盘管理工具,布局采用Masonry,MVVM数据绑定。
- 界面设计:推荐把工具栏添加到键盘,或者放在富文本编辑器的顶部
I 上篇:核心代码逻辑
https://blog.csdn.net/z929118967/article/details/125298245
II 工具栏设计(含demo)
2.1 工具栏在富文本编辑器的底部
demo: https://download.csdn.net/download/u011018979/85675638
2.2 工具栏在富文本编辑器的顶部
demo地址:https://download.csdn.net/download/u011018979/85959921
代码语言:javascript复制-(WKWebView *)editorView{
if (!_editorView) {
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc]init];
WKUserContentController *userCon = [[WKUserContentController alloc]init];
config.userContentController = userCon;
WKWebView *tmp = [[WKWebView alloc]initWithFrame:CGRectZero configuration:config];
//223
// [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, pDeviceWidth, self.view.frame.size.height-KWEditorBar_Height) configuration:config];
_editorView = tmp;
// [userCon addScriptMessageHandler:self name:@"column"];
tmp.navigationDelegate = self;
tmp.hidesInputAccessoryView = YES;// 去掉键盘自带的工具条
tmp.scrollView.bounces = NO;
tmp.backgroundColor = [UIColor whiteColor];
// anObserver:观察者对象,这个对象必须实现observeValueForKeyPath:ofObject:change:context:方法,以响应属性的修改通知。
// keyPath:被监听的属性。这个值不能为nil。
// options:监听选项,这个值可以是NSKeyValueObservingOptions选项的组合。关于监听选项,我们会在下面介绍。
// context:任意的额外数据,我们可以将这些数据作为上下文数据,它会传递给观察者对象的observeValueForKeyPath:ofObject:change:context:方法。这个参数的意义在于用于区分同一对象监听同一属性(从属于同一对象)的多个不同的监听。我们将在下面看到。
//
[tmp addObserver:self forKeyPath:@"URL" options:NSKeyValueObservingOptionNew context:nil];
[self addSubview:tmp];
__weak __typeof__(self) weakSelf = self;
[tmp mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.offset(0);//test
make.height.mas_equalTo(kAdjustRatio(223));
// make.top.offset(0);
make.left.right.offset(0);
make.top.equalTo(weakSelf.toolBarView.mas_bottom).offset(0);
}];
[self initConfig];
}
return _editorView;
}
- (KNEditorBar *)toolBarView{
if (!_toolBarView) {
KNEditorBar *tmp = [KNEditorBar editorBar];
_toolBarView = tmp;
tmp.frame = CGRectMake(0,self.frame.size.height - KWEditorBar_Height, self.frame.size.width, KWEditorBar_Height);
tmp.backgroundColor = rgb(237, 237, 237);
[self addSubview:tmp];
__weak __typeof__(self) weakSelf = self;
[tmp mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.offset(0);
make.height.mas_equalTo(KWEditorBar_Height);
// make.top.equalTo(weakSelf.editorView.mas_bottom).offset(0);
make.top.offset(KWFontBar_Height-13);
// make.bottom.offset(0);
}];
tmp.delegate = self;
[tmp addObserver:self forKeyPath:@"transform" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
self.editorView.associatedObject = tmp;
}
return _toolBarView;
}
- (KNFontStyleBar *)fontBar{
if (!_fontBar) {
_fontBar = [[KNFontStyleBar alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.toolBarView.frame) - KWFontBar_Height - KWEditorBar_Height, self.frame.size.width, KWFontBar_Height)];
_fontBar.delegate = self;
[_fontBar.heading2Item setSelected:YES];
}
return _fontBar;
}
#pragma mark -editorbarDelegate,通知外围html有变动
- (void)editorBar:(KNEditorBar *)editorBar didClickIndex:(k_RichToolBarButtonIndex)buttonIndex{
switch (buttonIndex) {
case k_RichToolBarButtonIndex4KeyboardButton:{//键盘唤醒与隐藏
NSLog(@"%f",self.toolBarView.transform.ty);
// if (self.toolBarView.transform.ty < 0) {
if (self.models.isVisable) {
[self.editorView hiddenKeyboard];
// self.toolBarView.keyboardButton.selected = NO;
}else{
[self.editorView focusTextEditor];
self.toolBarView.keyboardButton.selected = YES;
}
}
break;
case k_RichToolBarButtonIndex4undoButton:{//后退
[self.editorView undo];
}
break;
case k_RichToolBarButtonIndex4redoButton:{//前进
[self.editorView redo];
}
break;
case k_RichToolBarButtonIndex4fontButton:{//字体
editorBar.fontButton.selected = !editorBar.fontButton.selected;
if (editorBar.fontButton.selected) {
[self addSubview:self.fontBar];
__weak __typeof__(self) weakSelf = self;
[self.fontBar mas_updateConstraints:^(MASConstraintMaker *make) {
// _fontBar = [[KNFontStyleBar alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.toolBarView.frame) - KWFontBar_Height - KWEditorBar_Height, self.frame.size.width, KWFontBar_Height)];
make.bottom.equalTo(weakSelf.toolBarView.mas_top);
make.left.right.offset(0);
// make.width.offset(0);
make.height.mas_equalTo( KWFontBar_Height);
}];
}else{
[self.fontBar removeFromSuperview];
}
}
break;
case k_RichToolBarButtonIndex4linkButton:{//超连接
[self insertLink];
}
break;
case k_RichToolBarButtonIndex4imageButton:{//图片
[self insertImage];
}
break;
default:
break;
}
}
III 常见问题
3.1 隐藏键盘失去焦点时,图片内容被清空
问题: 标签的占位符,通过监听失去焦点事件,如果文本trim之后长度为0,就清空div数据,显示占位符。
原因:element.text()
获取的是文本,只判断了文本,而忽略了img标签。
<div id="zss_editor_content" class="zs_editor_content" contenteditable="true" placeholder="请输入文章正文"></div>
代码语言:javascript复制zss_editor.setPlaceholder = function(placeholder) {
var editor = $('#zss_editor_content');
//set placeHolder
editor.attr("placeholder",placeholder);
//set focus
editor.focusout(function(){//当元素失去焦点时触发 focusout 事件
var element = $(this);
if (!element.text().trim().length) {
element.empty();
}
});
}
解决方式:修改占位符的判断条件
代码语言:javascript复制 //element.text() 获取的是文本,只判断了文本,而忽略了img标签。,所以换成html()判断子标签
// window.alert(element.html());
if (!element.html().trim().length ) {
element.empty();
}
去除换行和空格在判断是否内容为空
代码语言:javascript复制 var html =element.html().trim();
if (!html.length || html=="<br>") {
element.empty();
}
3.2 插入图片无效
问题:插入图片无效 原因:点击插入图片按钮时,WebView获取到焦点 解决方式:点击插入图片按钮时,尝试获取焦点。
代码语言:javascript复制#pragma mark - 插入图片
//https://csdnimg.cn/medal/blinknewcomer@240.png
-(void)insertImage{
if (!self.models.isVisable) {
[self.editorView focusTextEditor];// 获取焦点
}
[self.editorView prepareInsertImage];
if(self.models.insertImageBlock){
self.models.insertImageBlock(nil);
}
}
#pragma mark - 插入链接
- (void)insertLink {
if (!self.models.isVisable) {
[self.editorView focusTextEditor];// 获取焦点
}
[self.editorView prepareInsertImage];
if(self.models.showInsertLinkDialogBlockWithLink){
self.models.showInsertLinkDialogBlockWithLink(nil,nil);
}
// [self showInsertLinkDialogWithLink:nil title:nil];
}
3.3 插入的网络图片 只能添加到最后位置
问题:插入的网络图片 只能添加到最后位置,不能再中间插入。
原因:插入图片时多次获取焦点,把焦点定位到最后的位置 解决:使用5.2 章节的方式
代码语言:javascript复制 if (!self.viewModel.model4editor.isVisable) {
// [self.viewModel.model4editor.editorView focusTextEditor];
}