Skip to content

Commit

Permalink
Add safe area constraints methods
Browse files Browse the repository at this point in the history
  • Loading branch information
tinder-cfuller committed Feb 24, 2024
1 parent 2d96c85 commit 81ad05b
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 0 deletions.
53 changes: 53 additions & 0 deletions Sources/Layout/UIKit/UIView+AutoLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -434,4 +434,57 @@ extension UIView {
right.constraint(to: superview.margins.right, constant: -inset)
]
}

// MARK: - Safe Area

/// Creates constraints aligning the edges of the receiver to the safe area of the superview with an inset.
///
/// - Parameter inset: The inset value.
///
/// - Returns: The created constraints.
public func safeAreaConstraints(
inset: CGFloat = 0
) -> [NSLayoutConstraint] {
safeAreaConstraints(insets: UIEdgeInsets(top: inset, left: inset, bottom: inset, right: inset))
}

/// Creates constraints aligning the edges of the receiver to the safe area of the superview with directional insets
/// ([`NSDirectionalEdgeInsets`](https://developer.apple.com/documentation/uikit/nsdirectionaledgeinsets)).
///
/// - Parameter insets: The directional insets.
///
/// - Returns: The created constraints.
public func safeAreaConstraints(
insets: DirectionalInsets
) -> [NSLayoutConstraint] {
assert(superview != nil, "safeAreaConstraints(insets:) requires superview")
guard let superview: UIView
else { return [] }
return [
leading.constraint(to: superview.safeArea.leading, constant: insets.leading),
trailing.constraint(to: superview.safeArea.trailing, constant: -insets.trailing),
top.constraint(to: superview.safeArea.top, constant: insets.top),
bottom.constraint(to: superview.safeArea.bottom, constant: -insets.bottom)
]
}

/// Creates constraints aligning the edges of the receiver to the safe area of the superview with canonical insets
/// ([`UIEdgeInsets`](https://developer.apple.com/documentation/uikit/uiedgeinsets)).
///
/// - Parameter insets: The canonical insets.
///
/// - Returns: The created constraints.
public func safeAreaConstraints(
insets: CanonicalInsets
) -> [NSLayoutConstraint] {
assert(superview != nil, "safeAreaConstraints(insets:) requires superview")
guard let superview: UIView
else { return [] }
return [
left.constraint(to: superview.safeArea.left, constant: insets.left),
right.constraint(to: superview.safeArea.right, constant: -insets.right),
top.constraint(to: superview.safeArea.top, constant: insets.top),
bottom.constraint(to: superview.safeArea.bottom, constant: -insets.bottom)
]
}
}
97 changes: 97 additions & 0 deletions Tests/LayoutTests/UIKit/UIView+AutoLayoutTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -987,4 +987,101 @@ final class UIViewAutoLayoutTests: XCTestCase {
expect(constraints[0]).to(match(view.left.constraint(to: superview.margins.left, constant: inset)))
expect(constraints[1]).to(match(view.right.constraint(to: superview.margins.right, constant: -inset)))
}

// MARK: - Safe Area

func testSageAreaConstraintsInset() {

// GIVEN

let superview: UIView = .init()
let view: UIView = .init()
superview.addSubview(view)

// WHEN

let constraints: [NSLayoutConstraint] = view.safeAreaConstraints()

// THEN

expect(constraints.count) == 4
expect(constraints[0]).to(match(view.left.constraint(to: superview.safeArea.left)))
expect(constraints[1]).to(match(view.right.constraint(to: superview.safeArea.right)))
expect(constraints[2]).to(match(view.top.constraint(to: superview.safeArea.top)))
expect(constraints[3]).to(match(view.bottom.constraint(to: superview.safeArea.bottom)))
}

func testSageAreaConstraintsInset_givenInset() {

// GIVEN

let superview: UIView = .init()
let view: UIView = .init()
superview.addSubview(view)
let inset: CGFloat = 10

// WHEN

let constraints: [NSLayoutConstraint] = view.safeAreaConstraints(inset: inset)

// THEN

expect(constraints.count) == 4
expect(constraints[0]).to(match(view.left.constraint(to: superview.safeArea.left, constant: inset)))
expect(constraints[1]).to(match(view.right.constraint(to: superview.safeArea.right, constant: -inset)))
expect(constraints[2]).to(match(view.top.constraint(to: superview.safeArea.top, constant: inset)))
expect(constraints[3]).to(match(view.bottom.constraint(to: superview.safeArea.bottom, constant: -inset)))
}

func testSageAreaConstraintsInsetsDirectional() {

// GIVEN

let superview: UIView = .init()
let view: UIView = .init()
superview.addSubview(view)
let insets: NSDirectionalEdgeInsets = .init(top: 1, leading: 2, bottom: 3, trailing: 4)

// WHEN

let constraints: [NSLayoutConstraint] = view.safeAreaConstraints(insets: insets)

// THEN

expect(constraints.count) == 4
expect(constraints[0])
.to(match(view.leading.constraint(to: superview.safeArea.leading, constant: insets.leading)))
expect(constraints[1])
.to(match(view.trailing.constraint(to: superview.safeArea.trailing, constant: -insets.trailing)))
expect(constraints[2])
.to(match(view.top.constraint(to: superview.safeArea.top, constant: insets.top)))
expect(constraints[3])
.to(match(view.bottom.constraint(to: superview.safeArea.bottom, constant: -insets.bottom)))
}

func testSageAreaConstraintsInsetsCanonical() {

// GIVEN

let superview: UIView = .init()
let view: UIView = .init()
superview.addSubview(view)
let insets: UIEdgeInsets = .init(top: 1, left: 2, bottom: 3, right: 4)

// WHEN

let constraints: [NSLayoutConstraint] = view.safeAreaConstraints(insets: insets)

// THEN

expect(constraints.count) == 4
expect(constraints[0])
.to(match(view.left.constraint(to: superview.safeArea.left, constant: insets.left)))
expect(constraints[1])
.to(match(view.right.constraint(to: superview.safeArea.right, constant: -insets.right)))
expect(constraints[2])
.to(match(view.top.constraint(to: superview.safeArea.top, constant: insets.top)))
expect(constraints[3])
.to(match(view.bottom.constraint(to: superview.safeArea.bottom, constant: -insets.bottom)))
}
}
6 changes: 6 additions & 0 deletions cheatsheet.html
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,12 @@ <h3>Margins</h3>
<pre>marginConstraints(insets: canonical)</pre>
<pre>sideMarginConstraints()</pre>
<pre>sideMarginConstraints(inset: 100)</pre>
<h3>Safe Area</h3>
<p>Constrains to superview safe area.<p>
<pre>safeAreaConstraints()</pre>
<pre>safeAreaConstraints(inset: 100)</pre>
<pre>safeAreaConstraints(insets: directional)</pre>
<pre>safeAreaConstraints(insets: canonical)</pre>
<h2>Auto Layout</h2>
<h3>NSLayoutConstraint</h3>
<pre>activate()</pre>
Expand Down

0 comments on commit 81ad05b

Please sign in to comment.