27

UIViewController with UIView and UITableView

UIView
|-UITableView

I'm trying to setup margins like this:

- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.layoutMargins = UIEdgeInsetsMake(30, 30, 30, 30);
    self.tableView.preservesSuperviewLayoutMargins = YES;
    [self.view layoutIfNeeded];
}

but nothing is happening on the view.

Here are the constraints from InterfaceBuilder

(lldb) po self.view.constraints
<__NSArrayM 0x786ab6e0>(
<NSLayoutConstraint:0x7896e940 UIView:0x7896e470.trailingMargin == UITableView:0x79b51a00.trailing - 16>,
<NSLayoutConstraint:0x7896e970 UITableView:0x79b51a00.leading == UIView:0x7896e470.leadingMargin - 16>,
<NSLayoutConstraint:0x7896e9a0 V:[_UILayoutGuide:0x7896e510]-(0)-[UITableView:0x79b51a00]>,
<NSLayoutConstraint:0x7896e9d0 V:[UITableView:0x79b51a00]-(0)-[_UILayoutGuide:0x7896e600]>,
<_UILayoutSupportConstraint:0x7896c7d0 V:[_UILayoutGuide:0x7896e510(0)]>,
<_UILayoutSupportConstraint:0x7896c2b0 V:|-(0)-[_UILayoutGuide:0x7896e510]   (Names: '|':UIView:0x7896e470 )>,
<_UILayoutSupportConstraint:0x7896cbf0 V:[_UILayoutGuide:0x7896e600(0)]>,
<_UILayoutSupportConstraint:0x7896ea00 _UILayoutGuide:0x7896e600.bottom == UIView:0x7896e470.bottom>
)

as a result don't see any margins, nothing is changed at all.... What I'm issing ?

iOS 8

jemmons
  • 18,605
  • 8
  • 55
  • 84
Marcin
  • 3,694
  • 5
  • 32
  • 52
  • What if you use `setNeedsUpdateConstraints` before calling `layoutIfNeeded`? I have a feeling that changing `layoutMargins` does not mark constraints as dirty. – pronebird Dec 11 '14 at 13:46
  • See [here](https://stackoverflow.com/a/34690543/5175709). There is a non-iOS11 solution. I'm just sure if it's a recommended one... – mfaani Dec 03 '17 at 00:48

2 Answers2

52

Custom layoutMargins don't work on a view that is the root to a UIViewController instance. Those are defined by the system and cannot be overridden. You need to add another subview that will hold all of your content, you can then modify the layout margins on this new "contentView"

Update:

Discussion

Use this property to specify the desired amount of space (measured in points) between the edge of the view and any subviews. Auto layout uses your margins as a cue for placing content. For example, if you specify a set of horizontal constraints using the format string “|-[subview]-|”, the left and right edges of the subview are inset from the edge of the superview by the corresponding layout margins. When the edge of your view is close to the edge of the superview and the preservesSuperviewLayoutMargins property is YES, the actual layout margins may be increased to prevent content from overlapping the superview’s margins.

The default margins are eight points on each side.

If the view is a view controller’s root view, the system sets and manages the margins. The top and bottom margins are set to zero points. The side margins vary depending on the current size class, but can be either 16 or 20 points. You cannot change these margins.

Community
  • 1
  • 1
the Reverend
  • 12,305
  • 10
  • 66
  • 121
  • Nice -- worked like a charm! Do you have a source for this? I'd love to read up some more on it. – Matt Fenwick Feb 24 '16 at 18:50
  • 1
    I don't know how to get a link to the documentation inside Xcode, but I will copy paste it from the UIView documentation. See above – the Reverend Feb 26 '16 at 18:15
  • 7
    I really fail to see the reasoning behind Apple's implementation of this. What's the point in having the sides adjust based on the device but the top and bottom remain the same when you generally want a consistent edge all the way around your content and why default the 'constrain to margins' tickbox to true when adding constraints when it's so useless. – Mark Bridges Jun 04 '16 at 14:00
  • 2
    there is an increasing lot of things that are not making sense on iOS. This is one example, and customizing the appearance of a UITableView is another. – the Reverend Jun 06 '16 at 22:48
  • 6
    I really don't understand why Apple did this. I often write constraints programmatically that pin the edges of a subview to the edges of its parent view. The expectation is that margins can be used to control the edge padding of these views. Unfortunately if the view happens to be a root view it won't work. – AtlasMeh-ed Aug 05 '16 at 00:50
  • I am just trying to find a way to programmatically get the "default margins" (of "The default margins are eight points on each side." above ) without using layoutMargins, which can change based on superview. Sometimes I want to pin to the superView, but still follow readable width. I need the default margins to do this, and I would prefer to get them without hardcoding them. – SAHM Feb 28 '17 at 21:25
  • Cause it does not seem to work for views that do not use autolayout, – Anton Tropashko Jul 17 '17 at 14:20
  • @SAHM, Isn't `self.view.layoutMargins` (self: `UIViewController`) working? – Iulian Onofrei Aug 08 '17 at 10:37
  • 1. contrary to your answer I was able to change in iOS **10**. See [here](https://stackoverflow.com/a/34690543/5175709). I was able to change my `layoutMargins` from `viewDidLayoutSubview` 2. I actually thought that there won't be any restrictions on changing a subview's layoutMargin upon its instantiation, but there actually seems that such restrictions are placed on subviews a well (just as IulianOnofrei said; @@Anton: I did use constraints), because non of my modifications worked. The only place where changing a subviews margins worked was again in `viewDidLayoutSubviews` @IulianOnofrei – mfaani Dec 03 '17 at 00:46
  • Well, maybe you can force-change it, but I don't think it's a good idea. Either change them only on iOS 11, either use a custom `UIView` with your desired layout margins for the safest approach. Also, I also change my layout margins only in `viewDidLayoutSubviews`. – Iulian Onofrei Dec 03 '17 at 18:48
19

This has been changed for iOS 11. Custom margins seems working very well when having deployment target setup for this iOS version. One day we will laugh at what we experienced before.

If you want to setup values lower than a system minimum (which may vary based on a device) - you have to also set viewRespectsSystemMinimumLayoutMargins for false (it's true by default).

If you are targeting iOS 10 and lower you are out of luck, @Reverend answer is correct - no layout margin customizations for viewcontroller's root view.

Viktor Kucera
  • 6,177
  • 4
  • 32
  • 43
  • 3
    This is actually the other way around I think. Default is true. Set `viewRespectsSystemMinimumLayoutMargins` to false to set your own margins. – Tricky Sep 19 '17 at 13:58
  • 5
    `viewRespectsSystemMinimumLayoutMargins` seems to work for `UIViewController` but I can't get it to work with `UITableViewController`. I'm able to set the table view margins to a higher value than the default (e.g. 64), but I can't set them lower, even when `viewRespectsSystemMinimumLayoutMargins` is set to `false`. Does anyone else see this behavior? Seems like a bug. – Greg Brown Nov 02 '17 at 12:55