Make a UIWebView as height as its content with Auto Layout
To make this work you have to do the following steps:
- Connect the
UIWebView
from the nib to an outlet in your view controller - Disable scrolling in the web view
- Set the constraints on the
UIScrollView
, theUIView
on top of the web view (In my example I omitted all the labels in that view) and theUIWebView
. - Connect the UIWebView's
height
constraint to an outlet in your view controller. - Set the view controller as
UIWebViewDelegate
- In
webViewDidFinishLoad
set the height constraint's constant to the height of thecontentSize
of the scroll view inside the web view. - Start Key-Value Observing on the contentSize to change the height, when height of the web view has to change because segments of the webpage change their size without reloading the page (like accordeons, or menus).
I won't explain the constraints in detail as you seem to already have figured them out yourself. Here is a screenshot of the constraints:
So, here is the code:
import UIKit
var MyObservationContext = 0
class ViewController: UIViewController {
@IBOutlet weak var webview: UIWebView!
@IBOutlet weak var webviewHeightConstraint: NSLayoutConstraint!
var observing = false
override func viewDidLoad() {
super.viewDidLoad()
webview.scrollView.scrollEnabled = false
webview.delegate = self
webview.loadRequest(NSURLRequest(URL: NSURL(string: "https://www.google.de/intl/de/policies/terms/regional.html")!))
}
deinit {
stopObservingHeight()
}
func startObservingHeight() {
let options = NSKeyValueObservingOptions([.New])
webview.scrollView.addObserver(self, forKeyPath: "contentSize", options: options, context: &MyObservationContext)
observing = true;
}
func stopObservingHeight() {
webview.scrollView.removeObserver(self, forKeyPath: "contentSize", context: &MyObservationContext)
observing = false
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
guard let keyPath = keyPath else {
super.observeValueForKeyPath(nil, ofObject: object, change: change, context: context)
return
}
switch (keyPath, context) {
case("contentSize", &MyObservationContext):
webviewHeightConstraint.constant = webview.scrollView.contentSize.height
default:
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
}
extension ViewController: UIWebViewDelegate {
func webViewDidFinishLoad(webView: UIWebView) {
print(webView.request?.URL)
webviewHeightConstraint.constant = webview.scrollView.contentSize.height
if (!observing) {
startObservingHeight()
}
}
}
Great solution from @joern. Works fine for me. I just adapted his code to swift 3.
Swift 3 Update Code :
I also modify stopObservingHeight to prevent remove observer before it was create.
import UIKit
var MyObservationContext = 0
class ViewController: UIViewController {
@IBOutlet weak var webview: UIWebView!
@IBOutlet weak var webviewHeightConstraint: NSLayoutConstraint!
var observing = false
override func viewDidLoad() {
super.viewDidLoad()
webview.scrollView.isScrollEnabled = false
webview.delegate = self
webview.loadRequest(NSURLRequest(URL: NSURL(string: "https://www.google.de/intl/de/policies/terms/regional.html")!))
}
deinit {
stopObservingHeight()
}
func startObservingHeight() {
let options = NSKeyValueObservingOptions([.new])
webview.scrollView.addObserver(self, forKeyPath: "contentSize", options: options, context: &MyObservationContext)
observing = true;
}
func stopObservingHeight() {
if observing {
webView.scrollView.removeObserver(self, forKeyPath: "contentSize", context: &MyObservationContext)
observing = false
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let keyPath = keyPath,
let context = context else {
super.observeValue(forKeyPath: nil, of: object, change: change, context: nil)
return
}
switch (keyPath, context) {
case("contentSize", &MyObservationContext):
webviewHeightConstraint.constant = webview.scrollView.contentSize.height
default:
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
}
extension ViewController: UIWebViewDelegate {
func webViewDidFinishLoad(_ webView: UIWebView) {
print(webView.request?.url ?? "nil")
webviewHeightConstraint.constant = webview.scrollView.contentSize.height
if (!observing) {
startObservingHeight()
}
}
}