Get the height of a website requested with UIWebView - ios

I request a Website with this code
let requestObj = NSURLRequest(URL: url)
myWebView.loadRequest(requestObj)
print (myWebView.scrollView.contentSize.height) //1
print (myWebView.frame.size.height) //2
The code always return 1000.0 when the real size of the website is much more than that. Is there a way to get the real size ? I want to show the hole content of a WebView without the need of scrolling within the WebView.

You need to use the webViewDidFinishLoad delegate method, this answer was originally founded here How to determine the content size of a UIWebView?
In your viewDidLoad method do something like this
override func viewDidLoad() {
super.viewDidLoad()
self.webView.loadHTMLString(component.html, baseURL: nil)
self.webView.scrollView.isScrollEnabled = false
self.webView.scrollView.bounces = false
self.webView.delegate = self
self.delegate = delegate
}
public func webViewDidFinishLoad(_ webView: UIWebView)
{
var frame = webView.frame
frame.size.height = 1
webView.frame = frame
let fittingSize = webView.sizeThatFits(CGSize(width: 0, height: 0))
frame.size = fittingSize
webView.frame = frame
webView.scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
self.delegate?.heightHasChanged(indexPath:self.indexPath, newHeight: frame.height)
self.loadingView.isHidden = true
self.loadingActivityIndicator.stopAnimating()
}
I hope this helps you, best regards

Related

Issue with Scroll View. Unexpectedly found nil while unwrapping optional value

I have a UIScrollView, and am using a UIPageControl to present images. The moment the first image appears, the program crashes and indicates a nil value was found.
EDIT: I'm no longer downloading images for the Scroll View, yet I'm still getting the error. The image appears and then the program crashes. Here is the stripped down code. Totally at a loss at this point. FYI, the reason I declare the variable i is I intend to loop and add additional images:
import UIKit
class HistoryViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
var frame = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 0, height: 0))
override func viewDidLoad() {
super.viewDidLoad()
self.scrollView.isPagingEnabled = true
let i = 0
frame.origin.x = self.scrollView.frame.size.width * CGFloat(i)
frame.size = self.scrollView.frame.size
let subView = UIImageView(frame: frame)
subView.image = UIImage(named: "maudite")
subView.contentMode = .scaleAspectFit
self.scrollView.addSubview(subView)
self.scrollView.contentSize = CGSize(width: self.scrollView.frame.size.width * CGFloat(1), height: self.scrollView.frame.size.height)
}
}
.....................
I have two theories why this is happening. The first is that The images for the subView that I add to the ScrollView haven't loaded yet. I'm using AlamoFireImage, which downloads images asynchronously.
This is the code that configures the Scroll View:
func configureScrollView() {
self.myScrollView.isPagingEnabled = true
for index in 0..<imageURLS.count {
frame.origin.x = self.myScrollView.frame.size.width * CGFloat(index)
frame.size = self.myScrollView.frame.size
let subView = UIImageView(frame: frame)
if let url = URL(string: imageURLS[index]) {
subView.af_setImage(withURL: (url))
subView.contentMode = .scaleAspectFit
self.myScrollView.addSubview(subView)
}
}
self.myScrollView.contentSize = CGSize(width: self.myScrollView.frame.size.width * CGFloat(imageURLS.count), height: self.myScrollView.frame.size.height)
pageControl.numberOfPages = imageURLS.count
}
The other thing that might be the cause of the issue is that I first have to download the image URL's. I do this and store them in an array. When that is complete, I call the configureScrollView(). I do the call on the Main Thread. Does the function then run on the Main Thread?
for dict in responseArray {
if let url = dict["fileName"] as? String {
let fullURL = "http://www.smarttapp.com" + url
print(fullURL)
self.imageURLS.append(fullURL)
}
}
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
self.activityIndicator.isHidden = true
self.networkingState = .finishedSearching
self.configureScrollView()
}
}

UIWebview remove padding / margin

I am loading a PDF into a UIWebview and I changed the background color to clear and set opaque to false. However now there is a padding or margin in my UIWebView that I would like to remove so the UIWebview is the screen.
I created a UIWebview like so:
let webview = UIWebView()
webview.frame = self.view.bounds
webview.scrollView.frame = webview.frame
webview.userInteractionEnabled = true
webview.scalesPageToFit = true
webview.becomeFirstResponder()
webview.delegate = self
webview.scrollView.delegate = self
webview.opaque = false
webview.backgroundColor = UIColor.clearColor()
self.view.addSubview(webview)
webview.loadRequest(NSURLRequest(URL:url))
webview.gestureRecognizers = [pinchRecognizer, panRecognizer]
and I applied this to the webViewDidFinishLoad method
func webViewDidFinishLoad(webView: UIWebView) {
let padding = "document.body.style.margin='0';document.body.style.padding = '0'"
webView.stringByEvaluatingJavaScriptFromString(padding)
}
but there is still a padding or margin, it looks like this:
How do I fix this?
The Reason why I need this fixed is because I am going to be saving this UIWebView as a PDF and when I save it, that extra spacing is there also, here is my PDF generate code:
func drawPDFUsingPrintPageRenderer(printPageRenderer: UIPrintPageRenderer) -> NSData! {
let data = NSMutableData()
UIGraphicsBeginPDFContextToData(data, CGRectZero, nil)
UIGraphicsBeginPDFPage()
printPageRenderer.drawPageAtIndex(0, inRect: UIGraphicsGetPDFContextBounds())
UIGraphicsEndPDFContext()
return data
}
and more
let screenHeight = appDelegate.webview!.scrollView.bounds.size.height
let heightStr = appDelegate.webview!.scrollView.bounds.size.height
let height = heightStr
let pages = ceil(height / screenHeight)
let pdfMutableData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfMutableData, appDelegate.webview!.scrollView.bounds, nil)
for i in 0..<Int(pages)
{
if (CGFloat((i+1)) * screenHeight > CGFloat(height)) {
var f = appDelegate.webview!.scrollView.frame
f.size.height -= ((CGFloat((i+1)) * screenHeight) - CGFloat(height));
appDelegate.webview!.scrollView.frame = f
}
UIGraphicsBeginPDFPage()
let currentContext = UIGraphicsGetCurrentContext()
appDelegate.webview!.scrollView.setContentOffset(CGPointMake(0, screenHeight * CGFloat(i)), animated: false)
appDelegate.webview!.scrollView.layer.renderInContext(currentContext!)
}
UIGraphicsEndPDFContext()
Setting the scroll view frame to the web view frame will have no effect while the web view is at the origin and an unwanted effect otherwise.
Adjust the scroll view contentInset:
// set t,l,b,r to be negative CGFloat values that suit your content
webView.scrollView.contentInset = UIEdgeInsetsMake(t,l,b,r);
This appears to fix my issue, not sure how well it work with other PDF documents (Portrait) mine is Landscape and it works fine.
appDelegate.webview = UIWebView()
appDelegate.webview!.frame = CGRect(x: 0 - 8, y: 0 - 8, width: self.view.bounds.size.width + 14, height: self.view.bounds.size.height + 14)
appDelegate.webview!.scrollView.frame = CGRect(x: 0 - 8, y: 0 - 8, width: self.view.bounds.size.width + 14, height: self.view.bounds.size.height + 14)
and then for saving the PDF
func createPdfFromView(aView: UIView, saveToDocumentsWithFileName fileName: String)
{
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, CGRect(x: 0 + 8, y: 0 - 8, width: aView.bounds.size.width - 17, height: aView.bounds.size.height - 117), nil)
UIGraphicsBeginPDFPage()
guard let pdfContext = UIGraphicsGetCurrentContext() else { return }
aView.layer.renderInContext(pdfContext)
UIGraphicsEndPDFContext()
if let documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first {
let documentsFileName = documentDirectories + "/" + fileName
debugPrint(documentsFileName)
pdfData.writeToFile(documentsFileName, atomically: true)
}
}
Please let me know if I am doing anything wrong
It worked for me to apply negative content insets. This works on iOS 11 and 10, on Swift 4:
webView.scrollView.contentInset = UIEdgeInsets(top: -8, left: -8, bottom: -8, right: -8)
I am not sure if you have found the answer but as a quick work around you can give pre adjusted constraint .
lets say if yours padding space is 10px from all 4 sides and if its always a constant
the work around is :
Give -10 space for the webView around it , also make don't forget to add a superview to your webView without the -10px side constraints and mark the superview
clipsToBounds = true
clipSubViews = true
This example gives a top spacing of 64 to allow for a nav bar.
let webView: UIWebView = UIWebView()
webView.frame = self.view.bounds
webView.scrollView.frame = webView.frame
webView.scrollView.contentInset = UIEdgeInsetsMake(64,0,0,0)
I don't know why I come here so many times but google never sends me through to the actually answered question. For anyone with the same problem here is the actual answer: https://stackoverflow.com/a/2442859/3393964
The problem is that browsers add a default margin, a simple fix is adding this:
body { margin: 0; padding: 0; }

Swift - Add UIImageView as subview of UIWebView scrollView and scaling

I have a UIWebView and I have successfully added a UIImage view to the UIWebView’s scrollView like so:
let localUrl = String(format:"%#/%#", PDFFilePath, fileNameGroup)
let url = NSURL.fileURLWithPath(localUrl)
panRecognizer = UITapGestureRecognizer(target: self, action: #selector(panDetected))
pinchRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(pinchDetected))
panRecognizer.delegate = self
pinchRecognizer.delegate = self
webview = UIWebView()
webview.frame = self.view.bounds
webview.scrollView.frame = webview.frame
webview.userInteractionEnabled = true
webview.scalesPageToFit = true
webview.becomeFirstResponder()
webview.delegate = self
webview.scrollView.delegate = self
self.view.addSubview(webview)
webview.loadRequest(NSURLRequest(URL:url))
webview.gestureRecognizers = [pinchRecognizer, panRecognizer]
let stampView:StampAnnotation = StampAnnotation(imageIcon: UIImage(named: "approved.png"), location: CGPointMake(currentPoint.x, currentPoint.y))
self.webview.scrollView.addSubview(stampView)
My UIWebView scrollView is scalable. Now I am looking for away to have my UIImageView (StampAnnotation is a class and UIImageView is its subclass) scale when the scrollView scales. So if the user zooms in on the scrollView, the UIImageView will get bigger and stay in a fixed position and if the user zooms out, the UIImageView will get smaller while the scrollView gets smaller while staying in a fixed position.
I really hope that makes sense. I have tried the following:
func pinchDetected(recognizer:UIPinchGestureRecognizer)
{
for views in webview.scrollView.subviews
{
if(views.isKindOfClass(UIImageView))
{
views.transform = CGAffineTransformScale(views.transform, recognizer.scale, recognizer.scale)
recognizer.scale = 1
}
}
if(appDelegate.annotationSelected == 0)
{
webview.scalesPageToFit = true
}
else
{
webview.scalesPageToFit = false
}
}
but this does nothing, if I remove this line:
recognizer.scale = 1
it scales way too big too fast. My question is, how do I get my UIImageView to scale when the UIWebview’s scrollView scrolls?
Any help would be appreciated.
This solved my problem.
func scrollViewDidZoom(scrollView: UIScrollView) {
for views in webview.scrollView.subviews
{
if(views.isKindOfClass(UIImageView))
{
views.transform = CGAffineTransformMakeScale(scrollView.zoomScale, scrollView.zoomScale)
}
}
}
No it does not stay in a fixed position on the page, but I think that is a constraints issue?
You were close...
1) Add a property to hold onto an external reference for your stampViewFrame:
var stampViewFrame = CGRect(x: 100, y: 100, width: 100, height: 100)
2) Replace your scrollViewDidZoom() with this:
func scrollViewDidZoom(scrollView: UIScrollView) {
for views in webView.scrollView.subviews
{
if(views.isKindOfClass(UIImageView))
{
views.frame = CGRect(x: stampViewFrame.origin.x * scrollView.zoomScale, y: stampViewFrame.origin.y * scrollView.zoomScale, width: stampViewFrame.width * scrollView.zoomScale, height: stampViewFrame.height * scrollView.zoomScale)
}
}
}
3) Finally, because the zoom scale resets to 1 at the begining of each new zooming action, you need to adjust the value of your stampViewFrame property:
func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView?, atScale scale: CGFloat) {
stampViewFrame = CGRect(x: stampViewFrame.origin.x * scale, y: stampViewFrame.origin.y * scale, width: stampViewFrame.width * scale, height: stampViewFrame.height * scale)
}
I also tried to answer your other question about layout during orientation change, but I now have a much better understanding of what you are trying to do. If you want your stampView to always be on in the same place relative to the web content, you have to get into HTML/JS because the webpage lays itself out dynamically. A much much more simple (and hopefully close enough) solution would be to add the following:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
webView.frame = view.bounds
stampView.frame = stampViewFrame
}
Use the scroll delegate method of scrollViewDidZoom :
func scrollViewDidZoom(scrollView: UIScrollView){
//Change the subview of scroll frame as per the scroll frame scale
//rect = initial position & size of the image.<class instance>
stampView.frame = CGRectMake((CGRectGetMaxX(rect)-rect.size.width)*webView.scrollView.zoomScale, (CGRectGetMaxY(rect)-rect.size.height)*webView.scrollView.zoomScale, rect.width*webView.scrollView.zoomScale,rect.height*webView.scrollView.zoomScale)
}

UIWebView sizeToFit() Smaller

It appears that using sizeToFit() on a UIWebView does not change the size to be smaller, only larger. This is some set-up code:
var scrollView: UIScrollView!
var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
scrollView = UIScrollView()
scrollView.frame = CGRect(x: 50.0, y: 50.0, width: 800.0, height: 1200.0)
webView = UIWebView()
webView.frame = CGRect(x: 50.0, y: 50.0, width: 800.0, height: 1.0)
webView.scrollView.scrollEnabled = false
webView.delegate = self
webView.scalesPageToFit = false
webView.loadHTMLString("Test", baseURL: nil)
self.view.addSubview(scrollView)
scrollView.addSubview(webView)
}
This is my test code. It changes the size of the content to be VERY LARGE after 3 seconds, then VERY SMALL after another 3 seconds:
dispatch_after_delay_main(3) {
self.webView.loadHTMLString("Test Test Test Test ", baseURL: nil)
// Much longer string abbreviated here
dispatch_after_delay_main(3, block: {
self.webView.loadHTMLString("Test", baseURL: nil)
})
}
This is called after the HTML is loaded. The goal is to change the size of the webView based on the size of its content. Note: I've done this with both .sizeToFit() and sizeThatFits() with the same size result.
func webViewDidFinishLoad(webView: UIWebView) {
webView.frame.size.height = 0.0 // Just in case
webView.sizeToFit()
}
I've omitted the print statements at the end of webViewDidFinishLoad(webView: UIWebView) but they output the size growing, but not decreasing. In other words, it will grow, but then once "Test" is loaded, it does not decrease in size.
(800.0, 1.0) // "Test" (before sizeToFit() called)
(800.0, 1976.0) // "Test Test .... Test "
(800.0, 1976.0) // "Test"
do you need the webview embedded on a scrollView? I think not.
Try to change your HTML string to "<div id='main-html'>Your text</div>" and then on the webViewDidFinishLoad delegate function you will do these things:
First, you get the content height with:
var result : Float = NSString (webView.stringByEvaluatingJavaScriptFromString("document.getElementById('main-html').offsetHeight").floatValue
Second, change the webview height with the result value.
And tries to change the height of webView.scrollView.contentSize using the same result value.
Finally, use the webView.sizeToFit()
Cheers
EDIT:
As you said, you can use:
func webViewDidFinishLoad(webView: UIWebView) {
webView.frame.size.height = 1.0 // Value needs to be > 0.0 and < of the current height
webView.sizeToFit()
}

How to disabled uiwebview auto scroll to top when finished load

I added same subviews in the UIWebView's scrollview.When the webview get contentSize the scrollview will scroll to top.Now I'know the event happend before finished(some images may still loading) but after get the text content.What should i do?
This is the code,and to see the bug you need slow down the request(use edge)!! Before load finished scroll the view,then wait.When the request finished you will see the view scroll to top .
class WebViewController: UIViewController {
var mw = UIScreen.mainScreen().bounds.width
var mh = UIScreen.mainScreen().bounds.height
override func viewDidLoad() {
super.viewDidLoad()
var web = UIWebView(frame: CGRect(x: 0, y: 0, width: mw, height: mh))
var scrollview = web.scrollView
var tmp = UIView(frame: CGRect(x: 0, y: 0, width: mw, height: 300))
tmp.backgroundColor = UIColor.redColor()
scrollview.addSubview(tmp)
(scrollview.subviews.first as UIView).frame.origin.y = 300
let requestURL = NSURL(string: "http://stackoverflow.com/questions/27008545/how-to-disabled-uiwebview-auto-scroll-to-top-when-finished-load")
let request = NSURLRequest(URL: requestURL!)
web.loadRequest(request)
self.view.addSubview(web)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
first you need to set the setContentSize: to the actual content size, not the frame size. That is if your content is e.g. 800px high, you should setContentSize:CGSizeMake(width, 800). using this you can set a UIScrollView that it should scroll to show 800px high content. If you set a UIScrollView's content size equal to its frame, no scrolling is performed and no scroll indicators appear .

Resources