单个自动布局约束更改会破坏整个布局

时间:2014-07-31 13:50:55

标签: ios uiscrollview autolayout ios8 nslayoutconstraint

我有UIViewController创建此布局:

UIViewController

给我带来麻烦的部分是底部较暗的区域,包含文字。

较暗的部分是UIScrollView子类,它使用自动布局创建此布局:

UIScrollView

我有几个UILabel s(“Omschrijving”,“I​​nformatie”,以及视图底部的小标签)和UITextView(包含以“罗伯特兰登“)。 我将UITextView的高度明确设置为60点,当点击“Meer”按钮时,我使用boundingRectWithSize:options:attributes:context计算其全高。然后我将其高度约束常数从其先前的硬编码值更改为我计算的值。

这就是出错的地方。在UITextView的高度变化之前,布局绝对正常。 UIScrollView子类中的所有内容似乎都莫名其妙地移动了。我使用Reveal查看了视图层次结构:new view hierarchy

我几个小时以来一直在搞乱这些限制,我找不到解决方案。

所有视图都将translatesAutoresizingMaskIntoConstraints设置为NO。 这是模拟器上的iOS 8,带有Xcode 6 b4。

我的init方法如下所示:

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.translatesAutoresizingMaskIntoConstraints = NO;
        [self layoutIfNeeded];

        [self addExpandButton];
        [self setupUI];
        [self setupConstraints];
        [self layoutIfNeeded];

        self.backgroundColor = [UIColor clearColor];
        self.textView.backgroundColor = [UIColor clearColor];
        self.textView.userInteractionEnabled = NO;
    }
    return self;
}

setupUI创建所有视图并将它们添加到scrollView。为简洁起见,我删除了一些重复的代码行

- (void)setupUI
{
    // Initialization
    UILabel *descriptionLabel = [[UILabel alloc] initWithFrame:CGRectZero];

    UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 60.0) textContainer:nil];
    textView.translatesAutoresizingMaskIntoConstraints = NO;
    textView.editable = NO;
    textView.textContainerInset = UIEdgeInsetsMake(0.0, -5.0, 0.0, -5.0);
    textView.textColor = [UIColor whiteColor];

   // init all the UILabels with CGRectZero frames
   // ...
   // ...

    UIView *separator = [[UIView alloc] initWithFrame:CGRectZero];
    separator.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.5];

    // Set the text
    descriptionLabel.text = @"Omschrijving";

    textView.text = @"Robert Langdon, hoogleraar kunstgeschiedenis en symboliek, wordt op een nacht wakker in een ziekenhuis in Florence zonder te weten hoe hij daar is beland. Geholpen door een stoïcijnse jonge vrouw, Sienna Brooks, vlucht Langdon en raakt hij verzeild in een duizelingwekkend avontuur. Langdon ontdekt dat hij in het bezit is van een reeks verontrustende codes, gecreëerd door een briljante wetenschapper; een genie dat geobsedeerd is door het einde van de wereld en het duistere meesterwerk Inferno van Dante Alighieri.";

    // Set the text of the labels
    // ...
    // ...

    descriptionLabel.font = [UIFont fontWithName:@"ProximaNova-Semibold" size:14.0];
    textView.font = [UIFont fontWithName:@"ProximaNova-Regular" size:12.0];
    informationLabel.font = [UIFont fontWithName:@"ProximaNova-Semibold" size:14.0];

    UIFont *font = [UIFont fontWithName:@"ProximaNova-Regular" size:10.0];
    // Set the font of the labels
    // ...
    // ...

    // Add the views to the view hierarchy
    [self addSubview:descriptionLabel];
    [self addSubview:textView];
    [self addSubview:separator];
    [self addSubview:informationLabel];
    // Add the other labels as subviews
    // ...
    // ...

    [self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if ([obj isKindOfClass:[UILabel class]]) {
            UILabel *label = (UILabel *)obj;
            [label sizeToFit];
            label.textColor = [UIColor whiteColor];
        }
    }];

    // Assign the local properties to properties
    // ...
    // ...


    for (UIView *view in self.subviews) {
        view.translatesAutoresizingMaskIntoConstraints = NO;
    }

}

现在谈到约束。我有一个很大的方法,可以添加所有名为-addConstraints的约束。

- (void)setupConstraints
{
    // Omschrijving label top
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-3-[descriptionLabel]-3-[textView]"
                                                                 options:NSLayoutFormatAlignAllLeading
                                                                 metrics:nil
                                                                   views:@{@"descriptionLabel": self.descriptionLabel,
                                                                           @"textView": self.textView}]];

    [self addConstraint:[NSLayoutConstraint constraintWithItem:self.descriptionLabel
                                                     attribute:NSLayoutAttributeTop
                                                     relatedBy:NSLayoutRelationEqual
                                                        toItem:self
                                                     attribute:NSLayoutAttributeTop
                                                    multiplier:1.0
                                                      constant:3.0]];

    // Omschrijving label leading
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-10-[descriptionLabel]"
                                                                 options:0
                                                                 metrics:nil
                                                                   views:@{@"descriptionLabel": self.descriptionLabel}]];

    // Text view width
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"[textView(==width)]"
                                                                 options:0
                                                                 metrics:@{@"width": @220.0}
                                                                   views:@{@"textView": self.textView}]];

    // Text view height
    NSArray *textViewHeightConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[textView(==60.0)]"
                                                                                 options:0
                                                                                 metrics:nil
                                                                                   views:@{@"textView": self.textView}];

    [self addConstraints:textViewHeightConstraints];
    self.textViewHeightConstraints = textViewHeightConstraints;
    NSLog(@"%@", self.textViewHeightConstraints);


    // Text view expand button
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[textView][expandButton(==12.0)]"
                                                                 options:NSLayoutFormatAlignAllTrailing
                                                                 metrics:nil
                                                                   views:@{@"textView": self.textView, @"expandButton": self.expandButton}]];

    // Separator
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[expandButton]-6-[separator]"
                                                                 options:NSLayoutFormatAlignAllTrailing
                                                                 metrics:nil
                                                                   views:@{@"expandButton": self.expandButton, @"separator": self.separator}]];

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"[separator(==textView)]"
                                                                 options:0
                                                                 metrics:nil
                                                                   views:@{@"separator": self.separator, @"textView": self.textView}]];

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[separator(==0.5)]"
                                                                 options:0
                                                                 metrics:nil
                                                                   views:@{@"separator": self.separator}]];

    NSString *leftVisualFormatString = @"V:[separator]-6-[informationLabel]-spacing-[languageDescriptionLabel]-spacing-[categoryDescriptionLabel]-spacing-[publisherDescriptionLabel]-spacing-[publishedDateDescriptionLabel]-spacing-[pageCountDescriptionLabel]-|";

    NSDictionary *descriptionViews = @{@"separator": self.separator,
                                       @"informationLabel": self.informationLabel,
                                       @"languageDescriptionLabel": self.languageDescriptionLabel,
                                       @"categoryDescriptionLabel": self.categoryDescriptionLabel,
                                       @"publisherDescriptionLabel": self.publisherDescriptionLabel,
                                       @"publishedDateDescriptionLabel": self.publishedDateDescriptionLabel,
                                       @"pageCountDescriptionLabel": self.pageCountDescriptionLabel};

    NSDictionary *metrics = @{@"spacing": @1.0};

    // All at once: vertical spacing and leading alignment for the labels on the left
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:leftVisualFormatString
                                                                 options:NSLayoutFormatAlignAllLeading
                                                                 metrics:metrics
                                                                   views:descriptionViews]];

    // Same, for the righthand labels

    NSDictionary *views = @{@"languageLabel": self.languageLabel,
                            @"categoryLabel": self.categoryLabel,
                            @"publisherLabel": self.publisherLabel,
                            @"publishedDateLabel": self.publishedDateLabel,
                            @"pageCountLabel": self.pageCountLabel};

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"[pageCountDescriptionLabel]-20-[pageCountLabel]"
                                                                 options:0
                                                                 metrics:nil
                                                                   views:@{@"pageCountDescriptionLabel": self.pageCountDescriptionLabel,
                                                                           @"pageCountLabel": self.pageCountLabel}]];


    NSString *rightVisualFormatString = @"V:[languageLabel]-spacing-[categoryLabel]-spacing-[publisherLabel]-spacing-[publishedDateLabel]-spacing-[pageCountLabel]-|";


    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:rightVisualFormatString
                                                                 options:NSLayoutFormatAlignAllLeading
                                                                 metrics:metrics
                                                                   views:views]];
}

正如我所说,在我称之为这种方法之前一切正常:

- (void)tappedExpandButton:(id)sender
{
    if (self.textViewHeightConstraints.count == 1) {
        NSLayoutConstraint *constraint = self.textViewHeightConstraints.firstObject;

        CGFloat newHeight = [self.textView.text boundingRectWithSize:CGSizeMake(self.textView.textContainer.size.width, CGFLOAT_MAX)
                                                             options:NSStringDrawingUsesLineFragmentOrigin
                                                          attributes:@{NSFontAttributeName: self.textView.font}
                                                             context:nil].size.height;

        constraint.constant = ceilf(newHeight);
        [self layoutIfNeeded];
    }
}

谢谢!

1 个答案:

答案 0 :(得分:1)

我想我已经明白了。由于某种原因,我还不明白,添加约束时包含UIScrollView的框架已更改。这对我来说似乎仍然很奇怪,因为我认为只有它的contentSize应该改变,但是从暗区(UIView)到UIScrollView添加约束来修复它。这是代码:

[self.darkEffectView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[descriptionView]|"
                                                                                options:0
                                                                                metrics:nil
                                                                                  views:@{@"descriptionView": self.descriptionView}]];

[self.darkEffectView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[descriptionView]|"
                                                                                options:0
                                                                                metrics:nil
                                                                                  views:@{@"descriptionView": self.descriptionView}]];