Skip to content

Commit

Permalink
Actual code 🎉
Browse files Browse the repository at this point in the history
  • Loading branch information
PimCoumans committed Aug 19, 2022
1 parent 08c9a98 commit 612bbae
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 31 deletions.
43 changes: 22 additions & 21 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,26 @@
import PackageDescription

let package = Package(
name: "ConstraintBuilder",
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "ConstraintBuilder",
targets: ["ConstraintBuilder"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "ConstraintBuilder",
dependencies: []),
.testTarget(
name: "ConstraintBuilderTests",
dependencies: ["ConstraintBuilder"]),
]
name: "ConstraintBuilder",
platforms: [
.iOS(.v11),
.tvOS(.v9),
.macCatalyst(.v13),
.macOS(.v10_11)
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "ConstraintBuilder",
targets: ["ConstraintBuilder"]),
],
dependencies: [],
targets: [
.target(
name: "ConstraintBuilder",
dependencies: []),
.testTarget(
name: "ConstraintBuilderTests",
dependencies: ["ConstraintBuilder"]),
]
)
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# ConstraintBuilder

A description of this package.
Convenience Auto Layout methods, applying constraints through a function builder

```swift
someView.applyConstraints {
$0.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor)
$0.centerYAnchor.constraint(equalTo: contentView.layoutMarginsGuide.centerYAnchor)
}
```
51 changes: 48 additions & 3 deletions Sources/ConstraintBuilder/ConstraintBuilder.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,51 @@
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif

@resultBuilder
public struct ConstraintBuilder {
public private(set) var text = "Hello, World!"
static func buildBlock(_ components: NSLayoutConstraint...) -> [NSLayoutConstraint] {
Array(components)
}
static func buildBlock(_ components: [NSLayoutConstraint]) -> [NSLayoutConstraint] {
Array(components)
}
}

public extension NSLayoutConstraint {
/// Activates all constraints created in the `builder` closure;
/// - Parameter builder: Closure in which all constraints should be created or referenced
///
/// Typical usage:
/// ```swift
/// NSLayoutConstraint.build {
/// someView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor)
/// someView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor)
/// anotherView.leadingAnchor.constraint(equalTo: someView.trailingAnchor)
/// anotherView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor)
/// }
/// ```
class func build(@ConstraintBuilder _ builder: () -> [NSLayoutConstraint]) {
activate(builder())
}
}

public init() {
}
public protocol ContraintBuildable {
/// Extends all edges to the edges of the provided view
/// - Parameter view: View of which edges should be extended to
func extend(to view: Self)

/// Aligns center with center of provided view
/// - Parameter view: View of which center should be aligned to
func center(in view: Self)

/// Extends all edges to the edges of the superview
/// Should result in `assertionFailure` when no superview is available
func extendToSuperview()

/// Aligns center with center of superview
/// Should result in `assertionFailure` when no superview is available
func centerInSuperview()
}
94 changes: 94 additions & 0 deletions Sources/ConstraintBuilder/UIView+ConstraintBuildable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#if canImport(UIKit)
import UIKit

public protocol UIViewConstraintBuildable: ContraintBuildable {
/// Create and activate constriants with this view as the main subject
/// ```swift
/// view.applyConstraints {
/// $0.leadingAnchor.constraint(equalTo: otherView.leadingAnchor)
/// $0.centerYAnchor.constraint(equalTo: otherView.centerYAnchor)
/// }
/// ```
/// - Parameter builder: Constraint builder to add the constriants from
func applyConstraints(@ConstraintBuilder _ builder: (UIView) -> [NSLayoutConstraint])

/// Extends all edges to provided layout guide
/// - Parameter view: UILayoutGuide of which edges should be extended to
func extend(to layoutGuide: UILayoutGuide)

/// Aligns center with center of layout guide
/// - Parameter view: UILayoutGuide of which center should be aligned to
func center(in layoutGuide: UILayoutGuide)

/// Extends all edges to the edges of the superview‘s safe area
/// Should result in `assertionFailure` when no superview is available
func extendToSuperviewSafeArea()

/// Extends all edges to the edges of the superview‘s layout margins
/// Should result in `assertionFailure` when no superview is available
func extendToSuperviewLayoutMargins()
}

extension UIView: UIViewConstraintBuildable {
public func applyConstraints(@ConstraintBuilder _ builder: (UIView) -> [NSLayoutConstraint]) {
NSLayoutConstraint.activate(builder(self))
}

public func extend(to view: UIView) {
NSLayoutConstraint.build {
leadingAnchor.constraint(equalTo: view.leadingAnchor)
trailingAnchor.constraint(equalTo: view.trailingAnchor)
topAnchor.constraint(equalTo: view.topAnchor)
bottomAnchor.constraint(equalTo: view.bottomAnchor)
}
}

public func center(in view: UIView) {
NSLayoutConstraint.build {
centerXAnchor.constraint(equalTo: view.centerXAnchor)
centerYAnchor.constraint(equalTo: view.centerYAnchor)
}
}

public func extend(to layoutGuide: UILayoutGuide) {
NSLayoutConstraint.build {
leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor)
trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor)
topAnchor.constraint(equalTo: layoutGuide.topAnchor)
bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor)
}
}

public func center(in layoutGuide: UILayoutGuide) {
NSLayoutConstraint.build {
centerXAnchor.constraint(equalTo: layoutGuide.centerXAnchor)
centerYAnchor.constraint(equalTo: layoutGuide.centerYAnchor)
}
}

/// Use superview if available, `assertionFailure()` if not
private func withSuperview(_ method: (UIView) -> Void) {
guard let superview = superview else {
return assertionFailure()
}
method(superview)
}

public func extendToSuperview() {
withSuperview(extend(to:))
}

public func centerInSuperview() {
withSuperview(center(in:))
}

public func extendToSuperviewSafeArea() {
withSuperview { extend(to: $0.safeAreaLayoutGuide) }
}

public func extendToSuperviewLayoutMargins() {
withSuperview { extend(to: $0.layoutMarginsGuide) }
}
}

#endif
6 changes: 0 additions & 6 deletions Tests/ConstraintBuilderTests/ConstraintBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,4 @@ import XCTest
@testable import ConstraintBuilder

final class ConstraintBuilderTests: XCTestCase {
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct
// results.
XCTAssertEqual(ConstraintBuilder().text, "Hello, World!")
}
}

0 comments on commit 612bbae

Please sign in to comment.