Skip to content

Commit 0bd4be0

Browse files
authored
Merge pull request #203 from TimothyChilvers/master
Add UILayoutSupport support
2 parents 435a0fc + a28cd12 commit 0bd4be0

6 files changed

Lines changed: 294 additions & 1 deletion

File tree

Cartography.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
4A3756C91D67445F0036190E /* LayoutSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66AF7EED1CABEABC00249E27 /* LayoutSupport.swift */; };
11+
4A3756CB1D6749190036190E /* LayoutSupportSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A3756CA1D6749180036190E /* LayoutSupportSpec.swift */; };
12+
4A3756CC1D6749190036190E /* LayoutSupportSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A3756CA1D6749180036190E /* LayoutSupportSpec.swift */; };
1013
541305B91B49598E003F1935 /* Matchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 541305B71B495986003F1935 /* Matchers.swift */; };
1114
541305BA1B49598F003F1935 /* Matchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 541305B71B495986003F1935 /* Matchers.swift */; };
1215
54143E4E1A93991D00208182 /* Distribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5467916E1A93962000DC9BF7 /* Distribute.swift */; };
@@ -115,6 +118,7 @@
115118
632F09531BF1F146002431A3 /* ViewHierarchySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB41A06E1A229BF7007142FE /* ViewHierarchySpec.swift */; };
116119
632F09541BF1F15C002431A3 /* Matchers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 541305B71B495986003F1935 /* Matchers.swift */; };
117120
632F09551BF1F15C002431A3 /* TestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54DE2B9E1ABE2BC5004D62D8 /* TestView.swift */; };
121+
66AF7EEE1CABEABC00249E27 /* LayoutSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66AF7EED1CABEABC00249E27 /* LayoutSupport.swift */; };
118122
BBAC6D131A22A27900E8A3E2 /* ViewUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBAC6D121A22A27900E8A3E2 /* ViewUtils.swift */; };
119123
BBAC6D141A22A3AC00E8A3E2 /* ViewUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBAC6D121A22A27900E8A3E2 /* ViewUtils.swift */; };
120124
BBAC6D1B1A22A8F300E8A3E2 /* ViewHierarchySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB41A06E1A229BF7007142FE /* ViewHierarchySpec.swift */; };
@@ -180,6 +184,7 @@
180184
/* End PBXCopyFilesBuildPhase section */
181185

182186
/* Begin PBXFileReference section */
187+
4A3756CA1D6749180036190E /* LayoutSupportSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutSupportSpec.swift; sourceTree = "<group>"; };
183188
541305B71B495986003F1935 /* Matchers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Matchers.swift; sourceTree = "<group>"; };
184189
54143E4F1A939D6000208182 /* DistributeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = DistributeSpec.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
185190
5435B8B41AD8610300B805F1 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Nimble.framework; sourceTree = "<group>"; };
@@ -224,6 +229,7 @@
224229
54FA3A431951A9730094B82A /* SizeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SizeSpec.swift; sourceTree = "<group>"; };
225230
632F090A1BF1E7AA002431A3 /* Cartography.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Cartography.framework; sourceTree = BUILT_PRODUCTS_DIR; };
226231
632F09221BF1E7FC002431A3 /* Cartography-tvOS-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Cartography-tvOS-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
232+
66AF7EED1CABEABC00249E27 /* LayoutSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutSupport.swift; sourceTree = "<group>"; };
227233
BB41A06E1A229BF7007142FE /* ViewHierarchySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewHierarchySpec.swift; sourceTree = "<group>"; };
228234
BBAC6D121A22A27900E8A3E2 /* ViewUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewUtils.swift; sourceTree = "<group>"; };
229235
/* End PBXFileReference section */
@@ -352,6 +358,7 @@
352358
546E9E941950A97F00B16707 /* Expression.swift */,
353359
54B093961A7165F2008A1102 /* Extensions.swift */,
354360
546E9E881950A29300B16707 /* LayoutProxy.swift */,
361+
66AF7EED1CABEABC00249E27 /* LayoutSupport.swift */,
355362
54FA3A3B1951A2B60094B82A /* Point.swift */,
356363
54FA3A351950FD320094B82A /* Priority.swift */,
357364
54C96A2F195064A6000CDD27 /* Property.swift */,
@@ -380,6 +387,7 @@
380387
54143E4F1A939D6000208182 /* DistributeSpec.swift */,
381388
546E9E921950A87600B16707 /* EdgeSpec.swift */,
382389
545F858E1953235F00791F75 /* EdgesSpec.swift */,
390+
4A3756CA1D6749180036190E /* LayoutSupportSpec.swift */,
383391
545F35831ACDFF6C00B57E98 /* MemoryLeakSpec.swift */,
384392
54FA3A3D1951A3750094B82A /* PointSpec.swift */,
385393
54FA3A371950FD8E0094B82A /* PrioritySpec.swift */,
@@ -671,6 +679,7 @@
671679
546E9E8F1950A33700B16707 /* Coefficients.swift in Sources */,
672680
54FA3A401951A41B0094B82A /* Compound.swift in Sources */,
673681
54B093951A715E52008A1102 /* ConstraintGroup.swift in Sources */,
682+
66AF7EEE1CABEABC00249E27 /* LayoutSupport.swift in Sources */,
674683
54BF29B01A9348B30066ED10 /* Align.swift in Sources */,
675684
546E9E951950A97F00B16707 /* Expression.swift in Sources */,
676685
);
@@ -684,6 +693,7 @@
684693
54FA3A441951A9730094B82A /* SizeSpec.swift in Sources */,
685694
54BF29B31A934F240066ED10 /* AlignSpec.swift in Sources */,
686695
54143E511A939D7000208182 /* DistributeSpec.swift in Sources */,
696+
4A3756CB1D6749190036190E /* LayoutSupportSpec.swift in Sources */,
687697
54FA3A3E1951A3750094B82A /* PointSpec.swift in Sources */,
688698
541305B91B49598E003F1935 /* Matchers.swift in Sources */,
689699
545F858F1953235F00791F75 /* EdgesSpec.swift in Sources */,
@@ -764,6 +774,7 @@
764774
632F09441BF1F127002431A3 /* Priority.swift in Sources */,
765775
632F09451BF1F127002431A3 /* Property.swift in Sources */,
766776
632F09461BF1F127002431A3 /* Size.swift in Sources */,
777+
4A3756C91D67445F0036190E /* LayoutSupport.swift in Sources */,
767778
632F09471BF1F127002431A3 /* View.swift in Sources */,
768779
632F09481BF1F127002431A3 /* ViewUtils.swift in Sources */,
769780
);
@@ -777,6 +788,7 @@
777788
632F09551BF1F15C002431A3 /* TestView.swift in Sources */,
778789
632F09491BF1F146002431A3 /* AlignSpec.swift in Sources */,
779790
632F094A1BF1F146002431A3 /* ConstraintGroupSpec.swift in Sources */,
791+
4A3756CC1D6749190036190E /* LayoutSupportSpec.swift in Sources */,
780792
632F094B1BF1F146002431A3 /* DimensionSpec.swift in Sources */,
781793
632F094C1BF1F146002431A3 /* DistributeSpec.swift in Sources */,
782794
632F094D1BF1F146002431A3 /* EdgeSpec.swift in Sources */,

Cartography/Compound.swift

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,85 @@ public func <= <P: RelativeCompoundInequality>(lhs: P, rhs: Expression<P>) -> [N
9494
public func >= <P: RelativeCompoundInequality>(lhs: P, rhs: Expression<P>) -> [NSLayoutConstraint] {
9595
return lhs.context.addConstraint(lhs, coefficients: rhs.coefficients, to: rhs.value, relation: NSLayoutRelation.GreaterThanOrEqual)
9696
}
97+
98+
#if os(iOS) || os(tvOS)
99+
100+
/// Declares a property equal to a layout support.
101+
///
102+
/// - parameter lhs: The affected property. The associated view will have
103+
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
104+
/// - parameter rhs: The layout support.
105+
///
106+
/// - returns: An `NSLayoutConstraint`.
107+
///
108+
109+
public func == <P: RelativeEquality>(lhs: P, rhs: LayoutSupport) -> NSLayoutConstraint {
110+
return lhs.context.addConstraint(lhs, to: rhs)
111+
}
112+
113+
/// Declares a property equal to the result of a layout support expression.
114+
///
115+
/// - parameter lhs: The affected property. The associated view will have
116+
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
117+
/// - parameter rhs: The layout support expression.
118+
///
119+
/// - returns: An `NSLayoutConstraint`.
120+
///
121+
122+
public func == <P: RelativeEquality>(lhs: P, rhs: Expression<LayoutSupport>) -> NSLayoutConstraint {
123+
return lhs.context.addConstraint(lhs, to: rhs.value, coefficients: rhs.coefficients[0])
124+
}
125+
126+
/// Declares a property greater than or equal to a layout support.
127+
///
128+
/// - parameter lhs: The affected property. The associated view will have
129+
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
130+
/// - parameter rhs: The layout support.
131+
///
132+
/// - returns: An `NSLayoutConstraint`.
133+
///
134+
135+
public func >= <P: RelativeEquality>(lhs: P, rhs: LayoutSupport) -> NSLayoutConstraint {
136+
return lhs.context.addConstraint(lhs, to: rhs, relation: NSLayoutRelation.GreaterThanOrEqual)
137+
}
138+
139+
/// Declares a property less than or equal to a layout support.
140+
///
141+
/// - parameter lhs: The affected property. The associated view will have
142+
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
143+
/// - parameter rhs: The layout support.
144+
///
145+
/// - returns: An `NSLayoutConstraint`.
146+
///
147+
148+
public func <= <P: RelativeEquality>(lhs: P, rhs: LayoutSupport) -> NSLayoutConstraint {
149+
return lhs.context.addConstraint(lhs, to: rhs, relation: NSLayoutRelation.LessThanOrEqual)
150+
}
151+
152+
/// Declares a property greater than or equal to the result of a layout support expression.
153+
///
154+
/// - parameter lhs: The affected property. The associated view will have
155+
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
156+
/// - parameter rhs: The layout support.
157+
///
158+
/// - returns: An `NSLayoutConstraint`.
159+
///
160+
161+
public func >= <P: RelativeEquality>(lhs: P, rhs: Expression<LayoutSupport>) -> NSLayoutConstraint {
162+
return lhs.context.addConstraint(lhs, to: rhs.value, coefficients: rhs.coefficients[0], relation: NSLayoutRelation.GreaterThanOrEqual)
163+
}
164+
165+
/// Declares a property less than or equal to the result of a layout support expression.
166+
///
167+
/// - parameter lhs: The affected property. The associated view will have
168+
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
169+
/// - parameter rhs: The layout support.
170+
///
171+
/// - returns: An `NSLayoutConstraint`.
172+
///
173+
174+
public func <= <P: RelativeEquality>(lhs: P, rhs: Expression<LayoutSupport>) -> NSLayoutConstraint {
175+
return lhs.context.addConstraint(lhs, to: rhs.value, coefficients: rhs.coefficients[0], relation: NSLayoutRelation.LessThanOrEqual)
176+
}
177+
178+
#endif

Cartography/Context.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,30 @@ import AppKit
1515
public class Context {
1616
internal var constraints: [Constraint] = []
1717

18+
#if os(iOS) || os(tvOS)
19+
20+
internal func addConstraint(from: Property, to: LayoutSupport, coefficients: Coefficients = Coefficients(), relation: NSLayoutRelation = .Equal) -> NSLayoutConstraint {
21+
from.view.car_translatesAutoresizingMaskIntoConstraints = false
22+
23+
let layoutConstraint = NSLayoutConstraint(item: from.view,
24+
attribute: from.attribute,
25+
relatedBy: relation,
26+
toItem: to.layoutGuide,
27+
attribute: to.attribute,
28+
multiplier: CGFloat(coefficients.multiplier),
29+
constant: CGFloat(coefficients.constant))
30+
31+
var view = from.view
32+
while let superview = view.superview {
33+
view = superview
34+
}
35+
constraints.append(Constraint(view: view, layoutConstraint: layoutConstraint))
36+
37+
return layoutConstraint
38+
}
39+
40+
#endif
41+
1842
internal func addConstraint(from: Property, to: Property? = nil, coefficients: Coefficients = Coefficients(), relation: NSLayoutRelation = .Equal) -> NSLayoutConstraint {
1943
from.view.car_translatesAutoresizingMaskIntoConstraints = false
2044

Cartography/LayoutSupport.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// LayoutSupport.swift
3+
// Cartography
4+
//
5+
// Created by Timothy Chilvers on 30/03/2016.
6+
// Copyright © 2016 Robert Böhnke. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
#if os(iOS) || os(tvOS)
12+
import UIKit
13+
14+
public struct LayoutSupport {
15+
let layoutGuide : UILayoutSupport
16+
let attribute : NSLayoutAttribute
17+
}
18+
19+
public extension UIViewController {
20+
21+
public var topLayoutGuideCartography : LayoutSupport {
22+
get {
23+
return LayoutSupport(layoutGuide: self.topLayoutGuide, attribute: .Bottom)
24+
}
25+
}
26+
27+
public var bottomLayoutGuideCartography : LayoutSupport {
28+
get {
29+
return LayoutSupport(layoutGuide: self.bottomLayoutGuide, attribute: .Top)
30+
}
31+
}
32+
}
33+
34+
#endif

Cartography/Property.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public func >= <P: RelativeInequality>(lhs: P, rhs: Expression<P>) -> NSLayoutCo
144144
return lhs.context.addConstraint(lhs, coefficients: rhs.coefficients[0], to: rhs.value, relation: NSLayoutRelation.GreaterThanOrEqual)
145145
}
146146

147-
// Mark: Addition
147+
// MARK: Addition
148148

149149
public protocol Addition : Property { }
150150

@@ -180,6 +180,17 @@ public func - <P: Addition>(lhs: Expression<P>, rhs: CGFloat) -> Expression<P> {
180180
return rhs - lhs
181181
}
182182

183+
#if os(iOS) || os(tvOS)
184+
185+
public func + (lhs: LayoutSupport, c : CGFloat) -> Expression<LayoutSupport> {
186+
return Expression<LayoutSupport>(lhs, [Coefficients(1, c)])
187+
}
188+
189+
public func - (lhs: LayoutSupport, c : CGFloat) -> Expression<LayoutSupport> {
190+
return lhs + (-c)
191+
}
192+
193+
#endif
183194
// MARK: Multiplication
184195

185196
public protocol Multiplication : Property { }
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import Cartography
2+
3+
import Nimble
4+
import Quick
5+
import UIKit
6+
7+
class LayoutSupportSpec: QuickSpec {
8+
override func spec() {
9+
var window: TestWindow!
10+
var view: TestView!
11+
var viewController: UIViewController!
12+
var navigationController: UINavigationController!
13+
var tabBarController: UITabBarController!
14+
15+
beforeEach {
16+
window = TestWindow(frame: CGRectMake(0,0,400,400))
17+
18+
view = TestView(frame: CGRectZero)
19+
20+
viewController = UIViewController()
21+
viewController.view.addSubview(view)
22+
23+
constrain(view) { view in
24+
view.height == 200
25+
view.width == 200
26+
}
27+
28+
navigationController = UINavigationController(rootViewController: viewController)
29+
tabBarController = UITabBarController()
30+
tabBarController.viewControllers = [navigationController]
31+
tabBarController.view.frame = window.bounds
32+
tabBarController.view.layoutIfNeeded()
33+
window.rootViewController = tabBarController
34+
35+
window.setNeedsLayout()
36+
window.layoutIfNeeded()
37+
38+
print(viewController.topLayoutGuide.debugDescription)
39+
}
40+
41+
describe("LayoutSupport.top") {
42+
it("should support relative equalities") {
43+
44+
viewController.view.layoutIfNeeded()
45+
46+
constrain(view) { view in
47+
view.top == viewController.topLayoutGuideCartography
48+
}
49+
viewController.view.layoutIfNeeded()
50+
51+
expect(view.convertRect(view.bounds, toView: window).minY).to(equal(viewController.topLayoutGuide.length))
52+
}
53+
54+
it("should support relative inequalities") {
55+
constrain(view) { view in
56+
view.top <= viewController.topLayoutGuideCartography
57+
view.top >= viewController.topLayoutGuideCartography
58+
}
59+
60+
viewController.view.layoutIfNeeded()
61+
62+
expect(view.convertRect(view.bounds, toView: window).minY).to(equal(viewController.topLayoutGuide.length))
63+
}
64+
65+
it("should support addition") {
66+
constrain(view) { view in
67+
view.top == viewController.topLayoutGuideCartography + 100
68+
}
69+
70+
viewController.view.layoutIfNeeded()
71+
72+
expect(view.convertRect(view.bounds, toView: window).minY).to(equal(100 + viewController.topLayoutGuide.length))
73+
}
74+
75+
it("should support subtraction") {
76+
constrain(view) { view in
77+
view.top == viewController.topLayoutGuideCartography - 100
78+
}
79+
80+
viewController.view.layoutIfNeeded()
81+
82+
expect(view.convertRect(view.bounds, toView: window).minY).to(equal(-100 - viewController.topLayoutGuide.length))
83+
}
84+
}
85+
86+
describe("LayoutSupport.bottom") {
87+
it("should support relative equalities") {
88+
constrain(view) { view in
89+
view.bottom == viewController.bottomLayoutGuideCartography
90+
}
91+
viewController.view.layoutIfNeeded()
92+
93+
expect(view.convertRect(view.bounds, toView: window).maxY).to(equal(window.bounds.maxY - viewController.bottomLayoutGuide.length))
94+
}
95+
96+
it("should support relative inequalities") {
97+
constrain(view) { view in
98+
view.bottom <= viewController.bottomLayoutGuideCartography
99+
view.bottom >= viewController.bottomLayoutGuideCartography
100+
}
101+
102+
viewController.view.layoutIfNeeded()
103+
104+
expect(view.convertRect(view.bounds, toView: window).maxY).to(equal(window.bounds.maxY - viewController.bottomLayoutGuide.length))
105+
}
106+
107+
it("should support addition") {
108+
constrain(view) { view in
109+
view.bottom == viewController.bottomLayoutGuideCartography + 100
110+
}
111+
112+
viewController.view.layoutIfNeeded()
113+
114+
expect(view.convertRect(view.bounds, toView: window).maxY).to(equal(100 + window.bounds.maxY - viewController.bottomLayoutGuide.length))
115+
}
116+
117+
it("should support subtraction") {
118+
constrain(view) { view in
119+
view.bottom == viewController.bottomLayoutGuideCartography - 100
120+
}
121+
122+
viewController.view.layoutIfNeeded()
123+
124+
expect(view.convertRect(view.bounds, toView: window).maxY).to(equal((window.bounds.maxY - 100) - viewController.bottomLayoutGuide.length))
125+
}
126+
127+
}
128+
129+
}
130+
}

0 commit comments

Comments
 (0)