Dynamic height of UIWebView with auto layout - iphone

I have a problem with setting the height of an UIWebView dynamically. The webview loads a product description which contains html. I want the UIWebView to change its height based on the contents of the description. The parent scrollview should also change its height, but then based on the height of the UIWebView.
Can anyone explain to me how I can achieve the desired behavior of my views?
This is my view hierarchy:

You have to add some code in the delegate method of UIWebView named webViewDidFinishLoad. Here is what you can do:
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
CGRect frame = webView.frame;
frame.size.height = 1;
webView.frame = frame;
CGSize fittingSize = [webView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
webView.frame = frame;
NSLog(#"size: %f, %f", fittingSize.width, fittingSize.height);
yourScrollView.contentSize = webView.bounds.size;
}
The same thing can also be achieved using javascript to find the right height which is as follow:
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
CGRect oldBounds = [[self webView] bounds];
CGFloat height = [[webView stringByEvaluatingJavaScriptFromString:#"document.height"] floatValue];
NSLog(#"NEW HEIGHT %f", height);
[webView setBounds:CGRectMake(oldBounds.origin.x, oldBounds.origin.y, oldBounds.size.width, height)];
yourScrollView.contentSize = webView.bounds.size;
}
Hope it helps!

I actually had the same problem recently and ended up figuring it out and putting together a sample project on github.

You can set a constant height constraint on your WebView and change that dynamically once you calculate the height from the response.
Also constraint between a child view and a parent view can be achieved by this method in NSLayoutConstraint
+ (id)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c

Related

UITextView will not resize

I have tried resizing a UITextView (inside a tableViewCell) to the content size. but it will not change its height at all. I have even changed the height of the UITableViewCell. What could be wrong?
- (void) setTextViewContents: (NSString*) string
{
[textView setText:string];
CGRect frame2 = self.frame;
frame2.size.height = 10000;
self.frame = frame2;
/* resize the view */
CGRect frame = textView.frame;
frame.size.height = textView.contentSize.height+60;
textView.frame = frame;
The string does appear on the view but the size does not change.
Try calling the -[UIView sizeToFit] method.

Is it possible to add fixed content to a UIScrollView?

I want to create a subclass of UITableView or UIScrollView that will have some shading at the top when the content offset is > 0 to indicate that the content is scrollable. (See image attached)
The way I'm implementing it right now is using the UIViewController that is the delegate of the tableView. I simply have a GradientView on top of the tableView, and I intercept scrollViewDidScroll: to animate the visibility of that top gradient.
My problem with this implementation is that it's not "clean". I want my UIViewControllers to take care of logic, and not to deal with applying gradients and stuff. I wish I could just drop a subclass of UITableView that will do that for me.
The challenge for me is that I can't figure out how the tableView could add to itself a fixed content on top of the scrollable content.
Another question is what method/s of UIScrollView should I override to intercept the scrolling event. Obviously I don't want the tableView to be the delegate of itself...
Any ideas?
Thanks!
Ok, so I found the solution on Apple's WWDC 2011 Session 104 video - Advanced Scroll View Techniques.
There is a whole section in this video about "Stationary Views" inside a scroll view.
According to Apple, the way to go here is to override layoutSubviews and put there all the code to position whatever you want - wherever you want.
I tried it and it's actually pretty easy and it's working as expected.
So for example if I would like a shadowed header on top of the table when the content is being scrolled, this is the code I should write:
-(void) layoutSubviews
{
[super layoutSubviews];
[self positionTopShadow];
}
-(void) positionTopShadow
{
CGFloat yOffset = self.contentOffset.y;
// I'm doing some limiting so that the maximum height of the shadow view will be 40 pixels
yOffset = MIN(yOffset, 40);
yOffset = MAX(0, yOffset);
CGRect frame = self.topShadowView.frame;
// The origin should be exactly like the content offset so it would look like
// the shadow is at the top of the table (when it's actually just part of the content)
frame.origin = CGPointMake(0, self.contentOffset.y);
frame.size.height = yOffset;
frame.size.width = self.frame.size.width;
self.topShadowView.frame = frame;
if (self.topShadowView.superview == nil)
{
[self addSubview:self.topShadowView];
}
[self bringSubviewToFront:self.topShadowView];
}
I've managed to figure out a much simpler way of doing this then what Avraham did.
I use the fact that the UIScrollView calls scrollViewDidScroll: ever pixel the scrolling changes to set the object at the location of the offset. Below is my full code to keep a gray bar at the top of the scrollview as you move around:
- (void)viewDidLoad {
UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(5.0, 50.0, self.bounds.size.width - 15.0, self.bounds.size.height - 60.0)];
[scrollView setBackgroundColor:[UIColor colorWithRed:251.0/255.0 green:251.0/255.0 blue:251.0/255.0 alpha:1.0]];
[scrollView setContentSize:CGSizeMake(scrollView.frame.size.width + 500, 1000.0)];
[scrollView setDelegate:self];
[self addSubview:scrollView];
UIView* header = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, scrollView.contentSize.width, 40.0)];
[header setTag:100];
[header setBackgroundColor:[UIColor darkGrayColor]];
[scrollView addSubview:header];
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
UIView* header = [self viewWithTag:100];
[header setFrame:CGRectMake(0.0, scrollView.contentOffset.y, header.bounds.size.width, header.bounds.size.height)];
}
You could try using viewForHeaderInSection method of tableView for the shaded view(and also heightForHeaderInSection)... Make the shaded portion as a header.That way there is a fixed content on top of the scrollable content.
#define kImageOriginHight 300
- (void)scrollViewDidScroll:(UIScrollView *)scrollView1{
CGFloat yOffset = scrollView1.contentOffset.y;
// NSLog(#" y offset := %f", yOffset);
//zoom images and hide upper view while scrooling to down position
if (yOffset < 0) {//-kImageOriginHight
CGRect f = imgV.frame;
f.origin.y = yOffset;
f.size.height = -yOffset + kImageOriginHight;
imgV.frame = f;
//viewTableUpperView.alpha = 1.5 - (yOffset/-kImageOriginHight);
//viewTableUpperView.userInteractionEnabled = NO;
if(yOffset+0.5 == -kImageOriginHight){
[UIView animateWithDuration:0.1 animations:^{
//viewTableUpperView.alpha = 1.0;
}];
//viewTableUpperView.userInteractionEnabled = YES;
}
}
}

UIScrollView not scrolling when keyboard covers active UITextField (Using apple's example)

I'm new to iOS programming, and I'm having trouble with getting a UIScrollView to move when editing a UITextField that is obscured by the keyboard. The code is straight out of Apple's documentation but it's not working for some reason.
Through debugging I've found that the notifications seem to be getting passed correctly (i.e. it logs "View should resize", but only when activeField is the textField that is under the keyboard) and scrollpoint is being set correctly, but the scrollview still does not move. Also, I'm reasonably sure that the delegation pattern is correct (ViewController is delegate of textFields as well as scrollView)
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y-kbSize.height);
[scrollView setContentOffset:scrollPoint animated:YES];
NSLog(#"%#",#"view should resize");
}
}
Seeing as the code is straight from the documentation, I'm probably just missing something simple. Can anyone point me in the direction of things to check for?
Apple's example has a bug in that it doesn't explicitly set the scroll view's content size and thus uses the default content size which is (0, 0). I fixed this problem by adding this code in my view controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Set the scroll view's content size to be the same width as the
// application's frame but set its height to be the height of the
// application frame minus the height of the navigation bar's frame
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
CGRect navigationFrame = [[self.navigationController navigationBar] frame];
CGFloat height = applicationFrame.size.height - navigationFrame.size.height;
CGSize newContentSize = CGSizeMake(applicationFrame.size.width, height);
((UIScrollView *)self.view).contentSize = newContentSize;
}

UIWebView problem with size adjustment when rotating device

I have a UIWebview in which a have a local html placed . When the ipad shifts, i rearange the elements so that the UIWebView is smaller/bigger after the case. The problem is that although when i sift and adjust the width, although the width is adjusted, the text isn't . The text is visible only in the webview's frame but it goes offscreen (some text is hidden, it continues to some point not being visible) . How can i adjust the webview content width ?
I have faced this problem and what i did to solve is like this ... create a mothod
-(void)reconfigureFrame
{
[_yourWebView loadHTMLString:[NSString stringWithFormat:#"<html><body style='background-color: transparent; width: 280px; margin: 0; padding: 0;color:black;'><div id='ContentDiv'>%#</div></body></html>",theStringToShow] baseURL:nil];
}
in webView:DidLoad delegate method do this
NSString *contentHeight = [webView stringByEvaluatingJavaScriptFromString:#"document.getElementById('ContentDiv').offsetHeight"];
CGRect frame = [containtView frame];
//containtView holds webView
frame.size.height = [contentHeight floatValue] + 10;
[containtView setFrame:frame];
frame = [webView frame];
frame.origin.x = 10;
frame.origin.y = 5;
frame.size.width = containtView.frame.size.width - 2*frame.origin.x;
frame.size.height = [contentHeight floatValue];
[webView setFrame:frame];
and in didAutoRotate just call the first method

UIWebView won't resize to fit content?

My webView is not resizing properly after it finished loading. I have tried this code.
- (void)webViewDidFinishLoad:(UIWebView *)webview {
CGRect frame = webView.frame;
frame.size.height = 1;
frame.size.width = screenWidth;
webView.frame = frame;
CGSize fittingSize = [webView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
webView.frame = frame;
[bodyScroll setContentSize:CGSizeMake(screenWidth, webView.frame.origin.y + webView.frame.size.height+keyboardRectY+60)];
It is incresing the width beyond the screenWidth and hence my content horizontally expands beyond the view size which I don't want.
changing
CGSize fittingSize = [webView sizeThatFits:CGSizeMake(screenWidht, 1)];
also leads to the same situation.
How can I get rid of this? The webview width should always remain as screenwidth and only it's height should adjust based on the content size so that the webView just about fits the whole content.
Thanks in Advance
When setting the size of frame, you are overwriting the width you set previously. This should it:
...
CGSize fittingSize = [webView sizeThatFits:CGSizeZero];
fittingSize.width = screenWidth; // Add this
frame.size = fittingSize
...