WKWebView+contentInset makes content size wrong - ios

On iOS, when you set a contentInset on a WKWebView's scroll view, it seems to make the web view think the content of the page is bigger than it is. For example, if you give a mostly-empty page a top or bottom contentInset, you'll be able to scroll the page down even though there's nothing to scroll to.
Is this expected behavior? Is there a workaround that still allows me to use contentInset?

The problem is that WKWebView always seems to use its frame as its viewport size, neglecting to subtract the scrollView's contentInset. I was able to find a workaround by stephan-leroux.
The idea is to set the WKWebView's frame to the desired viewport size, either manually or using autolayout constraints. To restore the "scroll-under" effect of contentInset, disable the scrollView's masksToBounds:
wkWebview.scrollView.layer.masksToBounds = NO;

weiyin's suggestion (resizing the web view's frame) worked for me.
Just wanted to share a code example:
CGFloat topInset = ...;
web.scrollView.layer.masksToBounds = NO;
[web.scrollView setContentInset:UIEdgeInsetsMake(topInset, 0, 0, 0)];
[web setFrame:CGRectMake(0, 0, web.width, web.height-topInset)];

Four years later and the problem still persists...
weiyin's answer works great, but if you're like me you might struggle a bit with figuring out what exactly to do (though it's really quite simple).
So here's a simple explanation for dummies (like me):
Embed your Web View inside a Wrapper View.
Size and position your Web View in a way that its frame is inset from the Wrapper View precisely by your desired content inset.
Set clipsToBounds = false on the Web View's scroll view (and true on the Wrapper View).
Make sure that there is no content inset set on the Web View (webview.contentInset = .zero).
(This is the default and normally, you don't need to set it at all).
Done:
100% content inset behavior on the Web View without using content insets.

Related

Adding ScrollView to ViewController with 2 custom Views

My viewController has one view with images and labels and one textView
Im new in objective c.
My problem is to add ScrollView in my ViewController with 2 custom views(UIView and UITextView).(image in the link) I have tried many things posted here in Stack but nothing works for me.
Thank YOU!
Here is what i have :
self.scrollView.contentSize=self.scrollView.frame.size;
self.scrollView.frame=self.view.frame;
[self.view addSubview:self.scrollView];
Adjusting view's frame was the technology of 5 years ago. You should never set the frame manually, not anymore. Instead start learning Autolayout and Constraints.
These tutorials may help:
https://www.raywenderlich.com/115440/auto-layout-tutorial-in-ios-9-part-1-getting-started-2
https://www.appcoda.com/auto-layout-guide/
You are setting the content size equal to the frame size before you actually set the frame, so it's probably just 0.
You need to just switch the calls around:
self.scrollView.frame=self.view.frame;
self.scrollView.contentSize=self.scrollView.frame.size;
[self.view addSubview:self.scrollView];
The other thing to keep in mind is that because you are setting the frame of a nested view to the frame of its superview, your layout will break (or at least not do what you expect), if the origin of your superview ever changes. If the origin is 0, 0, then you are fine for the moment, but otherwise you may want to set the subview (scrollView) frame to be equal to the superview (self.view) bounds instead of the frame, like this:
self.scrollView.frame=self.view.bounds

UIScrollView Only Works After Arbitrary App Interaction

I understand this is a duplicate of this question, but the question seems abandoned, the asker is not providing feedback and the current answers have not worked for me.
I currently have a simple app whose main view contains a UIScrollView. When the app is first installed and run, there is no scroll behaviour and the view is stuck at the top of the content view, which is not the intended functionality. Interacting with the app by rotating the device, touching in a text field or touching a segmented control all correct the scroll functionality (i.e. give the ability to scroll to the bottom of the content view):
Why is this behaviour happening?
How can it be fixed?
Details:
Once the app has been interacted with, it can be put in the background and reopened without losing the scroll functionality. Manually terminating the app from the background and reopening it will re-trigger the problem. The end goal is to have the scroll functionality work correctly and immediately without the user having to perform an arbitrary ritual to unlock part of the app's functionality (Very strongly discouraged in Apple's HMI guidelines).
The UIScrollView (scrollView) in question is embedded in the topmost UIView, and it has a UIView (scrollContentView) as a child. The scrollView's contentSize.height is defined programmatically in my viewWillLayoutSubviews() function, and this is the height that is used by the UIScrollView once one of the aforementioned actions is taken:
override func viewWillLayoutSubviews()
{
super.viewWillLayoutSubviews();
// (This is just setting a child table of the scrollView's height)
// Set the goal table's height to the height of its contents
formulaTableHeightConstaint.constant = getGoalTableHeight();
scrollView.contentSize.height = getGoalTableHeight() + Constants.formHeight;
}
At the end of the viewWillLayoutSubviews() function, if I print the scrollView.contentSize.height, it shows a large value around 2000, and printing the scrollView.frame.size.height shows a value around 600.
Attempts:
I have tried to use scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: SomeArbitraryValues, right: 0). This allows scrolling immediately when the app is first opened, but the behaviour is unintuitive in that, even setting the bottom to values that should be smaller than the scroll content height, allows the app to scroll a good distance past the bottom of the content. Just the same, setting smaller values usually prevents the view from scrolling to the bottom of the content. From what I understand from the documentation, this seems like a slightly unrelated (and maybe smelly) property to use to fix this problem (Though I could be wrong). As well, even though messing with the contentInset seems to trigger correct scrolling behaviour, printing the contentInset value before and after the scroll works shows no changes to it occurred.
I have also tried adding a height constraint to the scrollContentView and setting that height programmatically to no avail.
Any help on the matter is greatly appreciated.
You should set the contentSize height as follows:
scrollView.contentSize = CGSizeMake(width, getGoalTableHeight() + Constants.formHeight);
scrollView.contentSize.height refers to the height member of the struct that is returned from the contentSize method.

UIScrollView not scroll auto layout issues

I never used auto layouts in my life and it was great time, now storyboard take off my brain.
The conception maybe is not bad. But it's still buggy and worst to use. I have worked for today and spend 8 hours to setup one scroll view and few subviews. Does it save time???
If Apple wants to support adoptive design. Why they don't use Edge Reflow conception. It is for web, but it's great and without bugs....
I have scroll view with all needed constraints, it means that the scroll view size will be the same as superview.
I am trying to change scroll view content size in -viewDidLoad method:
[self.theScrollView setContentSize:CGSizeMake(self.view.frame.size.width, 20000)];
but seems it want work.
Print out says that the content size is 320 20000, but I can scroll it.
With autolayout, you have to do things bottom-up and implicitly; in other words, instead of telling the scrollView how big its content is, you let the scrollView adjust itself based on the size of its content. This allows the autolayout to resolve the constraints between subviews and propagate the changes upward.
If you want to explicitly set the content size, you could create an additional subview containing all the content of the scrollView, and then override its intrinsicContentSize method to explicitly tell the scrollView how big it is.
Working tried out.
if you are not using autolayout then put it only viewDidLoad():
[_mainScroll setContentSize:CGSizeMake(320, 2000)];
or if you are using autolayout then follow this:
First give constrains to scrollview top,bottom,leading and trailing.
Now take one more view and give same constrains as scrollview given by you earlier.
Don't forgot to give additional constrain for that view fix height and width equal to main parent view.
or
Just watch this.
https://www.youtube.com/watch?v=rjTS9fyWqdg

iOS: Getting height of views with programatically added constraints as only indicator

Hello there fellow iOS programmers. While creating an app I've ran into a problem I can't seem to find an answer to. Let's lay it out:
I'm creating a UIViewController with a UIScrollView as it's only child. Inside this view I have a UIView, and inside of this there is a list of UIViews with UILabels inside them. As you all know you need to specify a contentSize for a UIScrollView.
The problem is that the list needs to be dynamic with it's content, and I therefore have no way to know the views heights beforehand. I'm adding all views with constraints where the height is set to ">=0".
When I later try to set the height of the UIScrollView I need to either get the height of the UIView that the list is inside, or get the origin.y and height of the last view in the list. This of course needs to be ready by the time the view is displayed to the user.
I've currently tried view.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize), which returned 0; view.systemLayoutSizeFittingSize(UILayoutFittingExpandedSize), which returned 10000; and view.origin.y + view.frame.height, which also returns 0. It seems to me like the constraints haven't taken effect yet.
I've tried setting both constraints and UIScollView.contentSize from viewDidLoad(). I've also tried to set constraints in viewDidLoad and setting contentSize in viewWillAppear; this yielded the same results. Setting constraints in viewWillAppear and setting contentSize in viewDidLoad only caused a crash.
Bottom-line: When should I set up the UIScrollView.contentSize if I want to get view.height or similar methods to return a correct result, while at the same time be ready by the time the user sees the view?
Btw, I'm making this app in Swift, so answers in this language is preferred, but I'm able to translate from Objective-C to Swift; post in whatever suits you best.
Thank you! :)
You say:
As you all know you need to specify a contentSize for a UIScrollView.
No, as TN2154 says, the constraints between the scroll view and its subviews are "interpreted as the content size of the scroll view" (emphasis added). This is a boon, because you no longer have to mess around with contentSize if doing auto-layout. Just set the constraints for the scroll view's subviews and the content size takes care of itself. This leverages the labels' intrinsic size (and make sure that the label's numberOfLines to zero).
The only trick is that it sometimes cannot figure out the correct width of the labels (because the trailing constraint is to the scroll view's content size, it will sometimes make the scroll view a horizontally scrolling one). You can remedy this by either manually setting preferredMaxLayoutWidth or by setting a width constraint between the label and the scroll view's superview.
Personally, while I understand the inclination to add the UIView containers between the scroll view and the labels, I'd suggest losing them unless you need them for some other reason. IMHO, it simply complicates the constraints unnecessarily. It's hairy enough as it is. Obviously, if these containers bear other utility for you, then go ahead and keep them (and they'll work fine), but if you're doing this simply for the sake of the constraints, you might consider eliminating them.

UIScrollView using Autolayout will only bounce

I am really struggling to get a UIScrollView to work correctly with Autolayout. Instead of scrolling down as it should, it just bounces, so if I drag it to see more as soon as I let go it returns to its original position.
I have my scene set up in the following way:
-Main View
- Scroll View
- Content View
- Label
- Label
The View Controller has its size metric set to Freeform. The Main View, Scroll View and Content View all have their Height set to 700, so I can see the layout correctly.
In my .h file I have an outlet connected to the UIScrollView and in my viewDidLoad method I am doing the following:
Scroller.translatesAutoresizingMaskIntoConstraints = YES;
[self.Scroller setContentSize:CGSizeMake(320, 1000)];
I've tried setting all sorts of constraints, on various things. Normally when I add a new one Xcode then grumbles that the constraints are incorrect and prompts me to update them. I have tried so many different variations, I can't remember them all but here are a few:
Pinning the height of the Scroll View
Pinning the bottom of the Content View to the Scroll View - Getting UIScrollView to work with AutoLayout
Pinning the height of the Content View
Other things I have tried:
Reading & following Apple's Technote: - https://developer.apple.com/library/ios/technotes/tn2154/_index.html
Setting the ScrollView's Content Inset - Confusion Regarding UIScrollView and AutoLayout
Setting the Content Size: UIWebView's UIScrollView subview can scroll but bounce back to the frame without appearing vertical scroll indicator
I'm sure this must be relatively straight forward, but I have been tearing my hair out. There are many similar questions on Stackoverflow and Google, but none of them seem to fully meet my requirements.
Any help or resources would be greatly appreciated.
JA
One possible reason for this is that scroll view's contentSize is reset after layout event which happens after viewDidLoad (for example due to incorrect constrains settings).
I had to put the code in viewDidAppear for it to work right
CGSize size = CGSizeMake(320, 931);
self.scroller.scrollEnabled = YES;
self.scroller.contentSize = size;

Resources