Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Container View doesn't honour intrinsicContentSize

I want to use a Container View to contain a camera preview. I want the camera to centered in the middle and maintain it's proper aspect ratio. (it's pink to make it obvious where its frame is)

Story board showing container view

I'm stuck on the first step of trying to get the camera preview to appear at a size that's smaller than the container.

I have a UIView subclass for the Camera Controller's view, and have the code:

- (CGSize)intrinsicContentSize
{
  return CGSizeMake(320, 240);
}

Unfortunately this isn't honoured:

iPad simulator result

From reading the docs, intrinsicContentSize looks like it's what I want. Eventually I want to use Auto Layout as well, but I'm trying to solve one thing at a time.

How do I make this work?

like image 326
bradley.ayers Avatar asked Jun 29 '13 02:06

bradley.ayers


1 Answers

Auto layout uses the intrinsicContentSize property. If you're not using auto layout, the system will not ask any view for its intrinsicContentSize.

The pink view will be forced to the size of the container view unless you write some code. This is true whether you are using auto layout or not. This is just the way container views created in a storyboard work. The system will not ask your pink view for its intrinsicContentSize.

Since you imply that you're not using auto layout, here's one way you can make the container view be the intrinsicContentSize of its content view. Make a subclass of UIView called CameraContainerView and set this as the custom class of the container view. Override addSubview: in this new class:

@implementation CameraContainerView

- (void)addSubview:(UIView *)subview {
    [super addSubview:subview];
    CGRect frame = self.frame;
    frame.size = subview.intrinsicContentSize;
    self.frame = frame;
}

@end

That is sufficient in my testing. (It won't stay up to date if the subview changes its intrinsicContentSize dynamically.)

If you decide to use auto layout, you need to set up constraints between the container view and the content view, and you need to remove any conflicting constraints.

First, in your storyboard, make sure the container view has explicit width and height constraints. (These constraints will be deleted so their exact sizes don't matter.) Give CameraContainerView an outlet for each of these constraints:

@interface CameraContainerView ()

@property (nonatomic, strong) IBOutlet NSLayoutConstraint *widthConstraint;
@property (nonatomic, strong) IBOutlet NSLayoutConstraint *heightConstraint;

@end

Connect these outlets to the constraints in the storyboard. Note that these outlets are on CameraContainerView, not on ViewController, so don't try to control-drag from the view controller to the constraints; that won't work.

Also make sure that the container view isn't pinned to both the left and right (leading and trailing) edges of its superview, and that it isn't pinned to both the top and bottom of its superview. It's important that the container view's size be determined only by its explicit width and height constraints.

After you've set up the constraints in the storyboard and hooked up the outlets, you can rewrite addSubview: for auto layout. You'll need to turn off translatesAutoresizingMaskIntoConstraints on the embedded view:

@implementation CameraContainerView

- (void)addSubview:(UIView *)subview {
    subview.translatesAutoresizingMaskIntoConstraints = NO;

    [super addSubview:subview];

Then you need to remove the width and height constraints that were loaded from the storyboard:

    [self removeConstraint:self.widthConstraint];
    self.widthConstraint = nil;
    [self removeConstraint:self.heightConstraint];
    self.heightConstraint = nil;

Finally you can set up new constraints that position the embedded view in the container view and make the container view match the size of the embedded view:

    NSDictionary *views = NSDictionaryOfVariableBindings(subview);
    [self addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"H:|[subview]|"
        options:0 metrics:nil views:views]];
    [self addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"V:|[subview]|"
        options:0 metrics:nil views:views]];
}

@end

That worked in my testing, and should stay up to date if the subview changes its intrinsicContentSize dynamically, as long as the subview sends itself invalidateIntrinsicContentSize.

like image 173
rob mayoff Avatar answered Sep 22 '22 04:09

rob mayoff