I am rendering website using WkWebView. The problem I am facing is that as shown in the screenshot, if I scroll to the bottom, part of the page (last 2 rows) is not visible. If I drag scrollbar to the bottom, it shows that part of the page and automatically scrolls upwards and that part becomes no longer visible to the user. Do I need to update my webview constraints or change the height of the webview?
Here is the screenshot
here is the code snapshot
let webConfiguration = WKWebViewConfiguration()
let customFrame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: 0.0, height: self.webViewContainer.frame.size.height))
self.webView = WKWebView (frame: customFrame , configuration: webConfiguration)
webView.translatesAutoresizingMaskIntoConstraints = false
self.webViewContainer.addSubview(webView)
addConstraints(to: webView, with: webViewContainer)
func addConstraints(to webView: UIView, with superView: UIView) {
webView.translatesAutoresizingMaskIntoConstraints = false
let leadingConstraint = NSLayoutConstraint(item: webView, attribute: .leading, relatedBy: .equal, toItem: superView, attribute: .leading, multiplier: 1, constant: 0)
let trailingConstraint = NSLayoutConstraint(item: webView, attribute: .trailing, relatedBy: .equal, toItem: superView, attribute: .trailing, multiplier: 1, constant: 0)
let topConstraint = NSLayoutConstraint(item: webView, attribute: .top, relatedBy: .equal, toItem: superView, attribute: .top, multiplier: 1, constant: 0)
let bottomConstraint = NSLayoutConstraint(item: webView, attribute: .bottom, relatedBy: .equal, toItem: superView, attribute: .bottom, multiplier: 1, constant: 0)
superView.addConstraints([leadingConstraint, trailingConstraint, topConstraint, bottomConstraint])
}
Any help will be appreciated.
I have noticed this happening before.
I'd recommend
webView.scrollView.isScrollEnabled = false
Webpage is supposed to find out the viewport area and set its width and height using CSS or JS.
Most UI frameworks have support for this by default
So when you scroll the scrolling happens within the webpage rather scrolling in the native view.
I have a view and I want to center it horizontally and vertically in its superview.
I tried this but it's not working:
let horConstraint = NSLayoutConstraint(item: webView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0.0)
view.addConstraints([horConstraint])
I would suggest trying to use Anchors which are more convenient and easy to understand. This code centered my view:
let someView = UIView()
someView.backgroundColor = .red
someView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(someView)
NSLayoutConstraint.activate([
someView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
someView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
someView.heightAnchor.constraint(equalToConstant: 50),
someView.widthAnchor.constraint(equalToConstant: 50)
])
Try this:
Note: Make sure that you are setting this property (before applying constraints) to false for webView
webView.translatesAutoresizingMaskIntoConstraints = false
For x axis
let horConstraint = NSLayoutConstraint(item: webView, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX ,multiplier: 1.0, constant: 0.0)
For y axis
let verConstraint = NSLayoutConstraint(item: webView, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY ,multiplier: 1.0, constant: 0.0)
view.addConstraints([horConstraint, verConstraint])
I'm trying to get my head around how adding constraints programmatically works. So far I have my code like so:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//addViewStandard()
addConstraintsView()
}
func addConstraintsView() {
let someView = UIView(frame: CGRect.zero)
someView.backgroundColor = UIColor.blue
// I want to mimic a frame set of CGRect(x: 20, y: 50, width: 50, height: 50)
let widthConstraint = NSLayoutConstraint(item: someView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let heightConstraint = NSLayoutConstraint(item: someView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let leadingConstraint = NSLayoutConstraint(item: someView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 20)
someView.translatesAutoresizingMaskIntoConstraints = false
someView.addConstraints([widthConstraint, heightConstraint, leadingConstraint])
view.addSubview(someView)
}
}
Now when I run the app it crashes because of the leading constraint. The error message is "Impossible to set up layout with view hierarchy unprepared for constraint". What am I doing wrong here? Should I be adding the constraints to the object (the blue box on this case) or adding them to its superview?
EDIT:
After code changes I have:
func addConstraintsView() {
let someView = UIView(frame: CGRect.zero)
someView.backgroundColor = UIColor.blue
view.addSubview(someView)
someView.translatesAutoresizingMaskIntoConstraints = false
let widthConstraint = NSLayoutConstraint(item: someView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let heightConstraint = NSLayoutConstraint(item: someView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let leadingConstraint = NSLayoutConstraint(item: someView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1.0, constant: 20)
someView.addConstraints([widthConstraint, heightConstraint])
view.addConstraints([leadingConstraint])
}
First of all,
view.addSubview(someView)
someView.translatesAutoresizingMaskIntoConstraints = false
should come before the constraints phase; you have to apply the constraints AFTER someView is added to its superview.
Also, if you are targeting iOS 9, I'd advise you to use layout anchors like
let widthConstraint = someView.widthAnchor.constraint(equalToConstant: 50.0)
let heightConstraint = someView.heightAnchor.constraint(equalToConstant: 50.0)
let leadingConstraint = someView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0)
NSLayoutConstraint.activate([widthConstraint, heightConstraint, leadingConstraint])
This way you don't have to worry about which view to apply the constraints to.
Finally (and to clear up your doubt), if you can't use layout anchors, you should add the leading constraint to the superview, not the view.
I added a view to the top of my collection view, now when I dismiss the view remains for a split second. Does anyone have any idea to what might cause this?
let newView = UIView() newView.backgroundColor = UIColor.redColor()
newView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(newView)
let pinTop = NSLayoutConstraint(item: newView, attribute: .Top, relatedBy: .Equal, toItem:
self.topLayoutGuide, attribute: .Bottom, multiplier: 1.0, constant: 0)
let heightConstraint = newView.heightAnchor.constraintEqualToAnchor(nil, constant: 50)
let widthConstraint = NSLayoutConstraint(item: newView, attribute: .Width, relatedBy: .Equal, toItem: view, attribute: .Width, multiplier: 1.0, constant: 0)
self.topContentAdditionalInset = 50
NSLayoutConstraint.activateConstraints([pinTop, heightConstraint, widthConstraint])
If you are dismissing it with the viewController, try to add newView .removeFromSuperview() in viewWillDissapear and re-adding it in viewWillAppear
I was just missing a constraint, stupid me!
let centerX = NSLayoutConstraint(item: newView, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0)
I have UIWebView and in its scrollView, I added a UIImageView like so:
self.webview.scrollView.addSubview(image)
my problem is when I rotate my device from portrait to landscape the UIImageView does not stay at the position I originally set it to on the scrollView, I understand the width and height of the screen change, I guess what I am trying to do it change the the position on my UIImageView so it appears it did not change.
I have this method in place:
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
var newScrollViewFrame = webview.scrollView.frame
var newFrame = webview.frame
newFrame.size.width = size.width
newFrame.size.height = size.height
newScrollViewFrame.size.width = size.width
newScrollViewFrame.size.height = size.height
webview.frame = newFrame
webview.scrollView.frame = newScrollViewFrame
}
The current code inside this method just resize the UIWebView and its scroll view, but not the UIImageViews in the scrollView
Any help would be appreciated.
Thanks,
I have tried the following:
for views in webview.scrollView.subviews
{
if(views.isKindOfClass(UIImageView))
{
views.transform = CGAffineTransformMakeRotation(CGFloat(M_PI)/2);
}
}
but this puts on UIImageView sideways on rotate
Here is how I am adding the webview to the view:
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)
I have half solved my problem, but doing the following:
webview.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = NSLayoutConstraint(item: webview, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0)
view.addConstraint(horizontalConstraint)
let verticalConstraint = NSLayoutConstraint(item: webview, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0)
view.addConstraint(verticalConstraint)
let widthConstraint = NSLayoutConstraint(item: webview, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 0.1, constant: 500)
view.addConstraint(widthConstraint)
let heightConstraint = NSLayoutConstraint(item: webview, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 500)
view.addConstraint(heightConstraint)
However now, the UIWebView is not full width or height :( Sooooo close.
I also tried this, but the UIImageView do not remain in the same spot.
let left = NSLayoutConstraint(item: self.webView, attribute: .Left, relatedBy: .Equal, toItem: self.view, attribute: .Left, multiplier: 1, constant: 0)
let right = NSLayoutConstraint(item: self.webView, attribute: .Right, relatedBy: .Equal, toItem: self.view, attribute: .Right, multiplier: 1, constant: 0)
let top = NSLayoutConstraint(item: self.webView, attribute: .Top, relatedBy: .Equal, toItem: self.view, attribute: .Top, multiplier: 1, constant: 0)
let bottom = NSLayoutConstraint(item: self.webView, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1, constant: 0)
NSLayoutConstraint.activateConstraints([top, bottom, left, right])
I have also tried adding constraints to the UIImageView
let stampView:StampAnnotation = StampAnnotation(imageIcon: UIImage(named: "approved.png"), location: CGPointMake(currentPoint.x, currentPoint.y))
self.webview.scrollView.addSubview(stampView)
let left = NSLayoutConstraint(item: stampView, attribute: .Left, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Left, multiplier: 1, constant: 0)
let right = NSLayoutConstraint(item: stampView, attribute: .Right, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Right, multiplier: 1, constant: 0)
let top = NSLayoutConstraint(item: stampView, attribute: .Top, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Top, multiplier: 1, constant: 0)
let bottom = NSLayoutConstraint(item: stampView, attribute: .Bottom, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Bottom, multiplier: 1, constant: 0)
NSLayoutConstraint.activateConstraints([top, bottom, left, right])
same result, UIImageView does not stay in the same spot.
UPDATE
My webview:
webview = UIWebView()
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]
webview.translatesAutoresizingMaskIntoConstraints = false
let left = NSLayoutConstraint(item: webview, attribute: .Left, relatedBy: .Equal, toItem: self.view, attribute: .Left, multiplier: 1, constant: 0)
let right = NSLayoutConstraint(item: webview, attribute: .Right, relatedBy: .Equal, toItem: self.view, attribute: .Right, multiplier: 1, constant: 0)
let top = NSLayoutConstraint(item: webview, attribute: .Top, relatedBy: .Equal, toItem: self.view, attribute: .Top, multiplier: 1, constant: 0)
let bottom = NSLayoutConstraint(item: webview, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1, constant: 0)
NSLayoutConstraint.activateConstraints([top, bottom, left, right])
UIImageView
stampView.translatesAutoresizingMaskIntoConstraints = false
let left = NSLayoutConstraint(item: stampView, attribute: .Left, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Left, multiplier: 1, constant: 100)
let right = NSLayoutConstraint(item: stampView, attribute: .Right, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Right, multiplier: 1, constant: 0)
let top = NSLayoutConstraint(item: stampView, attribute: .Top, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Top, multiplier: 1, constant: 100)
let bottom = NSLayoutConstraint(item: stampView, attribute: .Bottom, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Bottom, multiplier: 1, constant: 0)
let width = NSLayoutConstraint(item: stampView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 0.1, constant: 150)
let height = NSLayoutConstraint(item: stampView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 73)
NSLayoutConstraint.activateConstraints([left, right, top, bottom, width, height])
I just need to figure out the math to have this device be at the position of touch. (in percentages ?)
I think these things are always easier to do without using autolayout. To do this, I recomend using viewDidLayoutSubviews(). Here is my code:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
webView.frame = view.bounds
let screen = UIScreen.mainScreen().fixedCoordinateSpace
//These values will give a rect half the size of the screen and centered.
let width = screen.bounds.width / 2
let height = screen.bounds.height / 2
let x = (screen.bounds.width - width) / 2
let y = (screen.bounds.height - height) / 2
let absoluteRect = CGRect(x: x, y: y, width: width, height: height)
let stampRect = screen.convertRect(absoluteRect, toCoordinateSpace: webView)
stampView.frame = stampRect
//Change the orientation of the image
switch UIDevice.currentDevice().orientation {
case .LandscapeLeft:
stampView.image = UIImage(CGImage:originalImage.CGImage!, scale:originalImage.scale, orientation: UIImageOrientation.Left)
break
case .LandscapeRight:
stampView.image = UIImage(CGImage:originalImage.CGImage!, scale:originalImage.scale, orientation: UIImageOrientation.Right)
break
default:
stampView.image = UIImage(CGImage:originalImage.CGImage!, scale:originalImage.scale, orientation: UIImageOrientation.Up)
break
}
}
I am doing several things here...First I set the webViewFrame. Next, it is helpful here to define an absolute coordinate system relative to your screen. When the phone orientation changes, the values of screen will not change (allowing you to keep your imageView in the same place.) Next I define the desired frame for the stampView, then convert the absolute frame into its equivalent inside your scrollView (it's superView) and assign it to the stampView. Finally, if you want the image in your stampView to always be oriented correctly, you need to change the image orientation. CGAffineTransformMakeRotation only works if your view is square, but we can make it more general by actually changing the imageOrientation within the stampView. This requires a property for the original image:
let originalImage = UIImage(named: "image_name")!
Finally, because viewWillLayoutSubViews() is not called for landscape to landscape transitions, do the following:
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
view.setNeedsLayout()
}
This code doesn't make for the prettiest transitions, it should help with your layout problems.
To make the UIImageView keep the same size and stay in the same position relative to the UIWebView, you can set your views to be a percentage of their respective superView. Here I've created some mock views that illustrate the basic idea:
import UIKit
class ViewController: UIViewController {
//we create some views for testing and create references to the size and points we need for positioning
let webView = UIWebView()
let image = UIImageView()
let image2 = UIImageView()
var imageViewSize = CGSize()
var imageViewCenter = CGPoint()
override func viewDidLoad() {
super.viewDidLoad()
//set webView frame
webView.frame = self.view.bounds
webView.backgroundColor = UIColor.clearColor()
webView.userInteractionEnabled = true
webView.scalesPageToFit = true
webView.opaque = false
self.view.addSubview(webView)
//we will hold onto the original size of the UIImageView for later
imageViewSize = CGSizeMake(webView.bounds.size.width * 0.2, webView.bounds.size.height * 0.1)
//mock UIImageView
image.backgroundColor = UIColor.orangeColor()
image.frame.size = CGSizeMake(imageViewSize.width, imageViewSize.height)
image.center = CGPointMake(webView.bounds.size.width / 2, webView.bounds.size.height / 2)
webView.scrollView.addSubview(image)
//I've created a subset image to illustrate the orientation
image2.backgroundColor = UIColor.whiteColor()
image2.frame = CGRectMake(10, 10, 10, 10)
image.addSubview(image2)
}
And then change the frame when rotated (Notice that the position on the UIImageView and the width of the UIWebView are set relative to the height of their superViews and vice versa since the device is rotated):
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
image.frame.size = CGSizeMake(imageViewSize.width, imageViewSize.height)
image.center = CGPointMake(webView.bounds.size.height / 2, webView.bounds.size.width / 2)
webView.frame = CGRectMake(0, 0, self.view.bounds.size.height, self.view.bounds.size.width)
webView.scrollView.contentSize = webView.frame.size
}
}
Since you are creating the position and sizes of your views as percentages of their superViews, the code will play nicer with different device sizes (i.e. iPhone/iPad).