From 9fcc87f28dc3e2a63e7dcd700b43928df27ed675 Mon Sep 17 00:00:00 2001 From: Blazej SLEBODA <5544365+Adobels@users.noreply.github.com> Date: Mon, 9 Oct 2023 22:28:48 +0200 Subject: [PATCH] Transform ibConstraints to global function (#4) * transform ibConstraints from UIView extension to global function, Rename NSLayoutConstraint.activate to NSLayoutConstraint.ibActivate and remove func apply * Update tests --- Sources/UIViewKit/NSObject+Extensions.swift | 19 ----- Sources/UIViewKit/UIViewDSL+Constraints.swift | 79 +++++++++---------- .../UIViewKitPreviewsDemo/HelloWordView.swift | 2 +- .../UserTitleTimeDescriptionView.swift | 7 +- ...tleTimeDescriptionViewWithIBSubviews.swift | 4 +- ...nViewWithIBSubviewsWithSuperviewView.swift | 4 +- ...ubviewHasConstraintsToItsSuperviewN2.swift | 2 +- .../ConstraintsBuilderTests.swift | 16 ++-- .../UIViewKitTests/HelloWorldViewTests.swift | 3 +- Tests/UIViewKitTests/IBConstraintsTests.swift | 18 ++--- .../NSLayoutConstraintTests.swift | 3 +- 11 files changed, 68 insertions(+), 89 deletions(-) delete mode 100644 Sources/UIViewKit/NSObject+Extensions.swift diff --git a/Sources/UIViewKit/NSObject+Extensions.swift b/Sources/UIViewKit/NSObject+Extensions.swift deleted file mode 100644 index 888e578..0000000 --- a/Sources/UIViewKit/NSObject+Extensions.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// NSObject+Extensions.swift -// UIViewKit -// -// Created by Blazej SLEBODA on 20/09/2023. -// - -import Foundation - -public protocol NSObjectExtensions {}; extension NSObject: NSObjectExtensions { } - -extension NSObjectExtensions { - - @discardableResult - public func ibApply(_ block: (Self) -> Void) -> Self { - block(self) - return self - } -} diff --git a/Sources/UIViewKit/UIViewDSL+Constraints.swift b/Sources/UIViewKit/UIViewDSL+Constraints.swift index 872cd2a..2ccbccc 100644 --- a/Sources/UIViewKit/UIViewDSL+Constraints.swift +++ b/Sources/UIViewKit/UIViewDSL+Constraints.swift @@ -7,23 +7,9 @@ import UIKit.NSLayoutConstraint -extension UIView { - - public func ibConstraints(to view: UIView, guide: LayoutGuide, anchors: ViewAnchor...) -> [NSLayoutConstraint] { - switch guide { - case .view: - return IBConstraints.generateConstraints(from: self, to: view, anchors: anchors) - case .viewMargins: - return IBConstraints.generateConstraints(from: self, to: view.layoutMarginsGuide, anchors: anchors) - case .viewSafeArea: - return IBConstraints.generateConstraints(from: self, to: view.safeAreaLayoutGuide, anchors: anchors) - } - } -} - extension NSLayoutConstraint { - static public func activate(@NSLayoutConstraintBuilder _ block: () -> [NSLayoutConstraint]) { + static public func ibActivate(@NSLayoutConstraintBuilder _ block: () -> [NSLayoutConstraint]) { NSLayoutConstraint.activate(block()) } @@ -46,36 +32,45 @@ extension NSLayoutConstraint { } } -enum IBConstraints { - static func generateConstraints(from view: UIView, to target: Any, anchors: [ViewAnchor]) -> [NSLayoutConstraint] { - var constraints: [NSLayoutConstraint] = [] - - for anchor in anchors { - switch anchor { - case .left(let value): - constraints.append(view.leftAnchor.constraint(equalTo: (target as? UILayoutGuide)?.leftAnchor ?? (target as! UIView).leftAnchor, constant: value)) - case .right(let value): - constraints.append(view.rightAnchor.constraint(equalTo: (target as? UILayoutGuide)?.rightAnchor ?? (target as! UIView).rightAnchor, constant: value)) - case .top(let value): - constraints.append(view.topAnchor.constraint(equalTo: (target as? UILayoutGuide)?.topAnchor ?? (target as! UIView).topAnchor, constant: value)) - case .bottom(let value): - constraints.append(view.bottomAnchor.constraint(equalTo: (target as? UILayoutGuide)?.bottomAnchor ?? (target as! UIView).bottomAnchor, constant: value)) - case .centerX(let value): - constraints.append(view.centerXAnchor.constraint(equalTo: (target as? UILayoutGuide)?.centerXAnchor ?? (target as! UIView).centerXAnchor, constant: value)) - case .centerY(let value): - constraints.append(view.centerYAnchor.constraint(equalTo: (target as? UILayoutGuide)?.centerYAnchor ?? (target as! UIView).centerYAnchor, constant: value)) - case .leading(let value): - constraints.append(view.leadingAnchor.constraint(equalTo: (target as? UILayoutGuide)?.leadingAnchor ?? (target as! UIView).leadingAnchor, constant: value)) - case .trailing(let value): - constraints.append(view.trailingAnchor.constraint(equalTo: (target as? UILayoutGuide)?.trailingAnchor ?? (target as! UIView).trailingAnchor, constant: value)) - case .all: - constraints.append(contentsOf: generateConstraints(from: view, to: target, anchors: [.top, .bottom, .left, .right])) - } +public func IBConstraints(from: UIView, to: UIView, guide: LayoutGuide, anchors: ViewAnchor...) -> [NSLayoutConstraint] { + switch guide { + case .view: + return generateConstraints(from: from, to: to, anchors: anchors) + case .viewMargins: + return generateConstraints(from: from, to: to.layoutMarginsGuide, anchors: anchors) + case .viewSafeArea: + return generateConstraints(from: from, to: to.safeAreaLayoutGuide, anchors: anchors) + } +} + +func generateConstraints(from view: UIView, to target: Any, anchors: [ViewAnchor]) -> [NSLayoutConstraint] { + var constraints: [NSLayoutConstraint] = [] + + for anchor in anchors { + switch anchor { + case .left(let value): + constraints.append(view.leftAnchor.constraint(equalTo: (target as? UILayoutGuide)?.leftAnchor ?? (target as! UIView).leftAnchor, constant: value)) + case .right(let value): + constraints.append(view.rightAnchor.constraint(equalTo: (target as? UILayoutGuide)?.rightAnchor ?? (target as! UIView).rightAnchor, constant: value)) + case .top(let value): + constraints.append(view.topAnchor.constraint(equalTo: (target as? UILayoutGuide)?.topAnchor ?? (target as! UIView).topAnchor, constant: value)) + case .bottom(let value): + constraints.append(view.bottomAnchor.constraint(equalTo: (target as? UILayoutGuide)?.bottomAnchor ?? (target as! UIView).bottomAnchor, constant: value)) + case .centerX(let value): + constraints.append(view.centerXAnchor.constraint(equalTo: (target as? UILayoutGuide)?.centerXAnchor ?? (target as! UIView).centerXAnchor, constant: value)) + case .centerY(let value): + constraints.append(view.centerYAnchor.constraint(equalTo: (target as? UILayoutGuide)?.centerYAnchor ?? (target as! UIView).centerYAnchor, constant: value)) + case .leading(let value): + constraints.append(view.leadingAnchor.constraint(equalTo: (target as? UILayoutGuide)?.leadingAnchor ?? (target as! UIView).leadingAnchor, constant: value)) + case .trailing(let value): + constraints.append(view.trailingAnchor.constraint(equalTo: (target as? UILayoutGuide)?.trailingAnchor ?? (target as! UIView).trailingAnchor, constant: value)) + case .all: + constraints.append(contentsOf: generateConstraints(from: view, to: target, anchors: [.top, .bottom, .left, .right])) } - - return constraints } + + return constraints } public enum LayoutGuide { diff --git a/Sources/UIViewKitPreviewsDemo/HelloWordView.swift b/Sources/UIViewKitPreviewsDemo/HelloWordView.swift index 165fb3f..e63304f 100644 --- a/Sources/UIViewKitPreviewsDemo/HelloWordView.swift +++ b/Sources/UIViewKitPreviewsDemo/HelloWordView.swift @@ -16,7 +16,7 @@ public class HelloWordView: UIView { super.init(frame: frame) self { UILabel().ibOutlet(&label).ibAttributes { - $0.ibConstraints(to: self, guide: .view, anchors: .centerX, .centerY) + IBConstraints(from: $0, to: self, guide: .view, anchors: .centerX, .centerY) $0.text = "Hello, world!" } } diff --git a/Sources/UIViewKitPreviewsDemo/UserTitleTimeDescriptionView/UserTitleTimeDescriptionView.swift b/Sources/UIViewKitPreviewsDemo/UserTitleTimeDescriptionView/UserTitleTimeDescriptionView.swift index 6417fe6..d06908a 100644 --- a/Sources/UIViewKitPreviewsDemo/UserTitleTimeDescriptionView/UserTitleTimeDescriptionView.swift +++ b/Sources/UIViewKitPreviewsDemo/UserTitleTimeDescriptionView/UserTitleTimeDescriptionView.swift @@ -15,9 +15,10 @@ public class UserTitleTimeDescriptionView: UIView { var labelTitle: UILabel! var labelDescription: UILabel! + var totoView: UIView! + override init(frame: CGRect) { super.init(frame: frame) - self { HorizontalStack(spacing: 12, alignment: .top) { UIImageView().ibOutlet(&imageViewUser).ibAttributes { @@ -61,7 +62,7 @@ public class UserTitleTimeDescriptionView: UIView { } } }.ibAttributes { - $0.ibConstraints(to: self, guide: .view, anchors: .top, .left(12), .right(-12), .bottom) + IBConstraints(from: $0, to: self, guide: .view, anchors: .top, .left(12), .right(-12), .bottom) } } } @@ -76,7 +77,7 @@ import SwiftUI struct UserTitleTimeDescriptionViewPreviews: PreviewProvider { static var previews: some View { ViewPreview( - UserTitleTimeDescriptionView().ibApply { + UserTitleTimeDescriptionView().ibAttributes { $0.imageViewUser.image = .init(systemName: "person.circle") $0.labelTitle.text = "Amanda Clarke" $0.labelTime.text = "1:08 PM" diff --git a/Sources/UIViewKitPreviewsDemo/UserTitleTimeDescriptionView/UserTitleTimeDescriptionViewWithIBSubviews.swift b/Sources/UIViewKitPreviewsDemo/UserTitleTimeDescriptionView/UserTitleTimeDescriptionViewWithIBSubviews.swift index d0e1667..2429a37 100644 --- a/Sources/UIViewKitPreviewsDemo/UserTitleTimeDescriptionView/UserTitleTimeDescriptionViewWithIBSubviews.swift +++ b/Sources/UIViewKitPreviewsDemo/UserTitleTimeDescriptionView/UserTitleTimeDescriptionViewWithIBSubviews.swift @@ -61,7 +61,7 @@ public class UserTitleTimeDescriptionViewWithIBSubviews: UIView { } } }.ibAttributes { - $0.ibConstraints(to: self, guide: .view, anchors: .top, .left(12), .right(-12), .bottom) + IBConstraints(from: $0, to: self, guide: .view, anchors: .top, .left(12), .right(-12), .bottom) } } } @@ -76,7 +76,7 @@ import SwiftUI struct UserTitleTimeDescriptionViewWithIBSubviewsPreviews: PreviewProvider { static var previews: some View { ViewPreview( - UserTitleTimeDescriptionViewWithIBSubviews().ibApply { + UserTitleTimeDescriptionViewWithIBSubviews().ibAttributes { $0.imageViewUser.image = .init(systemName: "person.circle") $0.labelTitle.text = "Amanda Clarke" $0.labelTime.text = "1:08 PM" diff --git a/Sources/UIViewKitPreviewsDemo/UserTitleTimeDescriptionView/UserTitleTimeDescriptionViewWithIBSubviewsWithSuperviewView.swift b/Sources/UIViewKitPreviewsDemo/UserTitleTimeDescriptionView/UserTitleTimeDescriptionViewWithIBSubviewsWithSuperviewView.swift index 8e53008..cc9a816 100644 --- a/Sources/UIViewKitPreviewsDemo/UserTitleTimeDescriptionView/UserTitleTimeDescriptionViewWithIBSubviewsWithSuperviewView.swift +++ b/Sources/UIViewKitPreviewsDemo/UserTitleTimeDescriptionView/UserTitleTimeDescriptionViewWithIBSubviewsWithSuperviewView.swift @@ -61,7 +61,7 @@ public class UserTitleTimeDescriptionViewWithIBSubviewsWithSuperviewView: UIView } } }.ibAttributes { - $0.ibConstraints(to: self, guide: .view, anchors: .top, .left(12), .right(-12), .bottom) + IBConstraints(from: $0, to: self, guide: .view, anchors: .top, .left(12), .right(-12), .bottom) } } } @@ -76,7 +76,7 @@ import SwiftUI struct UserTitleTimeDescriptionViewWithIBSubviewsWithSuperviewViewPreviews: PreviewProvider { static var previews: some View { ViewPreview( - UserTitleTimeDescriptionViewWithIBSubviewsWithSuperviewView().ibApply { + UserTitleTimeDescriptionViewWithIBSubviewsWithSuperviewView().ibAttributes { $0.imageViewUser.image = .init(systemName: "person.circle") $0.labelTitle.text = "Amanda Clarke" $0.labelTime.text = "1:08 PM" diff --git a/Sources/UIViewKitPreviewsDemo/ViewSubviewHasConstraintsToItsSuperviewN2.swift b/Sources/UIViewKitPreviewsDemo/ViewSubviewHasConstraintsToItsSuperviewN2.swift index e021d8d..dde7b74 100644 --- a/Sources/UIViewKitPreviewsDemo/ViewSubviewHasConstraintsToItsSuperviewN2.swift +++ b/Sources/UIViewKitPreviewsDemo/ViewSubviewHasConstraintsToItsSuperviewN2.swift @@ -19,7 +19,7 @@ class TestView: UIView { view2 { view3.ibAttributes { $0.tag = 3 - $0.ibConstraints(to: self, guide: .view, anchors: .all) + IBConstraints(from: $0, to: self, guide: .view, anchors: .all) $0.backgroundColor = .green $0.alpha = 0.3 } diff --git a/Tests/UIViewKitTests/ConstraintsBuilderTests.swift b/Tests/UIViewKitTests/ConstraintsBuilderTests.swift index 4c78a59..bfa1843 100644 --- a/Tests/UIViewKitTests/ConstraintsBuilderTests.swift +++ b/Tests/UIViewKitTests/ConstraintsBuilderTests.swift @@ -13,29 +13,29 @@ class ConstraintsBuilderTests: XCTestCase { func test() throws { let view = UIView() { sv in UIView().ibAttributes { - $0.ibConstraints(to: sv, guide: .view, anchors: .top) + IBConstraints(from: $0, to: sv, guide: .view, anchors: .top) if true { - $0.ibConstraints(to: sv, guide: .view, anchors: .left) + IBConstraints(from: $0, to: sv, guide: .view, anchors: .left) } else { - $0.ibConstraints(to: sv, guide: .view, anchors: .left) + IBConstraints(from: $0, to: sv, guide: .view, anchors: .left) } if false { - $0.ibConstraints(to: sv, guide: .view, anchors: .right) + IBConstraints(from: $0, to: sv, guide: .view, anchors: .right) } else { - $0.ibConstraints(to: sv, guide: .view, anchors: .right) + IBConstraints(from: $0, to: sv, guide: .view, anchors: .right) } if true { - $0.ibConstraints(to: sv, guide: .view, anchors: .bottom) + IBConstraints(from: $0, to: sv, guide: .view, anchors: .bottom) } print() $0.backgroundColor = .red } UIView().ibAttributes { #if DEBUG - $0.ibConstraints(to: sv, guide: .view, anchors: .top) + IBConstraints(from: $0, to: sv, guide: .view, anchors: .top) #endif if let _ = Optional(true) { - $0.ibConstraints(to: sv, guide: .view, anchors: .bottom) + IBConstraints(from: $0, to: sv, guide: .view, anchors: .bottom) } } } diff --git a/Tests/UIViewKitTests/HelloWorldViewTests.swift b/Tests/UIViewKitTests/HelloWorldViewTests.swift index abf184c..e761442 100644 --- a/Tests/UIViewKitTests/HelloWorldViewTests.swift +++ b/Tests/UIViewKitTests/HelloWorldViewTests.swift @@ -16,7 +16,8 @@ class HelloWorldViewTests: XCTestCase { let sut = SUT() let label = try XCTUnwrap(sut.subviews.first! as? UILabel) + XCTAssertEqual(label.superview, sut) XCTAssertEqual(label.text, "Hello, world!") + XCTAssertEqual(sut.constraints.count, 2) } - } diff --git a/Tests/UIViewKitTests/IBConstraintsTests.swift b/Tests/UIViewKitTests/IBConstraintsTests.swift index 50333ea..1af1558 100644 --- a/Tests/UIViewKitTests/IBConstraintsTests.swift +++ b/Tests/UIViewKitTests/IBConstraintsTests.swift @@ -17,15 +17,15 @@ class IBConstraintsTests: XCTestCase { view { subview.ibAttributes { - $0.ibConstraints(to: view, guide: .view, anchors: .all) - $0.ibConstraints(to: view, guide: .view, anchors: .top, .left, .right, .bottom) - $0.ibConstraints(to: view, guide: .view, anchors: .top(1), .left(1), .right(-1), .bottom(-1)) - $0.ibConstraints(to: view, guide: .view, anchors: .top, .leading, .trailing, .bottom) - $0.ibConstraints(to: view, guide: .view, anchors: .top(1), .leading(1), .trailing(-1), .bottom(-1)) - $0.ibConstraints(to: view, guide: .view, anchors: .centerX, .centerY) - $0.ibConstraints(to: view, guide: .view, anchors: .centerX(1), .centerY(1)) - $0.ibConstraints(to: view, guide: .viewMargins, anchors: .all) - $0.ibConstraints(to: view, guide: .viewSafeArea, anchors: .all) + IBConstraints(from: $0, to: view, guide: .view, anchors: .all) + IBConstraints(from: $0, to: view, guide: .view, anchors: .top, .left, .right, .bottom) + IBConstraints(from: $0, to: view, guide: .view, anchors: .top(1), .left(1), .right(-1), .bottom(-1)) + IBConstraints(from: $0, to: view, guide: .view, anchors: .top, .leading, .trailing, .bottom) + IBConstraints(from: $0, to: view, guide: .view, anchors: .top(1), .leading(1), .trailing(-1), .bottom(-1)) + IBConstraints(from: $0, to: view, guide: .view, anchors: .centerX, .centerY) + IBConstraints(from: $0, to: view, guide: .view, anchors: .centerX(1), .centerY(1)) + IBConstraints(from: $0, to: view, guide: .viewMargins, anchors: .all) + IBConstraints(from: $0, to: view, guide: .viewSafeArea, anchors: .all) } } } diff --git a/Tests/UIViewKitTests/NSLayoutConstraintTests.swift b/Tests/UIViewKitTests/NSLayoutConstraintTests.swift index ac89dd3..2e76c78 100644 --- a/Tests/UIViewKitTests/NSLayoutConstraintTests.swift +++ b/Tests/UIViewKitTests/NSLayoutConstraintTests.swift @@ -24,12 +24,13 @@ class NSLayoutConstraintTests: XCTestCase { UIView().ibOutlet(&viewSecond) } - NSLayoutConstraint.activate { + NSLayoutConstraint.ibActivate { viewFirst.topAnchor.constraint(equalTo: viewSecond.topAnchor).ibOutlet(&constraintTop).ibPriority(.defaultHigh) viewFirst.leftAnchor.constraint(equalTo: viewSecond.leftAnchor).ibOutlet(&constraintLeft).ibPriority(.defaultLow) viewFirst.rightAnchor.constraint(equalTo: viewSecond.rightAnchor) viewFirst.bottomAnchor.constraint(equalTo: viewSecond.bottomAnchor) } + XCTAssertEqual(view.constraints.count, 4) XCTAssertEqual(constraintTop.priority, UILayoutPriority.defaultHigh) XCTAssertEqual(constraintLeft.priority, UILayoutPriority.defaultLow)