diff --git a/Example/SnapLayout.xcodeproj/project.pbxproj b/Example/SnapLayout.xcodeproj/project.pbxproj index 71a2190..1f60ffd 100644 --- a/Example/SnapLayout.xcodeproj/project.pbxproj +++ b/Example/SnapLayout.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0543392C1EA1FB1100044728 /* BaseTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0543392B1EA1FB1100044728 /* BaseTestCase.swift */; }; 05D8B3621E8E1F7F00E5819A /* SnapConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05D8B3611E8E1F7F00E5819A /* SnapConfigTests.swift */; }; 05D8B3641E8E1FAE00E5819A /* SnapManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05D8B3631E8E1FAE00E5819A /* SnapManagerTests.swift */; }; 05FA916D1E919EE90033BC64 /* SnapLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05FA916C1E919EE90033BC64 /* SnapLayoutTests.swift */; }; @@ -29,6 +30,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 0543392B1EA1FB1100044728 /* BaseTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTestCase.swift; sourceTree = ""; }; 05D8B3571E8E1D7C00E5819A /* SnapLayout_ExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SnapLayout_ExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 05D8B35B1E8E1D7C00E5819A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 05D8B3611E8E1F7F00E5819A /* SnapConfigTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnapConfigTests.swift; sourceTree = ""; }; @@ -78,6 +80,7 @@ 05D8B3631E8E1FAE00E5819A /* SnapManagerTests.swift */, 05FA916C1E919EE90033BC64 /* SnapLayoutTests.swift */, 05D8B35B1E8E1D7C00E5819A /* Info.plist */, + 0543392B1EA1FB1100044728 /* BaseTestCase.swift */, ); path = SnapLayout_ExampleTests; sourceTree = ""; @@ -356,6 +359,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0543392C1EA1FB1100044728 /* BaseTestCase.swift in Sources */, 05D8B3641E8E1FAE00E5819A /* SnapManagerTests.swift in Sources */, 05D8B3621E8E1F7F00E5819A /* SnapConfigTests.swift in Sources */, 05FA916D1E919EE90033BC64 /* SnapLayoutTests.swift in Sources */, diff --git a/Example/SnapLayout_ExampleTests/BaseTestCase.swift b/Example/SnapLayout_ExampleTests/BaseTestCase.swift new file mode 100644 index 0000000..e40d906 --- /dev/null +++ b/Example/SnapLayout_ExampleTests/BaseTestCase.swift @@ -0,0 +1,39 @@ +// +// BaseTestCase.swift +// SnapLayout +// +// Created by Satinder Singh on 4/14/17. +// Copyright © 2017 CocoaPods. All rights reserved. +// + +import UIKit +import XCTest + +/// Holds boilerplate properies subclasses may use for testing purposes. +/// Manages view hierarchy of childView and parentView relationship throughout test cases +class BaseTestCase: XCTestCase { + + /// Child View of parent View + let childView = UIView() + + /// Second child view of parent View + let childView2 = UIView() + + /// Parent View of Child View + let parentView = UIView() + + /// View that is neither a parent view nor child view + let view = UIView() + + override func setUp() { + super.setUp() + parentView.addSubview(childView) + parentView.addSubview(childView2) + } + + override func tearDown() { + childView.removeFromSuperview() + childView2.removeFromSuperview() + } + +} diff --git a/Example/SnapLayout_ExampleTests/SnapConfigTests.swift b/Example/SnapLayout_ExampleTests/SnapConfigTests.swift index 9ba2506..00b40c4 100644 --- a/Example/SnapLayout_ExampleTests/SnapConfigTests.swift +++ b/Example/SnapLayout_ExampleTests/SnapConfigTests.swift @@ -9,18 +9,9 @@ import XCTest import SnapLayout +/// Tests SnapConfig class SnapConfigTests: XCTestCase { - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - /// Test Snap Config Constructor func testSnapConfigConstructor() { let topConstant = CGFloat(0) diff --git a/Example/SnapLayout_ExampleTests/SnapLayoutTests.swift b/Example/SnapLayout_ExampleTests/SnapLayoutTests.swift index b9ef2df..28b817a 100644 --- a/Example/SnapLayout_ExampleTests/SnapLayoutTests.swift +++ b/Example/SnapLayout_ExampleTests/SnapLayoutTests.swift @@ -10,13 +10,11 @@ import UIKit import XCTest import SnapLayout -class SnapLayoutTests: XCTestCase { +/// Tests SnapLayout +class SnapLayoutTests: BaseTestCase { /// Tests Snap Layout where no constraints were applied. func testSnapInActiveError() { - let childView = UIView() - let superview = UIView() - superview.addSubview(childView) let snapManager = childView.snap() XCTAssertEqual(childView.translatesAutoresizingMaskIntoConstraints, false) XCTAssertNil(snapManager.top) @@ -31,58 +29,31 @@ class SnapLayoutTests: XCTestCase { /// Tests Snap Width method func testSnapWidth() { - let view1 = UIView() - let view2 = UIView() - let containerView = UIView() - containerView.addSubview(view1) - containerView.addSubview(view2) - let snapManager = view1.snapWidth(to: view2, multiplier: 0.5) - XCTAssertEqual(view1.translatesAutoresizingMaskIntoConstraints, false) - XCTAssertEqual(view2.translatesAutoresizingMaskIntoConstraints, true) + let snapManager = childView.snapWidth(to: childView2, multiplier: 0.5) + XCTAssertEqual(childView.translatesAutoresizingMaskIntoConstraints, false) + XCTAssertEqual(childView2.translatesAutoresizingMaskIntoConstraints, true) XCTAssertNotNil(snapManager.width) XCTAssertEqual(snapManager.width!.isActive, true) XCTAssertEqual(snapManager.width!.constant, 0) XCTAssertEqual(snapManager.width!.multiplier, 0.5) } - /// Tests Snap Width where constraint was not applied - func testsSnapWidthError() { - let view1 = UIView() - let snapManager = view1.snapWidth(multiplier: 0.5) - XCTAssertEqual(view1.translatesAutoresizingMaskIntoConstraints, true) - XCTAssertNil(snapManager.width) - } - /// Tets Snap Height method func testSnapHeight() { - let view1 = UIView() - let view2 = UIView() - let containerView = UIView() - containerView.addSubview(view1) - containerView.addSubview(view2) - let snapManager = view1.snapHeight(to: view2, multiplier: 0.5) - XCTAssertEqual(view1.translatesAutoresizingMaskIntoConstraints, false) - XCTAssertEqual(view2.translatesAutoresizingMaskIntoConstraints, true) + let snapManager = childView.snapHeight(to: childView2, multiplier: 0.5) + XCTAssertEqual(childView.translatesAutoresizingMaskIntoConstraints, false) + XCTAssertEqual(childView2.translatesAutoresizingMaskIntoConstraints, true) XCTAssertNotNil(snapManager.height) XCTAssertEqual(snapManager.height!.isActive, true) XCTAssertEqual(snapManager.height!.constant, 0) XCTAssertEqual(snapManager.height!.multiplier, 0.5) } - /// Tests Snap Height where constraint was not applied - func testsSnapHeightError() { - let view1 = UIView() - let snapManager = view1.snapHeight(multiplier: 0.5) - XCTAssertEqual(view1.translatesAutoresizingMaskIntoConstraints, true) - XCTAssertNil(snapManager.height) - } - /// Tests Snap Size method func testSnapSize() { let size = CGSize(width: 30, height: 40) - let view1 = UIView() - let snapManager = view1.snapSize(size: size) - XCTAssertEqual(view1.translatesAutoresizingMaskIntoConstraints, false) + let snapManager = view.snapSize(size: size) + XCTAssertEqual(view.translatesAutoresizingMaskIntoConstraints, false) XCTAssertNotNil(snapManager.width) XCTAssertNotNil(snapManager.height) XCTAssertEqual(snapManager.width!.isActive, true) @@ -96,14 +67,9 @@ class SnapLayoutTests: XCTestCase { /// Tests Snap Trailing View func testSnapTrailingView() { let trailingConstant = CGFloat(8) - let view1 = UIView() - let view2 = UIView() - let containerView = UIView() - containerView.addSubview(view1) - containerView.addSubview(view2) - let snapManager = view1.snap(trailingView: view2, constant: trailingConstant) - XCTAssertEqual(view1.translatesAutoresizingMaskIntoConstraints, false) - XCTAssertEqual(view2.translatesAutoresizingMaskIntoConstraints, true) + let snapManager = childView.snap(trailingView: childView2, constant: trailingConstant) + XCTAssertEqual(childView.translatesAutoresizingMaskIntoConstraints, false) + XCTAssertEqual(childView2.translatesAutoresizingMaskIntoConstraints, true) XCTAssertNotNil(snapManager.trailing) XCTAssertEqual(snapManager.trailing!.isActive, true) XCTAssertEqual(snapManager.trailing!.constant, trailingConstant) @@ -113,14 +79,9 @@ class SnapLayoutTests: XCTestCase { /// Tests Snap Leading View func testSnapLeadingView() { let leadingConstant = CGFloat(8) - let view1 = UIView() - let view2 = UIView() - let containerView = UIView() - containerView.addSubview(view1) - containerView.addSubview(view2) - let snapManager = view1.snap(leadingView: view2, constant: leadingConstant) - XCTAssertEqual(view1.translatesAutoresizingMaskIntoConstraints, false) - XCTAssertEqual(view2.translatesAutoresizingMaskIntoConstraints, true) + let snapManager = childView.snap(leadingView: childView2, constant: leadingConstant) + XCTAssertEqual(childView.translatesAutoresizingMaskIntoConstraints, false) + XCTAssertEqual(childView2.translatesAutoresizingMaskIntoConstraints, true) XCTAssertNotNil(snapManager.leading) XCTAssertEqual(snapManager.leading!.isActive, true) XCTAssertEqual(snapManager.leading!.constant, leadingConstant) @@ -130,14 +91,9 @@ class SnapLayoutTests: XCTestCase { /// Tests Snap Bottom View func testSnapBottomView() { let bottomConstant = CGFloat(8) - let view1 = UIView() - let view2 = UIView() - let containerView = UIView() - containerView.addSubview(view1) - containerView.addSubview(view2) - let snapManager = view1.snap(bottomView: view2, constant: bottomConstant) - XCTAssertEqual(view1.translatesAutoresizingMaskIntoConstraints, false) - XCTAssertEqual(view2.translatesAutoresizingMaskIntoConstraints, true) + let snapManager = childView.snap(bottomView: childView2, constant: bottomConstant) + XCTAssertEqual(childView.translatesAutoresizingMaskIntoConstraints, false) + XCTAssertEqual(childView2.translatesAutoresizingMaskIntoConstraints, true) XCTAssertNotNil(snapManager.bottom) XCTAssertEqual(snapManager.bottom!.isActive, true) XCTAssertEqual(snapManager.bottom!.constant, bottomConstant) @@ -147,14 +103,9 @@ class SnapLayoutTests: XCTestCase { /// Tests Snap Trailing View func testSnapTopView() { let topConstant = CGFloat(8) - let view1 = UIView() - let view2 = UIView() - let containerView = UIView() - containerView.addSubview(view1) - containerView.addSubview(view2) - let snapManager = view1.snap(topView: view2, constant: topConstant) - XCTAssertEqual(view1.translatesAutoresizingMaskIntoConstraints, false) - XCTAssertEqual(view2.translatesAutoresizingMaskIntoConstraints, true) + let snapManager = childView.snap(topView: childView2, constant: topConstant) + XCTAssertEqual(childView.translatesAutoresizingMaskIntoConstraints, false) + XCTAssertEqual(childView2.translatesAutoresizingMaskIntoConstraints, true) XCTAssertNotNil(snapManager.top) XCTAssertEqual(snapManager.top!.isActive, true) XCTAssertEqual(snapManager.top!.constant, topConstant) diff --git a/Example/SnapLayout_ExampleTests/SnapManagerTests.swift b/Example/SnapLayout_ExampleTests/SnapManagerTests.swift index 160c7a5..02edee5 100644 --- a/Example/SnapLayout_ExampleTests/SnapManagerTests.swift +++ b/Example/SnapLayout_ExampleTests/SnapManagerTests.swift @@ -10,7 +10,8 @@ import UIKit import XCTest import SnapLayout -class SnapManagerTests: XCTestCase { +/// Tests SnapManager +class SnapManagerTests: BaseTestCase { /// Test Snap Manager Constructor func testSnapManagerConstructor() { @@ -22,9 +23,6 @@ class SnapManagerTests: XCTestCase { let heightConstant = CGFloat(64) let centerXConstant = true let centerYConstant = true - let parentView = UIView() - let childView = UIView() - parentView.addSubview(childView) let snapManager = childView.snap(top: topConstant, leading: leadingConstant, bottom: bottomConstant, @@ -56,7 +54,6 @@ class SnapManagerTests: XCTestCase { func testSnapManagerConstructorWithoutSuperView() { let widthConstant = CGFloat(32) let heightConstant = CGFloat(64) - let view = UIView() let snapManager = view.snap(width: widthConstant, height: heightConstant) XCTAssertEqual(view.translatesAutoresizingMaskIntoConstraints, false) XCTAssertNotNil(snapManager.width) @@ -74,7 +71,6 @@ class SnapManagerTests: XCTestCase { /// Test Snap Manager Constructor where a error would occur through logging func testSnapManagerConstructorError() { - let view = UIView() let snapManager = view.snap(trailing: 0) XCTAssertEqual(view.translatesAutoresizingMaskIntoConstraints, false) XCTAssertNil(snapManager.top) @@ -89,13 +85,13 @@ class SnapManagerTests: XCTestCase { /// Test Snap Manager Constructor with view argument func testSnapManagerConstructorWithSnapConfigErrorForNotSharingSameViewHeirarchy() { - let view = UIView() + let containerViewSubview = UIView() let otherView = UIView() let containerView = UIView() - containerView.addSubview(view) + containerView.addSubview(containerViewSubview) containerView.addSubview(otherView) - let snapManager = view.snap(to: otherView, constants: .zero) - XCTAssertEqual(view.translatesAutoresizingMaskIntoConstraints, false) + let snapManager = containerViewSubview.snap(to: otherView, constants: .zero) + XCTAssertEqual(containerViewSubview.translatesAutoresizingMaskIntoConstraints, false) XCTAssertNotNil(snapManager.top) XCTAssertEqual(snapManager.top!.constant, 0) XCTAssertNotNil(snapManager.leading) @@ -112,9 +108,6 @@ class SnapManagerTests: XCTestCase { /// Test Snap Manager Constructor with a config parameter func testSnapManagerConstructorWithSnapConfig() { - let parentView = UIView() - let childView = UIView() - parentView.addSubview(childView) let topConstant = CGFloat(0) let leadingConstant = CGFloat(8) let bottomConstant = CGFloat(16) @@ -150,5 +143,153 @@ class SnapManagerTests: XCTestCase { XCTAssertNotNil(snapManager.centerY) XCTAssertEqual(snapManager.centerY!.constant, 0) } + + /// Test Snap Manager Config Constructor when used with chaining. + /// Ensures SnapManager information is not lost during chaining + func testSnapManagerConfigChainConstructor() { + let topConstant = CGFloat(0) + let leadingConstant = CGFloat(8) + let bottomConstant = CGFloat(16) + let trailingConstant = CGFloat(24) + let widthConstant = CGFloat(32) + let heightConstant = CGFloat(64) + let centerXConstant = true + let centerYConstant = true + let snapConfig = SnapConfig(top: topConstant, + leading: leadingConstant, + bottom: bottomConstant, + trailing: trailingConstant, + width: widthConstant, + height: heightConstant) + let centerConfig = SnapConfig(centerX: centerXConstant, centerY: centerYConstant) + let snapManager = childView.snap(constants: snapConfig) + .snap(constants: centerConfig) + XCTAssertNotNil(snapManager.top) + XCTAssertEqual(snapManager.top!.constant, topConstant) + XCTAssertNotNil(snapManager.leading) + XCTAssertEqual(snapManager.leading!.constant, leadingConstant) + XCTAssertNotNil(snapManager.bottom) + XCTAssertEqual(snapManager.bottom!.constant, bottomConstant) + XCTAssertNotNil(snapManager.trailing) + XCTAssertEqual(snapManager.trailing!.constant, trailingConstant) + XCTAssertNotNil(snapManager.width) + XCTAssertEqual(snapManager.width!.constant, widthConstant) + XCTAssertNotNil(snapManager.height) + XCTAssertEqual(snapManager.height!.constant, heightConstant) + XCTAssertNotNil(snapManager.centerX) + XCTAssertNotNil(snapManager.centerY) + } + + /// Tests Snap Manager width during chaining + func testSnapManagerWidthConstructor() { + let topConstant = CGFloat(10) + let widthMultiplier = CGFloat(0.5) + let snapManager = childView.snap(top: topConstant).snapWidth(to: parentView, multiplier: widthMultiplier) + XCTAssertNotNil(snapManager.top) + XCTAssertEqual(snapManager.top!.constant, topConstant) + XCTAssertNotNil(snapManager.width) + XCTAssertEqual(snapManager.width!.multiplier, widthMultiplier) + XCTAssertEqual(snapManager.width!.constant, 0) + } + + /// Tests Snap Manager height during chaining + func testSnapManagerHeightConstructor() { + let topConstant = CGFloat(10) + let heightMultiplier = CGFloat(0.5) + let snapManager = childView.snap(top: topConstant).snapHeight(to: parentView, multiplier: heightMultiplier) + XCTAssertNotNil(snapManager.top) + XCTAssertEqual(snapManager.top!.constant, topConstant) + XCTAssertNotNil(snapManager.height) + XCTAssertEqual(snapManager.height!.multiplier, heightMultiplier) + XCTAssertEqual(snapManager.height!.constant, 0) + } + + /// Tests Snap Manager Size during chaining + func testSnapManagerSizeConstructor() { + let bottomConstant = CGFloat(10) + let size = CGSize(width: 30, height: 40) + let snapManager = childView.snap(bottom: bottomConstant).snapSize(size: size) + XCTAssertEqual(childView.translatesAutoresizingMaskIntoConstraints, false) + XCTAssertNotNil(snapManager.bottom) + XCTAssertNotNil(snapManager.width) + XCTAssertNotNil(snapManager.height) + XCTAssertEqual(snapManager.bottom!.isActive, true) + XCTAssertEqual(snapManager.width!.isActive, true) + XCTAssertEqual(snapManager.height!.isActive, true) + XCTAssertEqual(snapManager.bottom!.constant, bottomConstant) + XCTAssertEqual(snapManager.width!.constant, size.width) + XCTAssertEqual(snapManager.height!.constant, size.height) + XCTAssertEqual(snapManager.width!.multiplier, 1.0) + XCTAssertEqual(snapManager.height!.multiplier, 1.0) + } + + /// Tests Snap Manager Trailing View during chaining + func testSnapManagerTrailingViewConstructor() { + let bottomConstant = CGFloat(10) + let trailingConstant = CGFloat(8) + let snapManager = childView.snap(bottom: bottomConstant).snap(trailingView: childView2, constant: trailingConstant) + XCTAssertEqual(childView.translatesAutoresizingMaskIntoConstraints, false) + XCTAssertEqual(childView2.translatesAutoresizingMaskIntoConstraints, true) + XCTAssertNotNil(snapManager.trailing) + XCTAssertNotNil(snapManager.bottom) + XCTAssertEqual(snapManager.trailing!.isActive, true) + XCTAssertEqual(snapManager.bottom!.isActive, true) + XCTAssertEqual(snapManager.trailing!.constant, trailingConstant) + XCTAssertEqual(snapManager.bottom!.constant, bottomConstant) + XCTAssertEqual(snapManager.trailing!.multiplier, 1.0) + XCTAssertEqual(snapManager.bottom!.multiplier, 1.0) + } + + /// Tests Snap Manager Leading View during chaining + func testSnapLeadingView() { + let bottomConstant = CGFloat(10) + let leadingConstant = CGFloat(8) + let snapManager = childView.snap(bottom: bottomConstant).snap(leadingView: childView2, constant: leadingConstant) + XCTAssertEqual(childView.translatesAutoresizingMaskIntoConstraints, false) + XCTAssertEqual(childView2.translatesAutoresizingMaskIntoConstraints, true) + XCTAssertNotNil(snapManager.leading) + XCTAssertNotNil(snapManager.bottom) + XCTAssertEqual(snapManager.leading!.isActive, true) + XCTAssertEqual(snapManager.bottom!.isActive, true) + XCTAssertEqual(snapManager.leading!.constant, leadingConstant) + XCTAssertEqual(snapManager.bottom!.constant, bottomConstant) + XCTAssertEqual(snapManager.leading!.multiplier, 1.0) + XCTAssertEqual(snapManager.bottom!.multiplier, 1.0) + } + + /// Tests Snap Manager Bottom View during chaining + func testSnapBottomView() { + let leadingConstant = CGFloat(8) + let bottomConstant = CGFloat(10) + let snapManager = childView.snap(leading: leadingConstant).snap(bottomView: childView2, constant: bottomConstant) + XCTAssertEqual(childView.translatesAutoresizingMaskIntoConstraints, false) + XCTAssertEqual(childView2.translatesAutoresizingMaskIntoConstraints, true) + XCTAssertNotNil(snapManager.bottom) + XCTAssertNotNil(snapManager.leading) + XCTAssertEqual(snapManager.bottom!.isActive, true) + XCTAssertEqual(snapManager.leading!.isActive, true) + XCTAssertEqual(snapManager.bottom!.constant, bottomConstant) + XCTAssertEqual(snapManager.leading!.constant, leadingConstant) + XCTAssertEqual(snapManager.bottom!.multiplier, 1.0) + XCTAssertEqual(snapManager.leading!.multiplier, 1.0) + } + + /// Tests Snap Manager Top View during chaining + func testSnapTopView() { + let leadingConstant = CGFloat(8) + let topConstant = CGFloat(8) + let snapManager = childView.snap(leading: leadingConstant).snap(topView: childView2, constant: topConstant) + XCTAssertEqual(childView.translatesAutoresizingMaskIntoConstraints, false) + XCTAssertEqual(childView2.translatesAutoresizingMaskIntoConstraints, true) + XCTAssertNotNil(snapManager.top) + XCTAssertNotNil(snapManager.leading) + XCTAssertEqual(snapManager.top!.isActive, true) + XCTAssertEqual(snapManager.leading!.isActive, true) + XCTAssertEqual(snapManager.top!.constant, topConstant) + XCTAssertEqual(snapManager.leading!.constant, leadingConstant) + XCTAssertEqual(snapManager.top!.multiplier, 1.0) + XCTAssertEqual(snapManager.leading!.multiplier, 1.0) + } + } diff --git a/SnapLayout/Classes/SnapLayout.swift b/SnapLayout/Classes/SnapLayout.swift index 0f45089..7d5247c 100644 --- a/SnapLayout/Classes/SnapLayout.swift +++ b/SnapLayout/Classes/SnapLayout.swift @@ -10,6 +10,8 @@ public extension UIView { /// Snap to view based on argument values. + /// - Warning: Since all parameters have default values, it is possible to call `snap()` with no parameters. + /// This is **strongly** discouraged. /// /// - Parameters: /// - view: UIView to to apply constraints with (defaulted to superview if nil) @@ -103,11 +105,7 @@ public extension UIView { /// - multiplier: Multiplier value to apply constraint with (default 1) /// - Returns: SnapManager holding all the values associated with constraints @discardableResult - func snapWidth(to view: UIView? = nil, multiplier: CGFloat = 1) -> SnapManager { - guard let view = view ?? superview else { - print("SnapLayout Error - width constraint not applied for view: \(String(describing: self))") - return SnapManager() - } + func snapWidth(to view: UIView, multiplier: CGFloat = 1) -> SnapManager { translatesAutoresizingMaskIntoConstraints = false let snapManager = SnapManager(view: self) snapManager.width = widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: multiplier) @@ -122,11 +120,7 @@ public extension UIView { /// - multiplier: Multiplier value to apply constraint with (default 1) /// - Returns: SnapManager holding all the values associated with constraints @discardableResult - func snapHeight(to view: UIView? = nil, multiplier: CGFloat = 1) -> SnapManager { - guard let view = view ?? superview else { - print("SnapLayout Error - height constraint not applied for view: \(String(describing: self))") - return SnapManager() - } + func snapHeight(to view: UIView, multiplier: CGFloat = 1) -> SnapManager { translatesAutoresizingMaskIntoConstraints = false let snapManager = SnapManager(view: self) snapManager.height = heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: multiplier) diff --git a/SnapLayout/Classes/SnapManager.swift b/SnapLayout/Classes/SnapManager.swift index 2ba1d98..46348c0 100644 --- a/SnapLayout/Classes/SnapManager.swift +++ b/SnapLayout/Classes/SnapManager.swift @@ -34,17 +34,19 @@ public class SnapManager { public var centerY: NSLayoutConstraint? /// Internal reference to view - fileprivate weak var selfView: UIView? + fileprivate weak var weakView: UIView? /// Initalizes SnapManager with all properties set to nil - internal init() {} + fileprivate init() {} /// Initalizes the view property within `SnapManager` which allows for chaining to occur internal init(view: UIView) { - self.selfView = view + self.weakView = view } /// Snap to view based on argument values. + /// - Warning: Since all parameters have default values, it is possible to call `snap()` with no parameters. + /// This is **strongly** discouraged. /// /// - Parameters: /// - view: UIView to to apply constraints with (defaulted to superview if nil) @@ -62,19 +64,21 @@ public class SnapManager { public func snap(to view: UIView? = nil, top: CGFloat? = nil, leading: CGFloat? = nil, bottom: CGFloat? = nil, trailing: CGFloat? = nil, width: CGFloat? = nil, height: CGFloat? = nil, centerX: Bool? = nil, centerY: Bool? = nil) -> SnapManager { - guard let selfView = selfView else { + guard let weakView = weakView else { print("SnapLayout Error - Cannot apply constraint upon a view that is not retained") return SnapManager() } - return selfView.snap(to: view, - top: top, - leading: leading, - bottom: bottom, - trailing: trailing, - width: width, - height: height, - centerX: centerX, - centerY: centerY) + let newSnapManager = weakView.snap(to: view, + top: top, + leading: leading, + bottom: bottom, + trailing: trailing, + width: width, + height: height, + centerX: centerX, + centerY: centerY) + sync(with: newSnapManager) + return self } /// Snap to view based on argument values. @@ -104,12 +108,14 @@ public class SnapManager { /// - multiplier: Multiplier value to apply constraint with (default 1) /// - Returns: SnapManager holding all the values associated with constraints @discardableResult - public func snapWidth(to view: UIView? = nil, multiplier: CGFloat = 1) -> SnapManager { - guard let selfView = selfView else { + public func snapWidth(to view: UIView, multiplier: CGFloat = 1) -> SnapManager { + guard let weakView = weakView else { print("SnapLayout Error - Cannot apply constraint upon a view that is not retained") return SnapManager() } - return selfView.snapWidth(to: view, multiplier: multiplier) + let newSnapManager = weakView.snapWidth(to: view, multiplier: multiplier) + sync(with: newSnapManager) + return self } /// Apply height anchor between calling view and argument view with specified multiplier @@ -119,12 +125,14 @@ public class SnapManager { /// - multiplier: Multiplier value to apply constraint with (default 1) /// - Returns: SnapManager holding all the values associated with constraints @discardableResult - public func snapHeight(to view: UIView? = nil, multiplier: CGFloat = 1) -> SnapManager { - guard let selfView = selfView else { + public func snapHeight(to view: UIView, multiplier: CGFloat = 1) -> SnapManager { + guard let weakView = weakView else { print("SnapLayout Error - Cannot apply constraint upon a view that is not retained") return SnapManager() } - return selfView.snapHeight(to: view, multiplier: multiplier) + let newSnapManager = weakView.snapHeight(to: view, multiplier: multiplier) + sync(with: newSnapManager) + return self } /// Anchor size by applying width anchor and height anchor @@ -133,11 +141,13 @@ public class SnapManager { /// - Returns: SnapManager holding all the values associated with constraints @discardableResult public func snapSize(size: CGSize) -> SnapManager { - guard let selfView = selfView else { + guard let weakView = weakView else { print("SnapLayout Error - Cannot apply constraint upon a view that is not retained") return SnapManager() } - return selfView.snapSize(size: size) + let newSnapManager = weakView.snapSize(size: size) + sync(with: newSnapManager) + return self } /// Applies necessary constraint to ensure calling view will be leading view and the trailingView is on the trailing side. @@ -148,11 +158,13 @@ public class SnapManager { /// - Returns: SnapManager holding all the values associated with constraints @discardableResult public func snap(trailingView: UIView, constant: CGFloat = 0) -> SnapManager { - guard let selfView = selfView else { + guard let weakView = weakView else { print("SnapLayout Error - Cannot apply constraint upon a view that is not retained") return SnapManager() } - return selfView.snap(trailingView: trailingView, constant: constant) + let newSnapManager = weakView.snap(trailingView: trailingView, constant: constant) + sync(with: newSnapManager) + return self } /// Applies necessary constraint to ensure calling view will be trailing and the leadingView is on the leading side. @@ -163,11 +175,13 @@ public class SnapManager { /// - Returns: SnapManager holding all the values associated with constraints @discardableResult public func snap(leadingView: UIView, constant: CGFloat = 0) -> SnapManager { - guard let selfView = selfView else { + guard let weakView = weakView else { print("SnapLayout Error - Cannot apply constraint upon a view that is not retained") return SnapManager() } - return selfView.snap(leadingView: leadingView, constant: constant) + let newSnapManager = weakView.snap(leadingView: leadingView, constant: constant) + sync(with: newSnapManager) + return self } /// Applies necessary constraint to ensure calling view will be top view and the bottom view will be bottom view @@ -178,11 +192,13 @@ public class SnapManager { /// - Returns: SnapManager holding all the values associated with constraints @discardableResult public func snap(bottomView: UIView, constant: CGFloat = 0) -> SnapManager { - guard let selfView = selfView else { + guard let weakView = weakView else { print("SnapLayout Error - Cannot apply constraint upon a view that is not retained") return SnapManager() } - return selfView.snap(bottomView: bottomView, constant: constant) + let newSnapManager = weakView.snap(bottomView: bottomView, constant: constant) + sync(with: newSnapManager) + return self } /// Applies necessary constraint to ensure calling view will be bottom view and the top view will be top view @@ -193,11 +209,29 @@ public class SnapManager { /// - Returns: SnapManager holding all the values associated with constraints @discardableResult public func snap(topView: UIView, constant: CGFloat = 0) -> SnapManager { - guard let selfView = selfView else { + guard let weakView = weakView else { print("SnapLayout Error - Cannot apply constraint upon a view that is not retained") return SnapManager() } - return selfView.snap(topView: topView, constant: constant) + let newSnapManager = weakView.snap(topView: topView, constant: constant) + sync(with: newSnapManager) + return self + } + + /// Syncs two snapManagers together to ensure constraints are propogated between snapManagers. + /// Constraints from the other snapManager will update self's properties if non-nil. + /// - Note: other SnapManager will always take precedence over self's constraints. + /// + /// - Parameter other: SnapManager to update self with + fileprivate func sync(with other: SnapManager) { + top = other.top ?? top + leading = other.leading ?? leading + bottom = other.bottom ?? bottom + trailing = other.trailing ?? trailing + width = other.width ?? width + height = other.height ?? height + centerX = other.centerX ?? centerX + centerY = other.centerY ?? centerY } }