turbolinks / turbolinks-ios

Native iOS adapter for building hybrid apps with Turbolinks 5
MIT License
881 stars 92 forks source link

WebView page height is set too tall when the page doesn't fill the height #46

Closed glennfu closed 7 years ago

glennfu commented 8 years ago

If the height of the html is shorter than the height of the webView, then the page acts as if it's taller than you'd expect. Basically, the height is 100% the height of the device, ignoring the nav bar and status bar. As soon as you try to scroll down, you'll see the "top" of the page slide under the nav bar and align to the top of the device.

You can reproduce this easily by going to server/lib/turbolinks_demo/views/one.erb and delete half the paragraphs. You can also reproduce this, by replacing layout.erb with https://gist.github.com/glennfu/ba776039e1377699eb81f75952865606. Using this you can see an example of a height: 100% page, with flex box stretching content to fill the page. The top looks like it starts in the right place, but the bottom extends beyond the bottom of the device.

Note that on turbolinks-android, everything behaves just fine in both scenarios. In addition, for better or worse (I think I like it), on turbolinks-android there's no scrolling enabled in this case.

I've been wrestling around with this myself for a while. Hopefully someone else here can help come up with a clean fix!

Oh one more thing: You CAN fix the problem by adding this to DemoViewController:

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        self.edgesForExtendedLayout = .None
    }

However then you have to be satisfied with the opaque nav bar, which is a bummer. Maybe that's a clue that helps point someone in the right direction for a better fix.

Jasonbit commented 8 years ago

Thanks!

I am playing around with an app that uses a tab bar. The footer from the web page was obscured by this and adding the edgesForExtendedLayout to my visitable view controller subclass did the trick.

glennfu commented 8 years ago

@Jasonbit I did something similar as well, but without requiring edgesForExtendedLayout = .None

In my visitableDidRender I had mainToolbar = UIToolbar.init(frame: CGRectMake(0, navHeight + statusBarHeight - 1, view.frame.width, 1))

Then when I was ready to show it I'd do:

mainToolbar.frame = CGRectMake(mainToolbar.frame.minX, mainToolbar.frame.minY, mainToolbar.frame.width, 39 + 1)
mainToolbar.translucent = true

let views = [ "view": mainToolbar, "webView": visitableView ]
let metrics = [ "toolbarHeight": mainToolbar.frame.height ]
var allConstraints = [NSLayoutConstraint]()

view.layer.zPosition = -2
mainToolbar.layer.zPosition = 10

visitableView.removeConstraintsFromParent()

allConstraints += NSLayoutConstraint.constraintsWithVisualFormat("H:|[view]|", options: [], metrics: metrics, views: views)
allConstraints += NSLayoutConstraint.constraintsWithVisualFormat("H:|[webView]|", options: [], metrics: metrics, views: views)

allConstraints += NSLayoutConstraint.constraintsWithVisualFormat("V:|-toolbarHeight-[webView]|", options: [], metrics: metrics, views: views)
NSLayoutConstraint.activateConstraints(allConstraints)

This added the webView into the constraints appropriately so I could still have the translucent header while compensating for the size change. Unfortunately, it still doesn't affect the original bug I've posted here, but it does let you conditionally enable a tab view here without being forced to go opaque!

rohitgupta3 commented 7 years ago

Hi @glennfu thanks very much for this. Quick question - where do you use this code exactly? You say "when you were ready to show it" but I'm not sure I follow. Do you mean this is in visitableViewWillAppear?

Thanks again.

glennfu commented 7 years ago

@rohitgupta3 in my case, I do this in visitableDidRender inside the block of webView.evaluateJavaScript to run some JavaScript to see whether or not I want a toolbar on that page.

rohitgupta3 commented 7 years ago

Thank you!

benguild commented 7 years ago

This is fixed in iOS 11.

pietervisser commented 6 years ago

Hi, I'm still experiencing this same issue on iOS 11. This behavior is also visible in the demo project (see screenshot). In the attached image you will see that there is a scrollbar even though the page does not fill the full height. The workaround self.edgesForExtendedLayout = .None does not work on iOS 11.

I'm trying to fix this, but did not found a solution yet. Hopefully someone can give some insight about this?

image

pietervisser commented 6 years ago

A possible solution might be changing installVisitableView using other constraints:

fileprivate func installVisitableView() {
    view.addSubview(visitableView)
    if #available(iOS 11, *) {
        NSLayoutConstraint.activate([
            visitableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            visitableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            visitableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            visitableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)
        ])
    } else {
        NSLayoutConstraint.activate([
            visitableView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor),
            visitableView.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor),
            visitableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            visitableView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    }
}

@zachwaugh, could you check if this issue should be reopened? Thanks!

gearoidoceallaigh commented 6 years ago

@pietervisser The solution from @glennfu (thanks btw!) worked fine for me on iOS 11.3.1

summera commented 5 years ago

Has anyone figured out how to remove the unnecessary scroll? Tried @glennfu's solution but maybe I'm doing something wrong? Can't get it to work. We have a login view that we present and we are trying to show links at the bottom via flexbox, but the links are being pushed below the fold even though the height of the HTML can easily fit on the screen and there shouldn't be any scroll.

Using self.edgesForExtendedLayout = [] improves the situation but there is still some unnecessary scroll behavior.