WKWebView UITableView单元格高度和Instagram嵌入代码

时间:2017-07-06 12:32:05

标签: ios objective-c uitableview instagram wkwebview

我试图在桌面视图中将Instagram帖子添加到单元格中,但是尽管尝试了很多不同的东西,但我无法正确计算单元格高度。我为每个单元格创建了一个WKWebView,并使用Instagram提供的嵌入代码loadHTMLString,对Instagram的HTML进行了一些修改:

`<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"></head>`

当我在模拟器中运行它时,Instagram帖子似乎是正确的大小大约半秒钟,然后它被调整大小。

以下是一些基本代码,用于演示运行时的问题,单个硬编码的Instagram帖子显示在两个不同的单元格中,例如用途。如果我将loadRequestNSURLRequest与随机网站而不是loadHTMLString一起使用,则单元格大小合适,并且一切都按预期工作。如何在UITableViewCells中使用Instagram嵌入代码,以显示整个帖子?

InstagramViewController.h

#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>

@interface InstagramViewController : UIViewController <WKNavigationDelegate, WKUIDelegate, UITableViewDelegate, UITableViewDataSource>
@property (strong, nonatomic) IBOutlet WKWebView *webView;
@property (strong, nonatomic) IBOutlet UITableView *tableView;

@end

InstagramViewController.m

#import "InstagramViewController.h"

@interface InstagramViewController ()
@property (nonatomic) BOOL loaded;
@property (nonatomic) CGFloat cellHeight;

@end

@implementation InstagramViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.tableView.estimatedRowHeight = 150;
    self.tableView.rowHeight = UITableViewAutomaticDimension;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 2;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *simpleTableIdentifier = @"simpleTableIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
    }
    WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
    WKWebView *webView = [[WKWebView alloc] initWithFrame:cell.contentView.frame configuration:theConfiguration];
    webView.navigationDelegate = self;
    [webView.scrollView setScrollEnabled:NO];
    webView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
    webView.tag = indexPath.section;
    NSString *instagramEmbedHTML = @"\
    <!DOCTYPE html>\
    <html>\
    <head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\
    </head>\
    <body>\
    <blockquote class=\"instagram-media\" data-instgrm-captioned data-instgrm-version=\"7\" style=\" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);\">\
    <div style=\"padding:8px;\">\
    <div style=\" background:#F8F8F8; line-height:0; margin-top:40px; padding:41.91489361702128% 0; text-align:center; width:100%;\">\
    <div style=\" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;\">\
    </div>\
    </div>\
    <p style=\" margin:8px 0 0 0; padding:0 4px;\">\
    <a href=\"https://www.instagram.com/p/BVR2uajF1Qc/\" style=\" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;\" target=\"_blank\">I wish for... free cupcakes!!  First 50 customers at our #Gramercy location get a free #Summer Collection 3-Pack to celebrate 5 beautiful, magical years on 23rd St!  Today only! Open &#39;til 10pm  #happybirthday</a>\
    </p>\
    <p style=\" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;\">A post shared by Baked by Melissa (@bakedbymelissa) on <time style=\" font-family:Arial,sans-serif; font-size:14px; line-height:17px;\" datetime=\"2017-06-13T12:00:48+00:00\">Jun 13, 2017 at 5:00am PDT</time>\
    </p>\
    </div>\
    </blockquote>\
    <script async defer src=\"http://platform.instagram.com/en_US/embeds.js\"></script>\
    </body></html>";
    [webView loadHTMLString:instagramEmbedHTML baseURL:nil];
    [cell.contentView addSubview:webView];
    return cell;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return [self calculateHeightForCellAtIndexPath:indexPath];
}

-(CGFloat)calculateHeightForCellAtIndexPath:(NSIndexPath *)indexP{
    while (!_loaded) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }
    NSLog(@"new cell height: %f", _cellHeight);
    return _cellHeight;
}

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    NSLog(@"I finished");
    [webView evaluateJavaScript:@"document.body.scrollHeight;" completionHandler:^(NSString *result, NSError *error) {
        if (error != NULL) {
            NSLog(@"Error %@",error);
        }
        NSLog(@"new scrollHeight Result %@",result);
        float ht = [result floatValue];
        NSIndexPath* indexOfCell = [NSIndexPath indexPathForRow:0 inSection:webView.tag];
        UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexOfCell];
        cell.frame = CGRectMake(0, 0, cell.frame.size.width, ht);
        _cellHeight = ht;
        self.loaded = YES;
    }];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}
@end

这里是我运行上面代码时获得的输出的屏幕截图,其中您可以看到嵌入的大小超过计算单元格高度的大小,在这种情况下切断整个注释部分:< / p>

The author's text for each Instagram post is cut off because of the sizing issue

1 个答案:

答案 0 :(得分:0)

我已经为此苦苦挣扎很长时间了。此解决方案对我有用。

您将获得适当的高度。从那时起,您的任务就是处理它并设置容器的高度。

警告:这并不简单。

注意:Instagram和Twitter嵌入在这里真是可怕的动物。他们的脚本计算宽度ot 621(!)的高度,并将其设置为iframe的属性。因此,您“简单”(LOL)必须重新计算大小。请参阅新属性“ modded”,以避免多次设置高度。

没有跨域限制。您无法读取/更改从不同于baseURL-s域的网址加载的iframe内容。那太容易了,但是浏览器有一个很好的理由就拒绝了。

这是使用HTML,CSS,Javascript,当然还有Swift的混合解决方案。


func initMyWebView() {

//...

        let contentController = WKUserContentController()

        let config = WKWebViewConfiguration()
        config.preferences = preferences
        config.allowsInlineMediaPlayback = true
        config.userContentController = contentController
        config.mediaTypesRequiringUserActionForPlayback = []

        contentController.add(
            self,
            name: "heightChanged"
        )
        contentController.add(
            self,
            name: "log"
        )
        contentController.removeAllUserScripts()
        contentController.addUserScript(WKUserScript(source: windowSizeWatcherScript(), injectionTime: .atDocumentEnd, forMainFrameOnly: true))
// ...

}

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "log" {
            print("JSLog: " + String(describing: message.body))
            return
        }

        if message.name == "heightChanged" {
            if let messageDict = message.body as? Dictionary<String, CGFloat> {
                if nil != messageDict["height"] {
                    DispatchQueue.main.async { [weak self] in
                        // Implement the height update to your needs
                        self?.updateHeight(height: messageDict["height"]!)
                    }
                }
            }
            return
        }

    }


func style(jsonStyle: JSONTextStyleModel?) -> String {
        let maxHTMLW = bounds.width

        return """
        html, body {
            margin: 0 !important;
            padding: 0 !important;
            width: 100% !important;
            max-width: \(maxHTMLW)px !important;
            min-width: \(maxHTMLW)px !important;
        }
        body {
            font-family: Roboto;
            background-color: \(Theme.shared.generalColors.background);
            color: \((jsonStyle?.color).defaultsTo(value: "#333"));
            font-size: 16px;
            font-weight: 300;
        }

        #\(contentId) {
            margin: 0 2% !important;
            padding: 0 !important;
            height: 100%;
            display: block;
            width: 96%;
        }

        iframe {
            width: 100% !important;
        }

        iframe.instagram-media-rendered {
        }
        """
    }

    func online(html: String, with style: String) -> String {
        return """
        <!DOCTYPE html>
        <html>
            <head>
                <meta name="viewport" content="width=device-width, initial-scale=1">
                <style>
                \(style)
                </style>                
            </head>
            <body>
                <div id="\(contentId)">
                    \(html)
                </div>
            </body>
        </html>
        """
    }

 func windowSizeWatcherScript() -> String {
        let maxInstaW = bounds.width * 0.96
        return """
            var prevHeight = 0;
            var prevWidth = 0;
            var contentId = "\(contentId)";
            customLog("windowSizeWatcherScript added")

            var resizeTimeout = setTimeout(function () { resize(); }, 125);


            if (typeof(webkit) !== 'undefined' && typeof(webkit.messageHandlers.log) !== 'undefined') {
                console.log = customLog;
            }

            function resize() {
                clearTimeout(resizeTimeout);
                resizeTimeout = setTimeout('resize();', 250);

                if (typeof(webkit) == 'undefined' || typeof(webkit.messageHandlers.heightChanged) == 'undefined') {
                    customLog("Cannot send webkit messages");
                    return;
                }


                if (resizeInsta()) {
                    return
                }

                if (resizeTwitter()) {
                    return
                }

                if (resizeIframes()) {
                    return
                }

                if (resizeContent()) {
                    return
                }

                resizeDocument()

            }

            function customLog(message) {
                if (typeof(webkit) == "undefined" || typeof(webkit.messageHandlers.log) == "undefined") {
                    console.log("No native app context");
                    console.log(message)
                    return
                }
                try { webkit.messageHandlers.log.postMessage(message);} catch (err) { console.log("No native app context"); }
            }

            function handleSize(width, height) {
                if (height > 0 && prevHeight != height) {
                    customLog("W: " + width + " H: " + height);
                    try { webkit.messageHandlers.heightChanged.postMessage({"height": height});} catch (err) { customLog("No native app context"); }
                    customLog("Detected width " + width + " and height " + height);
                    prevHeight = height;
                }
            }

            function resizeInsta() {
                var insta = document.getElementsByClassName("instagram-media-rendered");

                if (typeof(insta) !== "undefined" && insta.length > 0) {
                    var width = insta[0].offsetWidth;
                    var height = Math.max(insta[0].scrollHeight, insta[0].offsetHeight);
                    var h = insta[0].getAttribute("height")
                    if (h !== "undefined" && h > 0) {
                        var modded = insta[0].getAttribute("modded")
                        if (modded == "undefined") {
                            h = h.replace("px", "")
                            let r = \(maxInstaW) / 621;
                            height = h * r;
                            width = \(Int(maxInstaW));
                            insta[0].setAttribute("height", height + "px");
                            insta[0].setAttribute("modded", "1");
                            insta[0].setAttribute("width", "\(Int(maxInstaW))px");
                        }
                    }
                    handleSize(width, height)
                    return true;
                }

                return false;
            }

            function resizeTwitter() {
                var insta = document.getElementsByClassName("twitter-tweet-rendered");

                if (typeof(insta) !== "undefined" && insta.length > 0) {
                    var width = insta[0].offsetWidth;
                    var height = Math.max(insta[0].scrollHeight, insta[0].offsetHeight);
                    var h = insta[0].getAttribute("height")
                    if (h !== "undefined" && h > 0) {
                        var modded = insta[0].getAttribute("modded")
                        if (modded == "undefined") {
                            h = h.replace("px", "")
                            let r = \(maxInstaW) / 621;
                            height = h * r;
                            width = \(Int(maxInstaW));
                            insta[0].setAttribute("height", height + "px");
                            insta[0].setAttribute("modded", "1");
                            insta[0].setAttribute("width", "\(Int(maxInstaW))px");
                        }
                    }
                    handleSize(width, height)
                    return true;
                }

                return false;
            }

            function resizeIframes() {
                var iframes = document.getElementsByTagName("iframe");

                if (typeof(iframes) !== "undefined" && iframes.length > 0) {
                    var width = iframes[0].offsetWidth;
                    var height = Math.max(iframes[0].scrollHeight, iframes[0].offsetHeight);
                    handleSize(width, height)
                    return true
                }
                return false
            }

            function resizeContent() {
                var content = document.getElementById(contentId);
                if (typeof(content) !== 'undefined') {
                        var width = content.offsetWidth;
                        var height = Math.max(content.scrollHeight, content.offsetHeight);
                        handleSize(width, height)
                        return true
                }
                return false
            }

        function resizeDocument() {
            var width = document.body.offsetWidth;
            var height = Math.max(document.body.scrollHeight, document.body.offsetHeight);
            handleSize(width, height)
            return true
        }
"""
    }