16

我正在为我正在开发的 iPhone 应用程序尝试富文本编辑器(具有 HTML 导出功能),并决定使用 iOS 5 的 WebKit 支持contentEditable/ designMode。我遇到了一个问题,该问题正在满足我的需要。在 UIWebView 中编辑内容时,不会自动滚动跟随光标,例如在 UITextView 中。键入时,光标继续在 scrollView 下,用户必须手动向上滚动。

以下是一些相关代码:

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    NSString *string = @"document.body.contentEditable=true;document.designMode='on';void(0)";
    [webView stringByEvaluatingJavaScriptFromString:string];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
}

任何想法如何解决这个问题?我不确定这是否也发生在 Safari 或仅在UIWebViewWebKit 实现中。

如果您遇到此问题,请确保前往https://bugreport.apple.com并复制rdar://16455638

4

5 回答 5

11

经过数小时的研究,我找到了更好的解决方案,所以我想我会分享。

iOS 5 的 contentEditable 实现中存在一个错误,主 scrollView 不会使用插入符号滚动。如果将 body 标签(或任何动态大小的元素)设为 contentEditable,插入符号将始终离开屏幕。

如果您将可编辑的 div 设置为 overflow: scroll,您会注意到该 div 将滚动。默认情况下,div 的滚动不会“反弹”或具有滚动条,但您可以将-webkit-overflow-scrolling: touch属性应用于 div 来解决此问题。

有了这些信息,您可以使用如下包装器来修复它:

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=no;"/>
<style type="text/css">
    html, body {height: 100%;}
    body {
        margin: 0;
    }
    #wrap {
        height: 100%;
        width: 100%;
        overflow-y: scroll;
        -webkit-overflow-scrolling: touch;
    }
    #editor {
        padding: 10px;
    }
</style>
</head>
<body>
<div id="wrap">
    <div id="editor" contenteditable="true">

    </div>
</div>
</body>
</html>

基本上,您正在滚动 div 而不是文档。

不幸的是,div 的“scrollView”不知道虚拟键盘,所以插入符号会消失在键盘后面。但是,您会注意到插入符号的位置仍位于键盘下方的屏幕上。所以要解决这个问题,降低 div/UIWebView 的高度以适应键盘。

您可能想要做的其他事情是禁用主滚动视图上的滚动:

webView.scrollView.scrollEnabled = NO;

主 scrollView 无论如何都不应该滚动,但它应该防止任何滚动故障。

于 2012-07-26T18:03:37.467 回答
4

为了回答我自己的问题,我们最终做了很多工作来确保它在多个 iOS 版本中都能正常工作。我们发现所有的滚动事件都是由于UIWebView试图管理它的UIWebScrollView. 我们决定不使用 a UIWebView,而是采用内部UIWebDocumentView/UIWebBrowserView并将其添加到我们自己的滚动视图中。这使我们能够自行管理内容大小和滚动,并消除了我们之前遇到的大部分问题。

于 2014-01-01T18:53:52.770 回答
1

首先,您必须注册键盘通知,并在该 keyboardWillShow 方法中,以 0.1 个计时器间隔触发滚动方法。

 -(void) keyboardWillShow:(NSNotification *)note
    {

        timer   =   [NSTimer    scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(scroll) userInfo:nil repeats:YES];

    }

    -(void) keyboardWillHide:(NSNotification *)note
    {
        [timer invalidate];

    }

在 .h 文件中创建一个成员变量,并在 init() 方法中为其分配内存。

  NSString    *prevStr;

在该滚动方法中,执行以下操作。

-(void)scroll{

    if (![prevStr isEqualToString:[WebView stringByEvaluatingJavaScriptFromString:@"document.body.innerHTML"]]) {
        prevStr  =   [[WebView stringByEvaluatingJavaScriptFromString:@"document.body.innerHTML"] retain];
        NSInteger height = [[WebView stringByEvaluatingJavaScriptFromString:@"document.body.offsetHeight;"] intValue];
        NSString* javascript = [NSString stringWithFormat:@"window.scrollBy(0, %d);", height];   
        [WebView stringByEvaluatingJavaScriptFromString:javascript];   
    }

}

这将允许您在编辑内容并且内容大于 WebView 框架时滚动到底部。在其他时候,您将能够滚动到页面顶部(那时自动滚动将被暂停)。

于 2011-12-22T14:19:12.413 回答
0

我无法设置“溢出:滚动”,因为它弄乱了我们已经很好的 css(如果我们在单击可编辑 div 时更改了溢出,那么布局就会混乱)。带着这个去:

$(this).on('keypress',function(e){
    //$(this) is the contenteditable div
    var lineHeight = parseInt($(this).css('line-height')) + 2;
    //gets the height of the div        
    newh=$(this).height();
    if (typeof oldh === 'undefined')
        oldh=newh;//saves the current height
    if(oldh!=newh)//checks if the height changes
    {
        //if height changes, scroll up by 1 line (plus a little 2 px)            
        window.scrollBy(0,lineHeight);
        oldh=newh;//resave the saved height since it changed
    }
});

希望这可以帮助某人。皮塔饼

于 2014-01-21T04:21:14.747 回答
0

您可以尝试通过一些棘手的技巧来获得插入符号框架并手动滚动到可见的矩形。

// Call this after every change in editable HTML document
func bodyDidChanged() {
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
        let caretRect = self.caretRect(inView: self.webView)
        self.webView.scrollView.scrollRectToVisible(caretRect.insetBy(dx: -2, dy: -2), animated: true)
    }
}

private func caretRect(inView parentView: UIView? ) -> CGRect {
    guard let parentView = parentView else {
        return CGRect.null
    }

    var rect: CGRect?
    for view in parentView.subviews {
        if view.isKind(of: NSClassFromString("UITextSelectionView")!) {
            // If have a selected text, then we seeking last blue dot (grabber) inside rangeView
            var rangeView = view.subviews.first?.subviews.last
            if rangeView == nil {
                // If text not selected then carret frame is same as rangeView frame
                rangeView = view.subviews.first
            }
            rect = rangeView?.frame
            break
        } else {
            rect = caretRect(inView: view)
            if let rect = rect, !rect.isNull {
                break
            }
        }
    }
    return rect ?? CGRect.null
}
于 2018-03-21T12:40:40.323 回答