Skip to content

Commit

Permalink
Add margin constraints methods
Browse files Browse the repository at this point in the history
  • Loading branch information
tinder-cfuller committed Feb 19, 2024
1 parent 2a2d4c6 commit 0667828
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 1 deletion.
73 changes: 73 additions & 0 deletions Sources/Layout/UIKit/UIView+AutoLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
// See https://github.com/Tinder/Layout/blob/main/LICENSE for license information.
//

// swiftlint:disable file_length

import UIKit

extension UIView {
Expand Down Expand Up @@ -363,4 +365,75 @@ extension UIView {
constraint(toSuperview: .right, constant: -inset)
]
}

// MARK: - Margins

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

/// Creates constraints aligning the edges of the receiver to the margins 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 marginConstraints(
insets: DirectionalInsets
) -> [NSLayoutConstraint] {
assert(superview != nil, "marginConstraints(insets:) requires superview")
guard let superview: UIView
else { return [] }
return [
leading.constraint(equalTo: superview.margins.leading, constant: insets.leading),
trailing.constraint(equalTo: superview.margins.trailing, constant: -insets.trailing),
top.constraint(equalTo: superview.margins.top, constant: insets.top),
bottom.constraint(equalTo: superview.margins.bottom, constant: -insets.bottom)
]
}

/// Creates constraints aligning the edges of the receiver to the margins 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 marginConstraints(
insets: CanonicalInsets
) -> [NSLayoutConstraint] {
assert(superview != nil, "marginConstraints(insets:) requires superview")
guard let superview: UIView
else { return [] }
return [
left.constraint(equalTo: superview.margins.left, constant: insets.left),
right.constraint(equalTo: superview.margins.right, constant: -insets.right),
top.constraint(equalTo: superview.margins.top, constant: insets.top),
bottom.constraint(equalTo: superview.margins.bottom, constant: -insets.bottom)
]
}

/// Creates constraints aligning the left and right edges of the receiver to the corresponding margins of the
/// superview with an inset.
///
/// - Parameter inset: The inset value.
///
/// - Returns: The created constraints.
public func sideMarginConstraints(
inset: CGFloat = 0
) -> [NSLayoutConstraint] {
assert(superview != nil, "sideMarginConstraints(inset:) requires superview")
guard let superview: UIView
else { return [] }
return [
left.constraint(equalTo: superview.margins.left, constant: inset),
right.constraint(equalTo: superview.margins.right, constant: -inset)
]
}
}
134 changes: 133 additions & 1 deletion Tests/LayoutTests/UIKit/UIView+AutoLayoutTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// See https://github.com/Tinder/Layout/blob/main/LICENSE for license information.
//

import Layout
@testable import Layout
import Nimble
import UIKit
import XCTest
Expand Down Expand Up @@ -975,4 +975,136 @@ final class UIViewAutoLayoutTests: XCTestCase {
multiplier: 1,
constant: -inset)))
}

// MARK: - Margins

func testMarginConstraintsInset() {

// GIVEN

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

// WHEN

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

// THEN

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

func testMarginConstraintsInset_givenInset() {

// GIVEN

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

// WHEN

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

// THEN

expect(constraints.count) == 4
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)))
expect(constraints[2]).to(match(view.top.constraint(to: superview.margins.top, constant: inset)))
expect(constraints[3]).to(match(view.bottom.constraint(to: superview.margins.bottom, constant: -inset)))
}

func testMarginConstraintsInsetsDirectional() {

// 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.marginConstraints(insets: insets)

// THEN

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

func testMarginConstraintsInsetsCanonical() {

// 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.marginConstraints(insets: insets)

// THEN

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

func testSideMarginConstraintsInset() {

// GIVEN

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

// WHEN

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

// THEN

expect(constraints.count) == 2
expect(constraints[0]).to(match(view.left.constraint(to: superview.margins.left)))
expect(constraints[1]).to(match(view.right.constraint(to: superview.margins.right)))
}

func testSideMarginConstraintsInset_givenInset() {

// GIVEN

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

// WHEN

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

// THEN

expect(constraints.count) == 2
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)))
}
}
8 changes: 8 additions & 0 deletions cheatsheet.html
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,14 @@ <h3>Edges</h3>
<pre>edgeConstraints(insets: canonical)</pre>
<pre>sideEdgeConstraints()</pre>
<pre>sideEdgeConstraints(inset: 100)</pre>
<h3>Margins</h3>
<p>Constrains to superview margins.<p>
<pre>marginConstraints()</pre>
<pre>marginConstraints(inset: 100)</pre>
<pre>marginConstraints(insets: directional)</pre>
<pre>marginConstraints(insets: canonical)</pre>
<pre>sidemarginConstraints()</pre>
<pre>sidemarginConstraints(inset: 100)</pre>
<h2>Auto Layout</h2>
<h3>NSLayoutConstraint</h3>
<pre>activate()</pre>
Expand Down

0 comments on commit 0667828

Please sign in to comment.