Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Misplaced view warning and odd behavior with IB_DESIGNABLE custom view using auto layout

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

like image 876
Michael Avatar asked Feb 10 '23 15:02

Michael


1 Answers

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!

like image 90
nekonari Avatar answered Feb 13 '23 06:02

nekonari