4

I create a custom IB-designable view (see code below) which renders correctly in IB and also works fine when running it. However, in get this warning about the view being misplaced and I cannot manually resize the view in Interface Builder (when touching a resize handle, the view will jump around in its container).

I get the same or similar behavior for all kinds of different layouts. Do you have an idea if I'm doing something wrong here, or is this just a bug in IB?

(PS: I cannot just ignore the warning)

Misplaced view warning

EDIT: added screenshot of constraints:

Constraints

Here is the code (header):



    IB_DESIGNABLE
    @interface AKATestView : UIView
    @end

Implementation:



    @interface AKATestView()

    @property(nonatomic)BOOL subviewsCreated;
    @property(nonatomic)BOOL subviewConstraintsCreated;
    @property(nonatomic)NSDictionary* views;

    @end

    @implementation AKATestView

    - (id)initWithCoder:(NSCoder *)aDecoder
    {
        self = [super initWithCoder:aDecoder];
        if (self) {
            [self setupAfterInit];
        }
        return self;
    }
    - (instancetype)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            [self setupAfterInit];
        }
        return self;
    }

    - (void)setupAfterInit
    {
        [self createSubviews];
    }

    - (void)createSubviews
    {
        if (!self.subviewsCreated)
        {
            self.translatesAutoresizingMaskIntoConstraints = NO;

            UILabel* labelView = [[UILabel alloc] initWithFrame:CGRectZero];
            labelView.text = @"Name";
            labelView.translatesAutoresizingMaskIntoConstraints = NO;
            [self addSubview:labelView];

            UITextField* textField = [[UITextField alloc] initWithFrame:CGRectZero];
            textField.borderStyle = UITextBorderStyleRoundedRect;
            textField.placeholder = @"Enter some text";
            textField.translatesAutoresizingMaskIntoConstraints = NO;
            [self addSubview:textField];

            UILabel* errorMessageLabel = [[UILabel alloc] initWithFrame:CGRectZero];
            errorMessageLabel.text = @"Error message";
            errorMessageLabel.translatesAutoresizingMaskIntoConstraints = NO;
            [self addSubview:errorMessageLabel];

            self.views = @{ @"label": labelView, @"editor": textField, @"errorMessageLabel": errorMessageLabel };
            self.subviewsCreated = YES;

            [self setNeedsUpdateConstraints];
        }
    }

    - (void)updateConstraints
    {
        if (!self.subviewConstraintsCreated)
        {
            NSDictionary* metrics =
            @{ @"pt": @(4), @"pr": @(4), @"pb": @(4), @"pl": @(4),
               @"labelWidth": @(100),
               @"errorPl": @(4 + 100 + 4),
               @"hsLabelEditor": @(4), @"vsEditorError": @(2)
               };
            NSArray* specs =
            @[ @{ @"format": @"H:|-(pl)-[label(labelWidth)]-(hsLabelEditor)-[editor]-(pr)-|",
                  @"options": @(NSLayoutFormatAlignAllFirstBaseline) },
               @{ @"format": @"V:|-(pt)-[editor]-(vsEditorError)-[errorMessageLabel]-(pb)-|",
                  @"options": @(NSLayoutFormatAlignAllLeading|NSLayoutFormatAlignAllTrailing) }
               ];
            for (NSDictionary* spec in specs)
            {
                NSString* format = spec[@"format"];
                NSUInteger options = ((NSNumber*)spec[@"options"]).unsignedIntegerValue;
                NSArray* constraints = [NSLayoutConstraint constraintsWithVisualFormat:format
                                                                               options:options
                                                                               metrics:metrics
                                                                                 views:self.views];
                [self addConstraints:constraints];
            }

            self.subviewConstraintsCreated = YES;
        }
        [super updateConstraints];
    }

    @end

Michael
  • 250
  • 3
  • 8
  • Watch out your constraints and springs. The implementation is not relevant to the question IMO, please include the constraints instead of it. – Maen Mar 20 '15 at 13:44
  • Added a screenshot with constraints, that's what you're looking for @Bigood? – Michael Mar 20 '15 at 13:49
  • Storyboard doesn't look at the constraints set from code. Only the ones you've specified in the storyboard. – Lord Zsolt Mar 20 '15 at 13:59
  • IB may not look at constraints set from code (?) but these constraints affect IB insofar as the height of the view is correctly computed according to the internal constraints. The view is also rendered correctly. I think you overlooked that the view is using live rendering (cf. IB_DESIGNABLE). – Michael Mar 20 '15 at 15:02
  • I'm getting an identical problem. I'll let you know if I find any solution. – ravron May 15 '15 at 19:06
  • I've found that if you do not add any constraints in your view code, this error disappears. However, it seems as if adding even one constraint causes it to appear. – ravron May 15 '15 at 20:24

1 Answers1

8

Try removing self.translatesAutoresizingMaskIntoConstraints = NO; in your createSubviews method. IB seems to be relying on this translation to come up with correct measurement on the designer. I had the exact same problem and this fixed it.

I still have translatesAutosizingMaskIntoConstraints to NO for subviews. I confirmed that there aren't any extra constraints generated even with this set to YES. Hope it's the case for you too!

nekonari
  • 480
  • 1
  • 6
  • 13
  • That seems to work, not that I understand why, but that's fine ;-) Thanks! – Michael Jul 25 '15 at 22:23
  • Yeah I don't understand fully, but after finding this out, I stopped setting `translatesAuto..` to `false` for all views with constraints added. I start turning it off with the leaf view and up until it works, and stop there. This seems to work well with IB. – nekonari Aug 13 '15 at 15:28