iosobjective-cxcodeuiviewlayoutmargins

Why are the margins of the root view and a subview with the same frame different?


I am simply trying to create a colored border along the margins of two subviews, one of which is defined in ViewController and the other in a UIView subclass. Both are subviews of the root view. I would like to make the blue border from the second subview (created in UIView subclass) match the red border from the first subview (created in ViewController).

ViewController (root view)

- (void)viewDidLoad {
    [super viewDidLoad];    
    self.view.backgroundColor = [UIColor whiteColor];

    // add border subview (red)
    UILayoutGuide *margins = self.view.layoutMarginsGuide;
    self.view.translatesAutoresizingMaskIntoConstraints = NO;
    UIView *l3 = [[UIView alloc] init];
    [self.view addSubview:l3];
    l3.layer.borderWidth = (CGFloat)1.0;
    l3.layer.borderColor = UIColor.systemRedColor.CGColor;
    l3.translatesAutoresizingMaskIntoConstraints = NO;
    [l3.trailingAnchor constraintEqualToAnchor:margins.trailingAnchor].active = YES;
    [l3.leadingAnchor constraintEqualToAnchor:margins.leadingAnchor].active = YES;
    [l3.topAnchor constraintEqualToAnchor:margins.topAnchor].active = YES;
    [l3.bottomAnchor constraintEqualToAnchor:margins.bottomAnchor].active = YES;

    // add border subview from LibraryView class (blue)
    LibraryView *library = [[LibraryView alloc] initWithFrame:self.view.frame];
    [self.view addSubview:library];
}

LibraryView (subview, UIView subclass)

@implementation LibraryView
// LibraryView inherits from UIView

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self != nil)
    {
        // add margins of this LibraryView object
        UILayoutGuide *margins = self.layoutMarginsGuide;
        self.layer.borderWidth = (CGFloat)1.0;
        self.layer.borderColor = UIColor.systemBlueColor.CGColor;
        self.translatesAutoresizingMaskIntoConstraints = NO;
        [self.trailingAnchor constraintEqualToAnchor:margins.trailingAnchor].active = YES;
        [self.leadingAnchor constraintEqualToAnchor:margins.leadingAnchor].active = YES;
        [self.topAnchor constraintEqualToAnchor:margins.topAnchor].active = YES;
        [self.bottomAnchor constraintEqualToAnchor:margins.bottomAnchor].active = YES;
    }
    return self;
}

@end

Result

iOS Simulator screenshot

Error

<UILayoutGuide: 0x600000a282a0 - "UIViewLayoutMarginsGuide", layoutFrame = {{0, 0}, {0, 0}}, owningView = <LibraryView: 0x144f06c70; frame = (0 0; 393 852); layer = <CALayer: 0x60000336dc80>>>
2023-04-22 10:51:48.573386-0400 VoiceMemos[1043:18123] [LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x60000102bd40 H:[UILayoutGuide:0x600000a282a0'UIViewLayoutMarginsGuide']-(0)-|   (active, names: '|':LibraryView:0x144f06c70 )>",
    "<NSLayoutConstraint:0x600001012580 'UIView-rightMargin-guide-constraint' H:[UILayoutGuide:0x600000a282a0'UIViewLayoutMarginsGuide']-(8)-|(LTR)   (active, names: '|':LibraryView:0x144f06c70 )>"
)

Questions/Issues

  1. As you can see, only the red border renders because the blue border fails to render due to the error (and several others just like it for the other anchors) shown above.
  2. Is this convention correct? And if not, is even possible the way I have implemented it? i.e. trying to match the margins of a subview of the root view in ViewController and a subview from a UIView subclass (LibraryView)

Apologies if this is a misguided question. iOS development is a bit tricky conceptually, and I am trying to do it in Objective-C so online resources are more limited. Thanks in advance.


Solution

  • The problem is with your initWithFrame of LibraryView. You are attempting to set the library view's constraints to itself.

    Remove all of the constraint related code from LibraryView. You want to set the constraints in the viewDidLoad of the root view controller.

    After the line:

    [self.view addSubview:library];
    

    you can add code to setup the constraints of library against the same margins variable used to setup l3.


    Also, it's easier to active a bunch of constraints as follows:

    [NSLayoutConstraint activateConstraints:@[
        [l3.trailingAnchor constraintEqualToAnchor:margins.trailingAnchor],
        [l3.leadingAnchor constraintEqualToAnchor:margins.leadingAnchor],
        [l3.topAnchor constraintEqualToAnchor:margins.topAnchor],
        [l3.bottomAnchor constraintEqualToAnchor:margins.bottomAnchor],
    ]];