携手创作,共同生长!这是我参与「日新计划 8 月更文应战」的第13天,点击查看活动概况

导言

  1. 富文本编辑器的应用场景: 编辑产品概况

预览:

iOS小技能:富文本编辑器(下篇)

  1. 规划思路: 编辑器基于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;\">&nbsp;</p>\n<p style=\"text-align: right;\">&nbsp;</p>\n<p style=\"text-align: right;\">&nbsp;</p>\n<p style=\"text-align: right;\">&nbsp;</p>\n<p style=\"text-align: right;\">&nbsp;</p>\n<p style=\"text-align: right;\">&nbsp;</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>"
  1. 运用IQKeyboardManager 键盘管理工具,布局采用Masonry,MVVM数据绑定。

  2. 界面规划:推荐把工具栏增加到键盘,或者放在富文本编辑器的顶部

iOS小技能:富文本编辑器(下篇)

I 上篇: 核心代码逻辑

blog.csdn.net/z929118967/…

II 工具栏规划(含demo)

2.1 工具栏在富文本编辑器的底部

demo: download.csdn.net/download/u0…

iOS小技能:富文本编辑器(下篇)

iOS小技能:富文本编辑器(下篇)

2.2 工具栏在富文本编辑器的顶部

iOS小技能:富文本编辑器(下篇)
demo地址:download.csdn.net/download/u0…

-(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>
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();
        }
    });
}

处理方法: 修正占位符的判断条件

        //element.text() 获取的是文本,只判断了文本,而疏忽了img标签。,所以换成html()判断子标签
//        window.alert(element.html());
        if (!element.html().trim().length ) {
            element.empty();
        }

iOS小技能:富文本编辑器(下篇)

去除换行和空格在判断是否内容为空

        var html =element.html().trim();
        if (!html.length || html=="<br>") {
            element.empty();
        }

3.2 刺进图片无效

问题:刺进图片无效 原因: 点击刺进图片按钮时,WebView获取到焦点 处理方法:点击刺进图片按钮时,尝试获取焦点。

#pragma mark - 刺进图片
//https://www.6hu.cc/wp-content/uploads/2023/02/1677160730-3ebe39d5e5c341c.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 章节的方法

    if (!self.viewModel.model4editor.isVisable) {
//        [self.viewModel.model4editor.editorView focusTextEditor];
    }

see also

大众号:iOS逆向