Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Autolayout confusion: Constraints for ScrollView with WebView and Header/Footer Views

To illustrate my problem, I created the following graphic:

Now in 3D

The black border around the red square is supposed to be the iPhone screen. Inside, we have in red a UIScrollView, which is taller than the screen. It is, in fact, as tall as a UIWebView, displayed above in yellow. The UIWebView is a subview of the UIScrollView, with scrolling disabled. Further more, we have two blue views, which I like to call the header and footer views. They are also subviews of the UIScrollView, but outside of the scrollable bounds.

My problem now is: How do I configure the auto layout constraints for this?

I have already managed to retrieved the UIWebView height using code from this answer and to then update the UIScrollView's contentSize accordingly. However, this is not really a good solution, because the UIWebView's width does not react to width changes of the UIScrollView.

I also managed to setup the header view's layout constraints like so (using Masonary):

[headerView makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(@-100);
    make.left.equalTo(@0);
    make.width.equalTo(scrollView.width);
    make.height.equalTo(@100);
}];

However, setting up the footer view's layout like this didn't work:

[changeView makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(scrollView.bottom)
    make.left.equalTo(@0);
    make.width.equalTo(scrollView.width);
    make.height.equalTo(@100);
}];

I feel like this can't be too difficult and would appreciate some help from someone a bit more familiar with auto layout. Thanks!

like image 309
fabian789 Avatar asked Oct 22 '22 02:10

fabian789


1 Answers

In Visual Format Language (VFL), the constraints would be:

[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(-100)-[headerView(100)]" options:0 metrics:nil views:views]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[footerView(100)]-(-100)-|" options:0 metrics:nil views:views]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[headerView]|" options:0 metrics:nil views:views]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[footerView]|" options:0 metrics:nil views:views]];

Or if you were going to replace the @"V:[footerView(100)]-(-100)-|" VFL with constraintWithItem calls, it would be:

[footerView addConstraint:[NSLayoutConstraint constraintWithItem:footerView
                                                       attribute:NSLayoutAttributeHeight
                                                       relatedBy:NSLayoutRelationEqual
                                                          toItem:nil
                                                       attribute:NSLayoutAttributeNotAnAttribute
                                                      multiplier:1.0
                                                        constant:100.0]];

[scrollView addConstraint:[NSLayoutConstraint constraintWithItem:footerView
                                                       attribute:NSLayoutAttributeBottom
                                                       relatedBy:NSLayoutRelationEqual
                                                          toItem:scrollView
                                                       attribute:NSLayoutAttributeBottom
                                                      multiplier:1.0
                                                        constant:100.0]];

I can't say why your Masonary implementation is not working, as I'm not familiar with it. But I'd try one of the above and make sure.

Frankly, you describe setting the height of the web view, and I wonder if you set the height constraint of the web view (good) or whether you just adjusted the frame.size.height value (bad). But you haven't shared the details of how you set the web view's height, so I cannot comment further.

Regardless, if you have constraint-based problems, there are two useful diagnostic techniques. If it's still not working, I'd run the app on the simulator, pause it, and then at the (lldb) prompt enter:

po [[UIWindow keyWindow] _autolayoutTrace]

You should see something like:

(lldb) po [[UIWindow keyWindow] _autolayoutTrace]
$0 = 0x0715d6e0 
*<UIWindow:0x758aae0>
|   *<UIView:0x71630f0>
|   |   *<UIScrollView:0x716a370>
|   |   |   *<HeaderView:0x716b860>
|   |   |   *<FooterView:0x716bbb0>
|   |   |   *<UIWebView:0x716c2b0>
|   |   |   |   <_UIWebViewScrollView:0x7176a30>
|   |   |   |   |   ...
|   |   |   |   |   <UIWebBrowserView:0x797fe00>
|   |   |   <UIImageView:0x75e1360>
|   |   |   <UIImageView:0x75e2a60>

Make sure you don't have any warnings there. If everything looks ok there, then I'd then use the following command at the (lldb) prompt:

po [[UIWindow keyWindow] recursiveDescription]

You should see something like:

(lldb) po [[UIWindow keyWindow] recursiveDescription]
$1 = 0x071c0ea0 <UIWindow: 0x758aae0; frame = (0 0; 320 568); autoresize = W+H; layer = <UIWindowLayer: 0x758b770>>
   | <UIView: 0x71630f0; frame = (0 20; 320 548); autoresize = RM+BM; layer = <CALayer: 0x7163180>>
   |    | <UIScrollView: 0x716a370; frame = (0 0; 320 548); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x716b1d0>; layer = <CALayer: 0x716ad20>; contentOffset: {0, 16}>
   |    |    | <HeaderView: 0x716b860; frame = (0 -100; 320 100); layer = <CALayer: 0x716b910>>
   |    |    | <FooterView: 0x716bbb0; frame = (0 564; 320 100); layer = <CALayer: 0x716bee0>>
   |    |    | <UIWebView: 0x716c2b0; frame = (0 8; 320 548); layer = <CALayer: 0x716c360>>
   |    |    |    | <_UIWebViewScrollView: 0x7176a30; frame = (0 0; 320 548); clipsToBounds = YES; autoresize = H; gestureRecognizers = <NSArray: 0x7177000>; layer = <CALayer: 0x7176c80>; contentOffset: {0, 0}>
   |    |    |    |    | ...
   |    |    |    |    | <UIWebBrowserView: 0x797fe00; frame = (0 0; 1024 1754); gestureRecognizers = <NSArray: 0x7173260>; layer = <UIWebLayer: 0x716d7f0>>
   |    |    |    |    |    | ...
   |    |    | <UIImageView: 0x75e1360; frame = (1 540; 318 7); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x75e2980>>
   |    |    | <UIImageView: 0x75e2a60; frame = (312 32.5; 7 530.5); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x75e2b00>>

Take a look at your various resulting frame values and see what's going on. That can help you diagnose the solution. You don't provide enough information for us to diagnose the problem (you tell us that the footer didn't work, but you don't tell us where your constraints ended up placing it). If you do the above commands, you can determine precisely where your constraint ended up placing your footer, and then figure out how to remedy the problem from there.

Note, you'll see that my headerView and footerView are showing up in these two listings as HeaderView and FooterView classes instead of UIView. When diagnosing constraint problems, it's useful if the key views have their own class, so I defined UIView subclasses with no implementation, but if I use those unique subclasses for my header and footer view, it makes it much easier to interpret the results of the above (lldb) commands.

@interface HeaderView : UIView
@end
@implementation HeaderView
@end

@interface FooterView : UIView
@end
@implementation FooterView
@end
like image 193
Rob Avatar answered Oct 29 '22 21:10

Rob