UITableViewCell中的UIWebView仅在第一个单元

时间:2017-02-26 04:31:39

标签: javascript ios uiwebview

我在UITable中的每个UITableViewCell中使用UIWebView作为附件视图,以便在每个表视图中显示一个小的Openlayers映射。当我第一次显示表视图时,第一个单元格中的UIWebView显示地图正常,但所有其他单元格中的其他UIWebViews都是空的。

如果我上下滚动表格,那么其他单元格在返回视图时都会正常加载,每个单元格都有不同的小地图。

UIWebViews每个都在init上加载一个初始的HMTL文档,这个文档包含标准的Openlayers ol.js javascript。

附件视图UIView子类是UIWebView的委托,等待它加载,然后发送另一个javascript来绘制地图功能。

登录此委托方法会显示网络视图loaded属性为YES / TRUE,但javascript readyState仍为loading

为什么会这样?我怎样才能按预期工作?

通过调用loadHTMLString:baseURL:方法中的drawRect:,告知webView加载通用HTML / javascript。然后webViewDidFinishLoad:委托方法用于检查加载何时完成,以便在通用HTML / javascript完成后运行特定于单元格的javascript。

相关代码是:

- (void)drawRect:(CGRect)rect {
    NSString *path = [NSBundle pathForResource:@"style" ofType:@"html" inDirectory:[[NSBundle mainBundle] bundlePath]];
    NSString *html = [NSString stringWithContentsOfFile:path usedEncoding:nil error:nil];

    [_webView loadHTMLString:html baseURL:[NSURL fileURLWithPath:[path stringByDeletingLastPathComponent] isDirectory:YES]];
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    if ( ! _webView.loading ) {
        if ( [[webView stringByEvaluatingJavaScriptFromString:@"document.readyState"] isEqualToString:@"complete"] ) {
            NSLog(@"All good");
            [self drawFeatures];
        } else {
            NSLog(@"ERROR:  webView loaded, readyState '%@'", [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"]);
            //[self performSelector:@selector(drawFeatures) withObject:nil afterDelay:0.5];
        }
    }
}

[self drawFeatures]组装一些javascript并将其发送到UIWebView进行运行,它依赖于在初始HTML网页中完成的标准Openlayers(ol.js)javascript。)。

输出结果为:

2017-02-26 15:15:00.071 myapp[50443:25603667] ERROR:  webView loaded, readyState 'loading'
2017-02-26 15:15:00.093 myapp[50443:25603667] ERROR:  webView loaded, readyState 'loading'
2017-02-26 15:15:00.116 myapp[50443:25603667] ERROR:  webView loaded, readyState 'loading'
2017-02-26 15:15:00.132 myapp[50443:25603667] ERROR:  webView loaded, readyState 'loading'
2017-02-26 15:15:00.148 myapp[50443:25603667] ERROR:  webView loaded, readyState 'loading'
2017-02-26 15:15:00.166 myapp[50443:25603667] ERROR:  webView loaded, readyState 'loading'
2017-02-26 15:15:00.190 myapp[50443:25603667] ERROR:  webView loaded, readyState 'loading'
2017-02-26 15:15:00.208 myapp[50443:25603667] ERROR:  webView loaded, readyState 'loading'
2017-02-26 15:15:00.226 myapp[50443:25603667] ERROR:  webView loaded, readyState 'loading'
2017-02-26 15:15:00.245 myapp[50443:25603667] ERROR:  webView loaded, readyState 'loading'
2017-02-26 15:15:00.262 myapp[50443:25603667] ERROR:  webView loaded, readyState 'loading'
2017-02-26 15:15:00.292 myapp[50443:25603667] All good

正如您从此输出中看到的,大多数单元格的isLoaded状态为真,对于FIRST CELL而言isLoaded为真(在真正加载并实际准备好之后出现)。这意味着当Web视图确实没有加载时,它会认为它已被加载。

永远不会调用加载错误委托方法,因此似乎没有HTML / UIWebView错误:

-(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
    NSLog(@"ERROR loading style view HTML:\n%@", [error description]);
}

请注意,如果我更改委托方法以仅检查javascript readyState,并忽略isLoading值,那么我永远不会得到完整的readyState。只需loading。据推测,这是因为Web查看它已完成加载的事情,因此这个委托方法不会被再次调用。

正常工作:

我可以通过取消注释上面委托方法中注释掉的performSelector:withObject:afterDelay:来解决此问题。这表明HTML最终会加载OK,但是委托方法在它准备就绪后永远不会被调用。

如何在不诉诸延迟执行的狡猾解决方法的情况下让这个按预期工作?

更新替代方法

这是另一个试图让这个工作的版本,部分基于来自@ Ricowere答案的想法(类似于我在开始drawRect: hack之前的开始

HTML加载现在在我的UIWebView子类方法中。在这种情况下,适当的方法是当我设置UIWebView子类的属性时:

- (void)setStyle:(RenderStyle *)style {
    _style = style;

    ...
    ...

    NSString *path = [NSBundle pathForResource:@"style" ofType:@"html" inDirectory:[[NSBundle mainBundle] bundlePath]];
    NSString *html = [NSString stringWithContentsOfFile:path usedEncoding:nil error:nil];

    [self loadHTMLString:html baseURL:[NSURL fileURLWithPath:[path stringByDeletingLastPathComponent] isDirectory:YES]];
}

然后在表视图委托中,每次表视图单元格出列时,将UIWebView子类视图作为附件视图添加到它,然后为该UIWebView子类视图调用加载HTML的属性方法:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"styleCell"];

    if ( cell == nil ) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"styleCell"];
        ...
        ...
    }

    ...
    ...
    StyleViewUIWebViewSubclass *styleView = [[StyleViewUIWebViewSubclass alloc] initWithFrame:frame];

    cell.accessoryView = styleView;

    styleView.style = [[RenderStyle alloc] initWithString:styles[styleName]];

    return cell;
}

然而,这仍然存在同样的问题。为了让它发挥作用,仍然需要上面的延迟执行工作。

2 个答案:

答案 0 :(得分:1)

首先,删除drawRect中的代码。这种方法是完全错误的,你将业务逻辑放在绘图方法中。 (执行一次,然后重复使用单元格)

在这里,我向您展示了解决此问题的简单方法。

class CellExample: UITableViewCell, UIWebViewDelegate {
    var accessoryWebView: UIWebView? {
        return accessoryView as? UIWebView
    }

    //If you're dequeuing the cell from IB.
    override func awakeFromNib() {
        // Set the frame you need or do this through autolayout.
        let webView = UIWebView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
        accessoryView = webView
    }

    //You have to call this everytime a cell is dequeued 
    //(Then the content is properly displayed again.)
    func load(url: URL) {
        accessoryWebView?.delegate = self

        //Load the request or the HTML content as you need.

        //accessoryWebView?.loadHTMLString("whatever", baseURL: URL(string: "whatever")!)
//      accessoryWebView?.loadRequest(URLRequest(url: url,
//                                               cachePolicy: .returnCacheDataElseLoad,
//                                               timeoutInterval: 10 ))
    }

    override func prepareForReuse() {
        super.prepareForReuse()

        //If the cell is reused 'restart' the stack.
        accessoryWebView?.delegate = nil
        accessoryWebView?.stopLoading()
    }

    func webViewDidFinishLoad(_ webView: UIWebView) {
        // Do the stuff you need about the javascript.
    }
}

答案 1 :(得分:0)

感谢@Ricowere的一些提示,我得到了这个工作。我的解决方案基于@Ricowere回答的想法,但使用UITableView委托而不是UITableViewCell子类,并在Objective C而不是Swift中完成所有这些。

这是最终为我工作的......

在我的WKWebView子类中:

- (id)initWithFrame:(CGRect)frame {
    if ( self = [super initWithFrame:frame] ) {
        ...
        self.navigationDelegate = self;
    }

    return self;
}

- (void)setStyle:(RenderStyle *)style {
    _style = style;

    NSURL *url = [NSBundle URLForResource:@"style" withExtension:@"html" subdirectory:nil inBundleWithURL:[[NSBundle mainBundle] bundleURL]];

    [self loadFileURL:url allowingReadAccessToURL:[url URLByDeletingLastPathComponent]];
}

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    [self drawFeatures];  //  This line runs the secondary instance-dependant javascript that relies on the generic javascript already having been completed
}

然后在我的UITableViewDelegate中:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"styleCell"];

    if ( cell == nil ) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"styleCell"];
        ...
        WKWebViewSubclass *styleView = [[WKWebViewSubclass alloc] initWithFrame:frame];

        cell.accessoryView = styleView;
    }

    ...
    ((WKWebViewSubclass *)cell.accessoryView).style = [[RenderStyle alloc] initWithString:styleString];

    return cell;
}

它的运行速度似乎比UIWebView稍慢,这很奇怪,但至少它没有错误,没有任何狡猾的工作。