Thomas Martinez
Thomas Martinez

Reputation: 1711

UIWebView dynamic content size

I've been looking around and wasnt able to see any swift related ways to do this. I'm trying to get my UIWebViews height to be dynamic. I have a UIWebView that loads data using the loadHtmlString function.The thing is that I am loading the data from an sqlite database, each time I load a different string with different length and naturally the web view obtains different height.
Now I need to know how to make the UIWebView that exact height in order to load my next content right under the webView. This is what I have so far

var jobSkillView = UIWebView(frame: CGRectMake(-5, 480.0, screenWidth, 300.0))
jobSkillView.loadHTMLString("<html><body p style='font-family:arial;font-size:16px;'>" + jobSkills + "</body></html>", baseURL: nil)
jobSkillView.stringByEvaluatingJavaScriptFromString("document.body.innerHTML")
jobSkillView.scrollView.scrollEnabled = true
jobSkillView.scrollView.bounces = true
jobSkillView.sizeToFit()
border.addSubview(jobSkillView)

I found something like this on SO but not sure how to link it to the UIWebView's frame:

func webViewDidFinishLoad(jobSkillView : UIWebView){
    // Change the height dynamically of the UIWebView to match the html content
    var jobSkillViewFrame: CGRect = jobSkillView.frame
    jobSkillViewFrame.size.height = 1
    jobSkillView.frame = jobSkillViewFrame
    var fittingSize: CGSize = (jobSkillView.sizeThatFits(CGSizeZero))
    jobSkillViewFrame.size = fittingSize
    // webViewFrame.size.width = 276; Making sure that the webView doesn't get wider than 276 px
    jobSkillView.frame = jobSkillViewFrame
    var jobSkillViewHeight = jobSkillView.frame.size.height
}

Upvotes: 23

Views: 71111

Answers (10)

Jalil
Jalil

Reputation: 1246

Try with

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        webView.frame.size.height = 1
        webView.frame.size = webView.sizeThatFits(.zero)
        webView.scrollView.isScrollEnabled = false
        let height = webView.scrollView.contentSize.height
}

Upvotes: 0

blyscuit
blyscuit

Reputation: 684

I'm using HTML string in my load and have to use the following method in Webview delegate:

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    webView.evaluateJavaScript("document.documentElement.scrollHeight", completionHandler: { (height, error) in
        self.webviewHeightConstraint?.constant = height as! CGFloat
    })
}

and the full code:

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate {

@IBOutlet weak var webviewHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var webview: WKWebView!
var observing = false

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    webview.navigationDelegate = self
    webview.scrollView.isScrollEnabled = false

        self.webview.loadHTMLString("""
        <html>
        <head>
        <style>
        ul {
        list-style: none;
        }

        ul li::before {
        content: "\\2022";
        color: red;
        font-weight: bold;
        display: inline-block;
        width: 1em;
        margin-left: -1em;
        }
        </style>
        </head>
        <body>

        <h2>Change Bullet Color of List Items</h2>

        <ul>
        <li>Adele</li>
        <li>Agnes</li>
        <li>Billy</li>
        <li>Bob</li>
        <li>Bob</li>
        <li>Bob</li>
        <li>Bob</li>
        <li>Bob</li>
        <li>Bob</li>
        <li>Bob</li>
        <li>Bob</li>
        <li>Bob</li>
        <li>Bob</li>
        <li>Bob</li>
        </ul>

        </body>
        </html>
""", baseURL: nil)
}

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    webView.evaluateJavaScript("document.documentElement.scrollHeight", completionHandler: { (height, error) in
        self.webviewHeightConstraint?.constant = height as! CGFloat
    })
}

}

Upvotes: 6

LinusG.
LinusG.

Reputation: 28912

This post has been updated for Swift 5 & WKWebView


So this is a really great function you wrote there, OP!
Here is just a shorter, more elegant version of your code:

// make sure to declare the delegate when creating your webView (add UIWebViewDelegate to class declaration as well)
myWebView.delegate = self

func webViewDidFinishLoad(webView: UIWebView) {
     webView.frame.size.height = 1
     webView.frame.size = webView.sizeThatFits(CGSize.zero)
}

Migrating to WKWebView

1) import WebKit
2) make your ViewController inherit from WKNavigationDelegate
3) hook up the WKWebView’s delegate: webView.navigationDelegate = self
4) implement the following protocol function:

webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)

After migrating from UIWebView to WKWebView, above approach doesn’t seem to work anymore.
What you can do instead, is change the line with webView.sizeThatFits(CGSize.zero) to:

webView.frame.size = webView.scrollView.contentSize

The full code for WKWebView would then be:

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    webView.frame.size.height = 1
    webView.frame.size = webView.scrollView.contentSize
}

Upvotes: 51

nja
nja

Reputation: 278

I had problem with:

let height = webView.scrollView.contentSize.height

Sometimes my web view height simply didn't calculacte and stayed on default value. So finally I manage to find better solution that works fine, just replace that line code with:

let height = webView.stringByEvaluatingJavaScript(from: "document.body.scrollHeight")

This is mine final complete code:

    func webViewDidFinishLoad(_ webView: UIWebView) {

    //webview height
    webView.frame.size.height = 1
    webView.frame.size = webView.sizeThatFits(.zero)
    webView.scrollView.isScrollEnabled = false
    let height = webView.stringByEvaluatingJavaScript(from: "document.body.scrollHeight")
    if let height = height {
        if let heightInt = Int(height) {
            let heightFloat = Float(heightInt)

            webViewHeightConstraint.constant = CGFloat(heightFloat)
        }
    }
    webView.scalesPageToFit = true
}

Upvotes: 4

Ruchin Somal
Ruchin Somal

Reputation: 1103

you can use this class for makking webview size to fit for swift 3 and swift 4

//
//  RSWebView.swift
//  CustumWebViewDemo
//
//  Created by Ruchin Somal on 01/01/18.
//  Copyright © 2018 Ruchin Somal. All rights reserved.
//

import UIKit

class RSWebView: UIWebView, UIWebViewDelegate {

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        self.scrollView.isScrollEnabled = false
        self.scrollView.bounces = false
        self.delegate = self
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.scrollView.isScrollEnabled = false
        self.scrollView.bounces = false
        self.delegate = self
    }

    override func loadHTMLString(_ string: String?, baseURL: URL?) {
        let s:String = "<html><head><title></title><meta name=\"viewport\" content=\"initial-scale=1, user-scalable=no, width=device-width\" /><link rel=\"stylesheet\" href=\"./webview.css\" ></link></head><body>"+string!+"</body></html>";
        var url:URL

        if baseURL == nil {
            url = Bundle.main.bundleURL
        } else {
            url = baseURL!
        }

        super.loadHTMLString(s, baseURL: url)
    }

    func webViewDidFinishLoad(_ webView: UIWebView) {
        self.webViewResizeToContent(webView: webView)
    }

    func webViewResizeToContent(webView: UIWebView) {
        webView.layoutSubviews()

        // Set to initial value of webview
        var frame:CGRect = webView.frame
        frame.size.height = 1.0
        webView.frame = frame

        // Calculated height of webview
        let wvheight: CGFloat = webView.scrollView.contentSize.height
        print("UIWebView.height: \(wvheight)")
        var wvRect = webView.frame
        wvRect.size.height = wvheight
        webView.frame = wvRect

        let heightConstraint = NSLayoutConstraint(item: webView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.height, multiplier: 1.0, constant: wvheight)
        webView.addConstraint(heightConstraint)

        webView.window?.setNeedsUpdateConstraints()
        webView.window?.setNeedsLayout()
    }

}

Upvotes: 1

Vidhi Patel
Vidhi Patel

Reputation: 641

When font change at that time change Webview height to 1 than get proper offsetHeight for Webview

webView1.frame.size.height = 1

Upvotes: 0

Gulz
Gulz

Reputation: 1813

this only worked for me

func webViewDidFinishLoad(_ webView: UIWebView) {
    webView.frame.size.height = 1
    webView.frame.size = webView.sizeThatFits(.zero)
    webView.scrollView.isScrollEnabled=false;
    myWebViewHeightConstraint.constant = webView.scrollView.contentSize.height
    webView.scalesPageToFit = true
}

make sure you've created an outlet for myWebViewHeightConstraint

Upvotes: 20

Teja Kumar Bethina
Teja Kumar Bethina

Reputation: 3706

Use the following code to make your UIWebView height dynamic in Swift 3.x

func webViewDidFinishLoad(_ webView: UIWebView) {        

 let height = webView.scrollView.contentSize.height
 var wRect = webView.frame
 wRect.size.height = height
 webView.frame = wRect

}

Upvotes: 1

Lukas Aumair
Lukas Aumair

Reputation: 61

Here is my custom class to work with custom UIWebViews, it has everything in it to get the correct scrollview content height, set UIWebView height and create a custom height constraint to get autolayout working. It also loads some custom CSS styling...

class CustomUIWebView: UIWebView, UIWebViewDelegate {

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.scrollView.scrollEnabled = false
        self.scrollView.bounces = false
        self.delegate = self
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.scrollView.scrollEnabled = false
        self.scrollView.bounces = false
        self.delegate = self
    }

    override func loadHTMLString(string: String!, baseURL: NSURL!) {
        var cssURL:String = NSBundle.mainBundle().pathForResource("webview", ofType: "css")!
        var s:String = "<html><head><title></title><meta name=\"viewport\" content=\"initial-scale=1, user-scalable=no, width=device-width\" /><link rel=\"stylesheet\" href=\"./webview.css\" ></link></head><body>"+string+"</body></html>";
        var url:NSURL

        if baseURL == nil {
            url = NSBundle.mainBundle().bundleURL
        } else {
            url = baseURL
        }

        super.loadHTMLString(s, baseURL: url)
    }

    func webViewDidFinishLoad(webView: UIWebView) {
        self.webViewResizeToContent(webView)
    }

    func webViewResizeToContent(webView: UIWebView) {
        webView.layoutSubviews()

        // Set to smallest rect value
        var frame:CGRect = webView.frame
        frame.size.height = 1.0
        webView.frame = frame

        var height:CGFloat = webView.scrollView.contentSize.height
        println("UIWebView.height: \(height)")

        webView.setHeight(height: height)
        let heightConstraint = NSLayoutConstraint(item: webView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.Height, multiplier: 1.0, constant: height)
        webView.addConstraint(heightConstraint)

        // Set layout flag
        webView.window?.setNeedsUpdateConstraints()
        webView.window?.setNeedsLayout()
    }

}

Upvotes: 6

Thomas Martinez
Thomas Martinez

Reputation: 1711

Okay I figured out what I was doing wrong so now I know how to call this function. First of all I was supposed to call a UIWebViewDelegate in my class. Then set my var jobSkillView to (jobSkillView.delegate = self). Now this is where I made my major mistake. In my code I had a if else statement that I was throwing my func webViewDidLoad in. I was supposed to put it out of that statement and into the actual class to override my frame for the UIWebView. Speaking of frame **** DONT FORGET TO SET YOUR FRAME HEIGHT TO 1. Then and only then this function will work for your UIWebView. After that you should have a fully functional based height for your UIWebView for Swift.

Upvotes: 1

Related Questions