简体   繁体   中英

Auto Layout Determine The Size of a Subview

I have a KSSection UIView subclass that I'm trying to use to do collapsing / expanding of different sections. It has a child view (set by an IBOutlet ) called content . The content 's size is determined by a number of child views ( UILabel , UIImageView , etc.) that are all variable size.

Currently I'm pinning the leading and trailing space of the content to the parent KSSection , aligning it centred vertically, and adding a remove at runtime constraint that the heights of content and section are equal. If I disable the remove at runtime everything works great - except that I can't collapse the view.

How can I calculate the size of the content to be used as the intrinsicContentSize of the KSection ? So far I have the following snippet, but the call to intrinsicContentSize always returns UIViewNoIntrinsicMetric for both properties.

@implementation SKContainer

- (CGSize)intrinsicContentSize
{
    if (self.collapsed) return CGSizeZero;
    else return [self.content intrinsicContentSize];
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    [self invalidateIntrinsicContentSize];
}

- (void)setCollapsed:(BOOL)collapsed
{
    if (_collapsed != collapsed)
    {
        _collapsed = collapsed;

        [self invalidateIntrinsicContentSize];
    }
}

@end

Edit:

Sorry to clarify it is actually returning UIViewNoIntrinsicMetric for both dimensions of the CGSize .

Attaching a sample: http://cl.ly/2F3s3X3y2U1H

You are misusing -intrinsicContentSize , both in why you're calling it and in your attempt to implement it for your view class. That method is for returning a size which is "intrinsic" to its nature and contents. It can not depend on other views, other constraints, etc. It also has nothing to do with the view's current size, because a view can be compressed or stretched from its intrinsic size by other constraints.

You should use constraints to make your view depend on its collapsed state and its subview's size (resulting from other constraints in combination with descendant views' intrinsic sizes, if they have any).

For example, assuming you want your view to collapse in the vertical direction, you might have constraints which always pin the content subview to the top, leading, and trailing edges. If your view is collapsed, you would have a constraint to make its height zero. You would not constrain your view's bottom to the content view's bottom. The content view would have its normal height, but that would all be clipped out by virtue of the fact that its superview has zero height.

On the other hand, if your view is not collapsed, you would remove the height constraint on your view and add a constraint connecting your view's bottom to the content view's bottom.

Okay, so my previous answer was on an unnecessary track. What I ended up doing was this:

First, I removed the section view entirely, leaving us with just the plain vanilla content view to play the role of the section. (The section view was just adding an extra layer of complication.) Then I lowered the priority of the section view height to 250, and ran the project. Presto! The section view now expands, all by itself, driven by the constraints of the labels within it.

Second, here's how I collapse and expand. I keep an outlet to two of the constraints: the section height constraint, and the last constraint in the height stack of the internal constraints. Then my expand/collapse code looks like this:

- (IBAction)toggleButtonSelector:(id)sender
{
    self.collapsed = !self.collapsed;
    if (self.collapsed) {
        self.sectionHeightConstraint.constant = 10; // or whatever height you like
        self.sectionHeightConstraint.priority = 999;
        [NSLayoutConstraint deactivateConstraints:@[self.bottomInternalConstraint]];
    } else {
        self.sectionHeightConstraint.priority = 250;
        [NSLayoutConstraint activateConstraints:@[self.bottomInternalConstraint]];

    }
}

You see, we need to overcome the desire of the internal stack of constraints to keep us expanded, so we remove one of the constraints in order to collapse, and we set the height constraint to a small number and raise its priority. To expand, we reverse that: we restore the missing internal constraint, and lower the priority of the overall section height constraint once again.

EDIT A really cool byproduct of this implementation is that we can now animate the collapse/expand effect merely by appending these lines of code at the end of that method:

    [UIView animateWithDuration:1 animations:^{
        [self.view layoutIfNeeded];
    }];

Using solutions from @ken and @matt my final code (still using the SKContainer) is:

@interface SKContainer ()

@property (nonatomic, strong) IBOutlet NSLayoutConstraint *expandedLayoutConstraint;
@property (nonatomic, strong) IBOutlet NSLayoutConstraint *collapsedLayoutConstraint;

@end

@implementation SKContainer

- (void)setCollapsed:(BOOL)collapsed
{
    if (_collapsed != collapsed)
    {
        _collapsed = collapsed;

        [self removeConstraint:collapsed ? self.expandedLayoutConstraint : self.collapsedLayoutConstraint];
        [self addConstraint:collapsed ? self.collapsedLayoutConstraint : self.expandedLayoutConstraint];
    }
}

@end

Where expandedLayoutConstraint is a equal height constraint to the content view and collapsedLayoutConstraint is a height 0 priority 200 constraint.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM