I've been trying different ways on the Playground, but the margins are not being implemented. I'm trying to understand the proper way of using UIEdgeInsets
(and NSDirectionalEdgeInsets
).
The expectation is that the super view should have margins of specified size and the subview should be within that margin, sort of like an invisible border.
Following didn't show any margins for the superview:
import UIKit
import PlaygroundSupport
let rootView = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
rootView.layoutMargins = UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100)
let subview = UIView()
subview.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
subview.widthAnchor.constraint(equalToConstant: 100),
subview.heightAnchor.constraint(equalToConstant: 100)
])
subview.backgroundColor = .red
rootView.addSubview(subview)
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = rootView
I've also tried with:
rootView.layoutMargins.top = 100
or instead of Autolayout:
let subview = UIView(frame: CGRect(origin: .zero, size: .init(width: 100, height: 100)))
tried NSDirectionEdgeInsets
, but didn't work:
rootView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 100, leading: 100, bottom: 100, trailing: 100)
Finally, I tried it within a view controller, but still futile:
class MyVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let rootView = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
rootView.layoutMargins = UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100)
self.view.addSubview(rootView)
let subview = UIView()
subview.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
subview.widthAnchor.constraint(equalToConstant: 100),
subview.heightAnchor.constraint(equalToConstant: 100)
])
subview.backgroundColor = .red
rootView.addSubview(subview)
}
}
Layout margins aren't a substitute for constraints that would otherwise specify your view's position, they work alongside such constraints. The examples you've given contain no such constraints, so the layout margins aren't going have any effect. What's more, my guess is that setting explicit height and width constraints causes any layout margins to be ignored.
The layout showcased in this image was produced using the code below, which makes use of Auto Layout's Visual Format Language to produce the required constraints. The first constraints pin the vertical edges of the red view to the vertical edges of the gray view, whilst the second set does the same for the horizontal edges. Crucially, the format string contains hyphens that signal to the Auto Layout engine that any specified layout margins should also be respected, so the final layout has the red view's edges inset from the gray view's edges. If you remove these hyphens (e.g. |[redView]|
), the custom layout margins are ignored and you just get the red view flush up against the edges of the gray view.
override func viewDidLoad() {
super.viewDidLoad()
let grayView = UIView(frame: CGRect(x: 0, y: 100, width: 300, height: 500))
let customInsets = NSDirectionalEdgeInsets(top: 40, leading: 5, bottom: 20, trailing: 15)
grayView.directionalLayoutMargins = customInsets
grayView.backgroundColor = .lightGray
view.addSubview(grayView)
let redView = UIView()
redView.backgroundColor = .red
redView.translatesAutoresizingMaskIntoConstraints = false
grayView.addSubview(redView)
let vrtConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:|-[redView]-|", options: [], metrics: nil, views: ["redView":redView])
let hrzConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-[redView]-|", options: [], metrics: nil, views: ["redView":redView])
NSLayoutConstraint.activate(verticalEdgeConstraints + horizontalEdgeConstraints)
}
Of course, you don't have to use visual format strings to get the layout margins to be respected: you can also 'opt-in' via interface builder. This option is explored in Apple's Positioning Content With Layout Margins Guide