To illustrate my problem, I created the following graphic:
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!
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With