UITableView + UIWebView within UIScrollView - uitableview

I'm struggling with wrapping my head around using UITableView with UIWebView within UIScrollView. The interface I'm trying to build consist of a header (containing a few labels, always the same size), UIWebView (can be any height, I'm scaling it dependently on the content) and a UITableView (also can have any number of elements). I want user to scroll through the whole content like he would be scrolling through one page.
I realize that Apple doesn't recommend using UITableView or UIWebView within UIScrollView. However, I'm having a hard time to think about some workaround.
Is it possible to get the desired effect done the way I'm trying to do it right now? If not, what would be the cleanest or at least working way of doing it?
(I'm currently using Xcode 6 beta 3 / swift)

You can set your UIScrollView's contentSize.height to the sum of your UITableView's contentSize.height and the height of your UIWebView's content.
To get it, just add this to your ViewController after setting it as your UIWebView's delegate:
func webViewDidFinishLoad(webView: UIWebView!) {
let output = webView.stringByEvaluatingJavaScriptFromString("document.body.offsetHeight;")
let contentHeight = (output as NSString).floatValue
}
Then, you just have to set the frame height of your tableView and webView to their content's height and place them correctly in the scroll view.
Hope this will help,

Related

Swift - contents within scrollview get chop off

Facing an issue when scrolling, a part of content didn't displayed.
https://i.stack.imgur.com/70FIC.png
Here it is what is supposed to be displayed
https://i.stack.imgur.com/ViPjF.png
My UI is designed in storyboard, the structure for the scrollable component is as below:
UIView -> UIScrollView -> UIStackView -> Multiple UIViews
If wondering how I take the screenshot of the one that's supposed to be displayed, the price is a drawer component, somehow after I tapped the drawer component, the content will be displayed, but after I scroll to top, same issue will happen to the content, which they get chop off.
https://i.stack.imgur.com/OKSEY.png
Updates:
Trying to put background colour on views to see which view is blocking the scrollview.
https://i.stack.imgur.com/vZBY7.png
Found that the scrollview (Booking DetailsSV) blocked by its superview (Booking Details View) in the hierarchy. Here is the screenshot taken in the test phone.
https://i.stack.imgur.com/Wm7xa.png
Is it suppose to happen like this?
Your scrollview doesn't seem to have correct contentSize. You need to set content size of your scrollView like following:
let sizeOfContent = 500
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width, sizeOfContent);
You can do this in viewDidLoad.

Swift - Can't understand the contentSize of a UIScrollView

Hi I have set in my storyboard a UIScrollView (of the same size of the view) and ctrl-dragged it in my code.
Now I have created a button programmatically using a simple algorithm of mine.
I then tried to
println(button.frame.origin.y)
which printed 1700.
Then I tried
println(button.frame.height)
which printed 142.2 (also coming from the algorithm). So the heigh I would like to reach while scrolling is 1700 + 142.2 = 1842.2
So i tried to hardcode the content size in this way
self.scrollView.contentSize = CGSize(width:self.view.bounds.size.width, height:2000)
But when I run the app I can scroll no more than the middle of the button, which is in numbers
1700 + 142.2/2 = 1771.1
Why the heck is that?
I did all this to try to understand how this UIScrollView works but I can't figure it out.
EDIT
Added a screenshot.
Inside the bubbles there is the button.frame.origin.y value. That's the max I can scroll leaving the screen untouched.
This is probably to do with scroll view insets - a mechanism where the edges of the content always appear under the navigation and toolbar.
You either need to manually account for this by reading the contentInset property of the scroll view, or using interface builder.
Scroll view Reference:
https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/UIScrollView_pg/CreatingBasicScrollViews/CreatingBasicScrollViews.html

Resize UITableView and Today Extension dynamically

After trying this to dynamically resize my UITableView according to its contents size and failing:
self.tableViewPairedDevices.sizeToFit()
I tried this which worked fine as long as my Today Widget is tall enough to show it entirely:
var frame:CGRect = self.tableViewPairedDevices.frame
frame.size.height = self.tableViewPairedDevices.contentSize.height
self.tableViewPairedDevices.frame = frame
My next challenge was to get my iOS 8 Today Widget to resize according to the UITableView size, which I almost solved with:
self.preferredContentSize = self.tableViewPairedDevices.contentSize;
self.tableViewPairedDevices.setTranslatesAutoresizingMaskIntoConstraints(false)
The problem is that only one of them works at a time, if the UITableView gets resized, the extension's view doesn't, if I comment out the code that resizes the UITableView, the Today Extension gets resized successfully (the weird thing is that it knows what size the UITableView should be, even though the UITableView itself hasn't been resized) but the UITableView doesnt.
Any suggestions on how to achieve both things?
Thanks a lot!
I was able to make the above code work perfectly by disabling AutoLayout on the Extension's view
Don't forget UITableView has a Scroll inside. You need to define the height somehow.
I add a height constraint to the UITableView and then I updated that value to the contentSize.height
And that works.

Detect when multiple UIWebViews on the same view have all finished loading?

Context
Single ViewController
Two UIWebView objects as subviews of a UIScrollView are in this ViewControllers view
The ViewController is the delegate of both UIWebViews
The delegate method - (void)webViewDidFinishLoad:(UIWebView *)webView gets called twice as expected for both UIWebViews
I'm setting the height of these UIWebViews using sizeThatFits once their content has loaded (in the webViewDidFinishLoad method
What I want to do
Once both WebViews have loaded their content I want to set the height of the UIScrollView that they are in relative to the WebViews height. Basically I want the UIScrollView to be tall enough to scroll all the way through the text in the UIWebViews.
Possible Solutions I've thought of
Have a counter within the webViewDidFinishLoad method, and when it is equal to the amount of WebViews on the view call a method that sets the height of the UIScrollView.
Call set height of UIScrollView in webViewDidFinishLoad - it will be called multiple times, but the last call will be the correct height.
Question
How do I work out when both webViews have loaded in a "better" way than the possible solutions above?
I think you're making this too complicated. As you suggest at the end of your question, why not just set the height (contentSize.height, I believe is what you want) with each webViewDidFinishLoad:? You can test to see which webview is the tallest and set the height to that value:
CGSize size = CGSizeMake(320, 0);
size.height = webView1.frame.size.height + webView2.frame.size.height;
scrollView.contentSize = size;
IMO it's not a great idea to be placing multiple webviews inside a scrollview (although I've done it before too...) Could you consider getting rid of the scroll view altogether and having a single webview (which is self-scrolling) and has two iframe elements, which each display your original webview1/webview2 content?

how to forward UIWebView scroll to UIScrollView ? (conditionally)

I have a UIWebView inside UIScrollView.
This idea is to be able to create more reading space on screen when user scroll the webpage upwards - by scrolling the UIScrollView upwards till the toolbar is visible, and obviously when the toolbars is nomore visible actually scroll the webpage to show more content that's on the page.
IPhone Safari browser does exactly the same functionality.
see screenshot (first) for default behavior i am getting - i guess because : the scrolling message is consumed by the webview since the touch/scroll is happening directly on webview area.
what i would like to do here is programatiicaly decide when to forward the 'upward scroll' to the UIScrollivew and when not to.
Any tips on how to get around this will be so helpful. Thanks!!
The UIWebView class reference discourages embedding a UIWebView in a UIScrollView:
Important: You should not embed UIWebView or UITableView objects in
UIScrollView objects. If you do so, unexpected behavior can result
because touch events for the two objects can be mixed up and wrongly
handled.
However I suppose you still want to implement your feature ;-)
Idea 1: Don't embed the UIWebView in a UIScrollView, instead run javascript using UIWebView's stringByEvaluatingJavaScriptFromString: method, modifying the DOM to add a toolbar below. If required, you can callback objective-c code from any buttons pushed on the toolbar by registering some custom URL schemes.
Idea 2: Don't embed the UIWebView in a UIScrollView, but in a normal UIView. Building on Vignesh's suggestion, listen for your webView's inner scrollView's scrollViewDidScroll: callback via the delegate, checking the contentOffset vs. the contentSize's height each time the callback is called. Once they are equal, it means you got to the bottom. Once this happens you can animate your toolbar's frame to "enter" the containing UIView and "push" the webView's frame away.
Idea 3: Ignore Apple's recommendation, and embed the UIWebView in a UIScrollView. Building on Vignesh's suggestion, listen for your webView's inner scrollView's scrollViewDidScroll: callback via the delegate, checking the contentOffset vs. the contentSize's height each time the callback is called. Once they are equal, it means you got to the bottom. Once this happens set the userInteractionEnabled property of the webView to NO, and set it to YES on the scrollView which contains the webView and the toolbar. Hopefully the scroll will continue smoothly enough. Of course you have to listen to the containing scroll view in the same way to determine when to switch back the userInteractionEnabled.
A variation on this idea would be to just set userInteractionEnabled to NO for the webView, but set the webView's frame's height to match its contentSize, and also enlarge the contentSize of the containing scrollView accordingly.
Both variations have the drawback that in some cases you won't be able to do things such as click on links :-( But maybe that's good enough for your case. At least in the first variation it's not so bad.
You can add a searchbox and uiwebview in a UIscrollview one below another. To get the content offset when webview is scrolled you can use the following code snippet.
UIScrollView* currentScrollView;
for (UIView* subView in testWebView.subviews) {
if ([subView isKindOfClass:[UIScrollView class]]) {
currentScrollView = (UIScrollView*)subView;
currentScrollView.delegate = (id)self;
}
}
Now you can change your base scrollview offset's y value to the offset value you get from the scrollview didscroll delegate method.
Advanced ScrollView Techniques
https://developer.apple.com/videos/wwdc/2011/

Resources