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

2022-08-22 11:34:53 浏览数 (2)

引言

  1. 富文本编辑器的应用场景:编辑商品详情

预览:

  1. 设计思路:编辑器基于WKWebview实现,Editor使用WKWebview加载一个本地editor.html文件,Editor使用evaluateJavaScript执行JS往本地html添加标签代码,编辑器最终输出富文本字符串(html代码)传输给服务器。
代码语言:javascript复制
"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. 界面设计:推荐把工具栏添加到键盘,或者放在富文本编辑器的顶部

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标签。

代码语言:javascript复制
<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];
        
    }
    

0 人点赞