diff --git a/Cartography.podspec b/Cartography.podspec index 54b03e6..237bf98 100644 --- a/Cartography.podspec +++ b/Cartography.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "Cartography" - s.version = "3.0.0" + s.version = "3.0.1" s.summary = "Declarative Auto Layout in Swift" s.description = <<-DESC @@ -13,7 +13,7 @@ Pod::Spec.new do |s| s.author = { "Robert Böhnke" => "robb@robb.is" } s.ios.deployment_target = "8.0" - s.osx.deployment_target = "10.9" + s.osx.deployment_target = "10.10" s.tvos.deployment_target = "9.0" s.source = { :git => "https://github.com/robb/Cartography.git", :tag => s.version } diff --git a/Cartography.xcodeproj/project.pbxproj b/Cartography.xcodeproj/project.pbxproj index fc05bc4..f7127ac 100644 --- a/Cartography.xcodeproj/project.pbxproj +++ b/Cartography.xcodeproj/project.pbxproj @@ -89,6 +89,7 @@ 977C36821F9FAC890057A057 /* AutoresizingMaskLayoutProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 977C36811F9FAC890057A057 /* AutoresizingMaskLayoutProxy.swift */; }; 977C36831F9FC2900057A057 /* AutoresizingMaskLayoutProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 977C36811F9FAC890057A057 /* AutoresizingMaskLayoutProxy.swift */; }; 977C36841F9FC2910057A057 /* AutoresizingMaskLayoutProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 977C36811F9FAC890057A057 /* AutoresizingMaskLayoutProxy.swift */; }; + 97885B1E1FC4F7C800BB4E51 /* LayoutGuideSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979F29C91F94DC6B00257363 /* LayoutGuideSpec.swift */; }; 979558E41F9700B40096BBEA /* LayoutGuideSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979F29C91F94DC6B00257363 /* LayoutGuideSpec.swift */; }; 979558EC1F97015E0096BBEA /* TestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D17C9E1F8E774700C57CE1 /* TestView.swift */; }; 979558ED1F97015E0096BBEA /* TestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D17C9E1F8E774700C57CE1 /* TestView.swift */; }; @@ -138,8 +139,8 @@ 97D17CAC1F8E779300C57CE1 /* LayoutGuideProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D17CA91F8E779300C57CE1 /* LayoutGuideProxy.swift */; }; 97E7F0A91F8D598A004857CE /* ViewProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97E7F0A81F8D598A004857CE /* ViewProxy.swift */; }; 97F50E521F962CF300C6DCF5 /* LayoutProxy+TypeErasure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97F50E511F962CF300C6DCF5 /* LayoutProxy+TypeErasure.swift */; }; - 97F50E541F9633AA00C6DCF5 /* SafeAreaLayoutGuideSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97F50E531F9633AA00C6DCF5 /* SafeAreaLayoutGuideSpec.swift */; }; - 97F50E551F9633AA00C6DCF5 /* SafeAreaLayoutGuideSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97F50E531F9633AA00C6DCF5 /* SafeAreaLayoutGuideSpec.swift */; }; + 97F50E541F9633AA00C6DCF5 /* ViewLayoutGuideSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97F50E531F9633AA00C6DCF5 /* ViewLayoutGuideSpec.swift */; }; + 97F50E551F9633AA00C6DCF5 /* ViewLayoutGuideSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97F50E531F9633AA00C6DCF5 /* ViewLayoutGuideSpec.swift */; }; 97F50E561F96401200C6DCF5 /* LayoutProxy+TypeErasure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97F50E511F962CF300C6DCF5 /* LayoutProxy+TypeErasure.swift */; }; 97F50E571F96401300C6DCF5 /* LayoutProxy+TypeErasure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97F50E511F962CF300C6DCF5 /* LayoutProxy+TypeErasure.swift */; }; A75B6143FF12C54FF3223B47 /* Pods_TestPods_Cartography_tvOS_tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0827A83361EACF1E6062607E /* Pods_TestPods_Cartography_tvOS_tests.framework */; }; @@ -259,7 +260,6 @@ 97D17C9F1F8E774700C57CE1 /* Matchers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Matchers.swift; sourceTree = ""; }; 97D17CA01F8E774700C57CE1 /* DistributeSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DistributeSpec.swift; sourceTree = ""; }; 97D17CA11F8E774700C57CE1 /* PointSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointSpec.swift; sourceTree = ""; }; - 97D17CA21F8E774700C57CE1 /* EdgesSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EdgesSpec.swift; sourceTree = ""; }; 97D17CA31F8E774700C57CE1 /* ViewHierarchySpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewHierarchySpec.swift; sourceTree = ""; }; 97D17CA41F8E774700C57CE1 /* DimensionSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DimensionSpec.swift; sourceTree = ""; }; 97D17CA51F8E774700C57CE1 /* MemoryLeakSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryLeakSpec.swift; sourceTree = ""; }; @@ -268,7 +268,7 @@ 97D17CA91F8E779300C57CE1 /* LayoutGuideProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutGuideProxy.swift; sourceTree = ""; }; 97E7F0A81F8D598A004857CE /* ViewProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewProxy.swift; sourceTree = ""; }; 97F50E511F962CF300C6DCF5 /* LayoutProxy+TypeErasure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LayoutProxy+TypeErasure.swift"; sourceTree = ""; }; - 97F50E531F9633AA00C6DCF5 /* SafeAreaLayoutGuideSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafeAreaLayoutGuideSpec.swift; sourceTree = ""; }; + 97F50E531F9633AA00C6DCF5 /* ViewLayoutGuideSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewLayoutGuideSpec.swift; sourceTree = ""; }; EE85314F1F9363DC003EC021 /* LayoutItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutItem.swift; sourceTree = ""; }; EEDD4098FF7503B1F9188F10 /* Pods_Cartography_iOS_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Cartography_iOS_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -467,13 +467,12 @@ 97D17C9F1F8E774700C57CE1 /* Matchers.swift */, 97D17CA01F8E774700C57CE1 /* DistributeSpec.swift */, 97D17CA11F8E774700C57CE1 /* PointSpec.swift */, - 97D17CA21F8E774700C57CE1 /* EdgesSpec.swift */, 97D17CA31F8E774700C57CE1 /* ViewHierarchySpec.swift */, 97D17CA41F8E774700C57CE1 /* DimensionSpec.swift */, 97D17CA51F8E774700C57CE1 /* MemoryLeakSpec.swift */, 97D17CA61F8E774700C57CE1 /* ConstraintGroupSpec.swift */, 97D17CA81F8E774700C57CE1 /* SizeSpec.swift */, - 97F50E531F9633AA00C6DCF5 /* SafeAreaLayoutGuideSpec.swift */, + 97F50E531F9633AA00C6DCF5 /* ViewLayoutGuideSpec.swift */, ); path = CartographyTests; sourceTree = SOURCE_ROOT; @@ -935,7 +934,7 @@ buildActionMask = 2147483647; files = ( 979558F61F97017D0096BBEA /* LayoutSupportSpec.swift in Sources */, - 97F50E541F9633AA00C6DCF5 /* SafeAreaLayoutGuideSpec.swift in Sources */, + 97F50E541F9633AA00C6DCF5 /* ViewLayoutGuideSpec.swift in Sources */, 9795590B1F9701CD0096BBEA /* ConstraintGroupSpec.swift in Sources */, 979559081F9701C90096BBEA /* MemoryLeakSpec.swift in Sources */, 979558FC1F97019E0096BBEA /* Matchers.swift in Sources */, @@ -1000,6 +999,7 @@ 9795590C1F9701CE0096BBEA /* ConstraintGroupSpec.swift in Sources */, 979559061F9701BC0096BBEA /* ViewHierarchySpec.swift in Sources */, 979558F91F9701860096BBEA /* PrioritySpec.swift in Sources */, + 97885B1E1FC4F7C800BB4E51 /* LayoutGuideSpec.swift in Sources */, 979558F21F97016C0096BBEA /* EdgesSpec.swift in Sources */, 979558ED1F97015E0096BBEA /* TestView.swift in Sources */, 979559091F9701C90096BBEA /* MemoryLeakSpec.swift in Sources */, @@ -1045,7 +1045,7 @@ buildActionMask = 2147483647; files = ( 979558F71F97017F0096BBEA /* LayoutSupportSpec.swift in Sources */, - 97F50E551F9633AA00C6DCF5 /* SafeAreaLayoutGuideSpec.swift in Sources */, + 97F50E551F9633AA00C6DCF5 /* ViewLayoutGuideSpec.swift in Sources */, 9795590D1F9701CF0096BBEA /* ConstraintGroupSpec.swift in Sources */, 9795590A1F9701CA0096BBEA /* MemoryLeakSpec.swift in Sources */, 979558FD1F9701A10096BBEA /* Matchers.swift in Sources */, diff --git a/Cartography/Edges.swift b/Cartography/Edges.swift index 7216ec6..2248270 100644 --- a/Cartography/Edges.swift +++ b/Cartography/Edges.swift @@ -17,9 +17,106 @@ public struct Edges: Compound, RelativeCompoundEquality, RelativeCompoundInequal public let properties: [Property] internal init(_ context: Context, _ properties: [Property]) { + guard properties.count == 4 else { + fatalError("No valid edges were used") + } + self.context = context self.properties = properties } + + /// Insets all edges individually. + /// + /// - parameter top: The amount by which to inset the top edge, in points. + /// - parameter leading: The amount by which to inset the leading edge, in points. + /// - parameter bottom: The amount by which to inset the bottom edge, in points. + /// - parameter trailing: The amount by which to inset the trailing edge, in points. + /// + /// - returns: A new expression with the inseted edges. + /// + public func inseted(top: CGFloat, leading: CGFloat, bottom: CGFloat, trailing: CGFloat) -> Expression { + return Expression( + self, + [ + Coefficients(1, top), + Coefficients(1, leading), + Coefficients(1, -bottom), + Coefficients(1, -trailing) + ] + ) + } + + /// Insets all horizontal and vertical edges. + /// + /// - parameter horizontally: The amount by which to inset the leading and trailing edges, in points. + /// - parameter vertically: The amount by which to inset the top and bottom edges, in points. + /// + /// - returns: A new expression with the inseted edges. + /// + public func inseted(horizontally: CGFloat, vertically: CGFloat) -> Expression { + return self.inseted( + top: vertically, + leading: horizontally, + bottom: vertically, + trailing: horizontally + ) + } + + /// Insets all horizontal edges. + /// + /// - parameter horizontally: The amount by which to inset the leading and trailing edges, in points. + /// + /// - returns: A new expression with the inseted edges. + /// + public func inseted(horizontally: CGFloat) -> Expression { + return self.inseted( + horizontally: horizontally, + vertically: 0 + ) + } + + /// Insets all vertical edges. + /// + /// - parameter vertically: The amount by which to inset the top and bottom edges, in points. + /// + /// - returns: A new expression with the inseted edges. + /// + public func inseted(vertically: CGFloat) -> Expression { + return self.inseted( + horizontally: 0, + vertically: vertically + ) + } + + /// Insets all edges by a single value. + /// + /// - parameter by: The amount by which to inset the top and bottom edges, in points. + /// + /// - returns: A new expression with the inseted edges. + /// + public func inseted(by value: CGFloat) -> Expression { + return self.inseted( + horizontally: value, + vertically: value + ) + } + + #if os(iOS) || os(tvOS) + /// Insets all edges individually using an existing UIEdgeInsets. + /// + /// - parameter by: The UIEdgeInsets to use as a base value. + /// + /// - returns: A new expression with the inseted edges. + /// + public func inseted(by insets: UIEdgeInsets) -> Expression { + return self.inseted( + top: insets.top, + leading: insets.left, + bottom: insets.bottom, + trailing: insets.right + ) + } + #endif } /// Insets all edges. @@ -30,7 +127,7 @@ public struct Edges: Compound, RelativeCompoundEquality, RelativeCompoundInequal /// - returns: A new expression with the inset edges. /// public func inset(_ edges: Edges, _ all: CGFloat) -> Expression { - return inset(edges, all, all, all, all) + return edges.inseted(by: all) } /// Insets the horizontal and vertical edges. @@ -44,7 +141,31 @@ public func inset(_ edges: Edges, _ all: CGFloat) -> Expression { /// - returns: A new expression with the inset edges. /// public func inset(_ edges: Edges, _ horizontal: CGFloat, _ vertical: CGFloat) -> Expression { - return inset(edges, vertical, horizontal, vertical, horizontal) + return edges.inseted(horizontally: horizontal, vertically: vertical) +} + +/// Insets the horizontal edges. +/// +/// - parameter edges: The edges to inset. +/// - parameter horizontally: The amount by which to inset the horizontal edges, in +/// points. +/// +/// - returns: A new expression with the inset edges. +/// +public func inset(_ edges: Edges, horizontally horizontal: CGFloat) -> Expression { + return edges.inseted(horizontally: horizontal) +} + +/// Insets the vertical edges. +/// +/// - parameter edges: The edges to inset. +/// - parameter vertically: The amount by which to inset the vertical edges, in +/// points. +/// +/// - returns: A new expression with the inset edges. +/// +public func inset(_ edges: Edges, vertically vertical: CGFloat) -> Expression { + return edges.inseted(vertically: vertical) } /// Insets edges individually. @@ -58,12 +179,7 @@ public func inset(_ edges: Edges, _ horizontal: CGFloat, _ vertical: CGFloat) -> /// - returns: A new expression with the inset edges. /// public func inset(_ edges: Edges, _ top: CGFloat, _ leading: CGFloat, _ bottom: CGFloat, _ trailing: CGFloat) -> Expression { - return Expression(edges, [ - Coefficients(1, top), - Coefficients(1, leading), - Coefficients(1, -bottom), - Coefficients(1, -trailing) - ]) + return edges.inseted(top: top, leading: leading, bottom: bottom, trailing: trailing) } #if os(iOS) || os(tvOS) @@ -75,6 +191,6 @@ public func inset(_ edges: Edges, _ top: CGFloat, _ leading: CGFloat, _ bottom: /// - returns: A new expression with the inset edges. /// public func inset(_ edges: Edges, _ insets: UIEdgeInsets) -> Expression { - return inset(edges, insets.top, insets.left, insets.bottom, insets.right) + return edges.inseted(by: insets) } #endif diff --git a/Cartography/LayoutGuide.swift b/Cartography/LayoutGuide.swift index df05ad5..a78a6f7 100644 --- a/Cartography/LayoutGuide.swift +++ b/Cartography/LayoutGuide.swift @@ -9,6 +9,10 @@ #if os(iOS) || os(tvOS) import UIKit +@available(iOS, introduced: 9.0) +@available(tvOS, introduced: 9.0) +public typealias LayoutGuide = UILayoutGuide + @available(iOS, introduced: 9.0) @available(tvOS, introduced: 9.0) extension UILayoutGuide: LayoutItem { @@ -19,4 +23,18 @@ extension UILayoutGuide: LayoutItem { return LayoutGuideProxy(context: context, item: self) } } +#elseif os(OSX) +import AppKit + +@available(OSX, introduced: 10.11) +public typealias LayoutGuide = NSLayoutGuide + +@available(OSX, introduced: 10.11) +extension NSLayoutGuide: LayoutItem { + + @available(OSX, introduced: 10.11) + public func asProxy(context: Context) -> LayoutGuideProxy { + return LayoutGuideProxy(context: context, item: self) + } +} #endif diff --git a/Cartography/LayoutGuideProxy.swift b/Cartography/LayoutGuideProxy.swift index 6e9816a..8b9e9ac 100644 --- a/Cartography/LayoutGuideProxy.swift +++ b/Cartography/LayoutGuideProxy.swift @@ -6,20 +6,18 @@ // Copyright © 2017 Robert Böhnke. All rights reserved. // -#if os(iOS) || os(tvOS) -import UIKit - @available(iOS, introduced: 9.0) @available(tvOS, introduced: 9.0) +@available(OSX, introduced: 10.11) public final class LayoutGuideProxy: SupportsPositioningLayoutProxy { public let context: Context - private let layoutGuide: UILayoutGuide + private let layoutGuide: LayoutGuide public var item: AnyObject { return layoutGuide } - public init(context: Context, item: UILayoutGuide) { + public init(context: Context, item: LayoutGuide) { self.context = context self.layoutGuide = item } @@ -28,4 +26,3 @@ public final class LayoutGuideProxy: SupportsPositioningLayoutProxy { return layoutGuide.owningView?.asProxy(context: context) } } -#endif diff --git a/Cartography/ViewProxy.swift b/Cartography/ViewProxy.swift index 58810a4..83d92cf 100644 --- a/Cartography/ViewProxy.swift +++ b/Cartography/ViewProxy.swift @@ -38,5 +38,17 @@ public final class ViewProxy: SupportsPositioningLayoutProxy, SupportsBaselineLa public var safeAreaLayoutGuide: LayoutGuideProxy { return view.safeAreaLayoutGuide.asProxy(context: context) } + + @available(iOS, introduced: 9.0) + @available(tvOS, introduced: 9.0) + public var layoutMarginsGuide: LayoutGuideProxy { + return view.layoutMarginsGuide.asProxy(context: context) + } + + @available(iOS, introduced: 9.0) + @available(tvOS, introduced: 9.0) + public var readableContentGuide: LayoutGuideProxy { + return view.readableContentGuide.asProxy(context: context) + } #endif } diff --git a/CartographyTests/EdgesSpec.swift b/CartographyTests/EdgesSpec.swift index 21aaaba..015c6e1 100644 --- a/CartographyTests/EdgesSpec.swift +++ b/CartographyTests/EdgesSpec.swift @@ -61,6 +61,26 @@ class EdgesSpec: QuickSpec { expect(view.frame).to(equal(CGRect(x: 20, y: 30, width: 360, height: 340))) } + it("should inset the horizontal edges") { + constrain(view) { view in + view.edges == inset(view.superview!.edges, horizontally: 20) + } + + window.layoutIfNeeded() + + expect(view.frame).to(equal(CGRect(x: 20, y: 0, width: 360, height: 400))) + } + + it("should inset the vertical edges") { + constrain(view) { view in + view.edges == inset(view.superview!.edges, vertically: 30) + } + + window.layoutIfNeeded() + + expect(view.frame).to(equal(CGRect(x: 0, y: 30, width: 400, height: 340))) + } + it("should inset all edges individually") { constrain(view) { view in view.edges == inset(view.superview!.edges, 10, 20, 30, 40) diff --git a/CartographyTests/LayoutGuideSpec.swift b/CartographyTests/LayoutGuideSpec.swift index 50bd9d2..020b093 100644 --- a/CartographyTests/LayoutGuideSpec.swift +++ b/CartographyTests/LayoutGuideSpec.swift @@ -8,23 +8,22 @@ import Quick import Nimble -import UIKit @testable import Cartography @available(iOS, introduced: 9.0) +@available(tvOS, introduced: 9.0) +@available(OSX, introduced: 10.11) final class LayoutGuideSpec: QuickSpec { override func spec() { - var view: UIView! - var layoutGuide: UILayoutGuide! + var view: View! + var layoutGuide: LayoutGuide! beforeEach { - view = TestView(frame: CGRect(x: 0, y: 0, width: 400, height: 400)) - layoutGuide = UILayoutGuide() - - constrain(view) { view in - view.width == 400.0 - view.height == 400.0 - } + view = View(frame: CGRect(x: 0, y: 0, width: 400, height: 400)) + #if os(OSX) + view.autoresizingMask = .none + #endif + layoutGuide = LayoutGuide() view.addLayoutGuide(layoutGuide) } @@ -35,12 +34,22 @@ final class LayoutGuideSpec: QuickSpec { layoutGuide.edges == layoutGuide.owningView!.edges } - view.layoutIfNeeded() - - expect(layoutGuide.layoutFrame.origin.x) == 0 - expect(layoutGuide.layoutFrame.origin.y) == 0 - expect(layoutGuide.layoutFrame.width) == 400 - expect(layoutGuide.layoutFrame.height) == 400 + #if os(iOS) || os(tvOS) + view.layoutIfNeeded() + + expect(layoutGuide.layoutFrame.origin.x) == 0 + expect(layoutGuide.layoutFrame.origin.y) == 0 + expect(layoutGuide.layoutFrame.width) == 400 + expect(layoutGuide.layoutFrame.height) == 400 + #elseif os(OSX) + view.needsLayout = true + view.layoutSubtreeIfNeeded() + + expect(layoutGuide.frame.origin.x) == 0 + expect(layoutGuide.frame.origin.y) == 0 + expect(layoutGuide.frame.width) == 400 + expect(layoutGuide.frame.height) == 400 + #endif } it("should support inequalities") { @@ -49,18 +58,30 @@ final class LayoutGuideSpec: QuickSpec { layoutGuide.bottom <= layoutGuide.owningView!.bottom layoutGuide.left >= layoutGuide.owningView!.left - layoutGuide.left <= layoutGuide.owningView!.left + layoutGuide.right <= layoutGuide.owningView!.right + + layoutGuide.center == layoutGuide.owningView!.center ~ .defaultLow layoutGuide.width == 200 layoutGuide.height == 200 } - view.layoutIfNeeded() - - expect(layoutGuide.layoutFrame.origin.x) == 0 - expect(layoutGuide.layoutFrame.origin.y) == 0 - expect(layoutGuide.layoutFrame.width) == 200 - expect(layoutGuide.layoutFrame.height) == 200 + #if os(iOS) || os(tvOS) + view.layoutIfNeeded() + + expect(layoutGuide.layoutFrame.midX) == 200 + expect(layoutGuide.layoutFrame.midY) == 200 + expect(layoutGuide.layoutFrame.width) == 200 + expect(layoutGuide.layoutFrame.height) == 200 + #elseif os(OSX) + view.needsLayout = true + view.layoutSubtreeIfNeeded() + + expect(layoutGuide.frame.midX) == 200 + expect(layoutGuide.frame.midY) == 200 + expect(layoutGuide.frame.width) == 200 + expect(layoutGuide.frame.height) == 200 + #endif } it("should support addition") { @@ -68,9 +89,16 @@ final class LayoutGuideSpec: QuickSpec { layoutGuide.leading == layoutGuide.owningView!.leading + 10 } - view.layoutIfNeeded() + #if os(iOS) || os(tvOS) + view.layoutIfNeeded() - expect(layoutGuide.layoutFrame.minX) == 10 + expect(layoutGuide.layoutFrame.minX) == 10 + #elseif os(OSX) + view.needsLayout = true + view.layoutSubtreeIfNeeded() + + expect(layoutGuide.frame.minX) == 10 + #endif } it("should support subtraction") { @@ -78,9 +106,16 @@ final class LayoutGuideSpec: QuickSpec { layoutGuide.trailing == layoutGuide.owningView!.trailing - 10 } - view.layoutIfNeeded() + #if os(iOS) || os(tvOS) + view.layoutIfNeeded() + + expect(layoutGuide.layoutFrame.maxX) == 390 + #elseif os(OSX) + view.needsLayout = true + view.layoutSubtreeIfNeeded() - expect(layoutGuide.layoutFrame.maxX) == 390 + expect(layoutGuide.frame.maxX) == 390 + #endif } it("should support multiplication") { @@ -88,9 +123,16 @@ final class LayoutGuideSpec: QuickSpec { layoutGuide.width == 0.5 * layoutGuide.owningView!.width } - view.layoutIfNeeded() + #if os(iOS) || os(tvOS) + view.layoutIfNeeded() + + expect(layoutGuide.layoutFrame.width) == 0.5 * view.frame.width + #elseif os(OSX) + view.needsLayout = true + view.layoutSubtreeIfNeeded() - expect(layoutGuide.layoutFrame.width) == 0.5 * view.frame.width + expect(layoutGuide.frame.width) == 0.5 * view.frame.width + #endif } it("should support division") { @@ -98,9 +140,16 @@ final class LayoutGuideSpec: QuickSpec { layoutGuide.width == layoutGuide.owningView!.width / 2 } - view.layoutIfNeeded() + #if os(iOS) || os(tvOS) + view.layoutIfNeeded() - expect(layoutGuide.layoutFrame.width) == view.frame.width / 2 + expect(layoutGuide.layoutFrame.width) == view.frame.width / 2 + #elseif os(OSX) + view.needsLayout = true + view.layoutSubtreeIfNeeded() + + expect(layoutGuide.frame.width) == view.frame.width / 2 + #endif } it("should support centering") { @@ -108,10 +157,18 @@ final class LayoutGuideSpec: QuickSpec { layoutGuide.center == layoutGuide.owningView!.center } - view.layoutIfNeeded() + #if os(iOS) || os(tvOS) + view.layoutIfNeeded() + + expect(layoutGuide.layoutFrame.midX) == 200 + expect(layoutGuide.layoutFrame.midY) == 200 + #elseif os(OSX) + view.needsLayout = true + view.layoutSubtreeIfNeeded() - expect(layoutGuide.layoutFrame.midX) == 200 - expect(layoutGuide.layoutFrame.midY) == 200 + expect(layoutGuide.frame.midX) == 200 + expect(layoutGuide.frame.midY) == 200 + #endif } } } diff --git a/CartographyTests/SafeAreaLayoutGuideSpec.swift b/CartographyTests/SafeAreaLayoutGuideSpec.swift deleted file mode 100644 index eef77a3..0000000 --- a/CartographyTests/SafeAreaLayoutGuideSpec.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// SafeAreaLayoutGuideSpec.swift -// Cartography -// -// Created by Vitor Travain on 17/10/17. -// Copyright © 2017 Robert Böhnke. All rights reserved. -// - -import UIKit -import Quick -import Nimble -@testable import Cartography - -@available(iOS, introduced: 11.0) -@available(tvOS, introduced: 11.0) -final class SafeAreaLayoutGuideSpec: QuickSpec { - override func spec() { - describe("SafeAreaLayoutGuide") { - var superview: UIView! - var view: UIView! - - beforeEach { - superview = TestView(frame: UIScreen.main.bounds) - view = TestView(frame: CGRect.zero) - - superview.addSubview(view) - } - - it("Views should align to edges") { - constrain(view) { view in - view.edges == view.superview!.safeAreaLayoutGuide.edges - } - - superview.layoutIfNeeded() - - expect(view.frame.minX) == (superview.safeAreaLayoutGuide.layoutFrame.minX) - expect(view.frame.minY) == (superview.safeAreaLayoutGuide.layoutFrame.maxX) - expect(view.frame.width) == (superview.safeAreaLayoutGuide.layoutFrame.width) - expect(view.frame.height) == (superview.safeAreaLayoutGuide.layoutFrame.height) - } - - it("View should center in safe area") { - constrain(view) { view in - view.center == view.superview!.safeAreaLayoutGuide.center - view.width == 200 - view.height == 200 - } - - superview.layoutIfNeeded() - - expect(view.frame.midX) == (superview.safeAreaLayoutGuide.layoutFrame.midX) - expect(view.frame.midY) == (superview.safeAreaLayoutGuide.layoutFrame.midY) - expect(view.frame.width) == 200 - expect(view.frame.height) == 200 - } - } - } -} diff --git a/CartographyTests/ViewLayoutGuideSpec.swift b/CartographyTests/ViewLayoutGuideSpec.swift new file mode 100644 index 0000000..d230d45 --- /dev/null +++ b/CartographyTests/ViewLayoutGuideSpec.swift @@ -0,0 +1,146 @@ +// +// SafeAreaLayoutGuideSpec.swift +// Cartography +// +// Created by Vitor Travain on 17/10/17. +// Copyright © 2017 Robert Böhnke. All rights reserved. +// + +import UIKit +import Quick +import Nimble +@testable import Cartography + +@available(iOS, introduced: 9.0) +@available(iOS, introduced: 9.0) +final class ViewLayoutGuideSpec: QuickSpec { + override func spec() { + describe("Layout margin guide") { + var superview: UIView! + var view: UIView! + + beforeEach { + superview = UIView(frame: UIScreen.main.bounds) + view = UIView(frame: .zero) + + superview.addSubview(view) + } + + it("Views should align to edges within margins") { + constrain(view) { view in + view.edges == view.superview!.layoutMarginsGuide.edges + } + + superview.layoutIfNeeded() + + expect(view.frame.minX) == superview.layoutMarginsGuide.layoutFrame.minX + expect(view.frame.minY) == superview.layoutMarginsGuide.layoutFrame.minY + expect(view.frame.width) == superview.layoutMarginsGuide.layoutFrame.width + expect(view.frame.height) == superview.layoutMarginsGuide.layoutFrame.height + } + + it("Views should center within margins") { + constrain(view) { view in + view.center == view.superview!.layoutMarginsGuide.center + + view.width == 200 + view.height == 200 + } + + superview.layoutIfNeeded() + + expect(view.frame.midX) == (superview.layoutMarginsGuide.layoutFrame.midX) + expect(view.frame.midY) == (superview.layoutMarginsGuide.layoutFrame.midY) + expect(view.frame.width) == 200 + expect(view.frame.height) == 200 + } + } + + describe("Readable content guide") { + var superview: UIView! + var view: UIView! + + beforeEach { + superview = UIView(frame: UIScreen.main.bounds) + view = UIView(frame: .zero) + + superview.addSubview(view) + } + + it("Views should align to edges within readable margins") { + constrain(view) { view in + view.edges == view.superview!.readableContentGuide.edges + } + + superview.layoutIfNeeded() + + expect(view.frame.minX) == superview.readableContentGuide.layoutFrame.minX + expect(view.frame.minY) == superview.readableContentGuide.layoutFrame.minY + expect(view.frame.width) == superview.readableContentGuide.layoutFrame.width + expect(view.frame.height) == superview.readableContentGuide.layoutFrame.height + } + + it("Views should center within readable margins") { + constrain(view) { view in + view.center == view.superview!.readableContentGuide.center + + view.width == 200 + view.height == 200 + } + + superview.layoutIfNeeded() + + expect(view.frame.midX) == (superview.readableContentGuide.layoutFrame.midX) + expect(view.frame.midY) == (superview.readableContentGuide.layoutFrame.midY) + expect(view.frame.width) == 200 + expect(view.frame.height) == 200 + } + } + } +} + +@available(iOS, introduced: 11.0) +@available(tvOS, introduced: 11.0) +final class SafeAreaLayoutGuideSpec: QuickSpec { + override func spec() { + describe("Safe area layout guide") { + var superview: UIView! + var view: UIView! + + beforeEach { + superview = TestView(frame: UIScreen.main.bounds) + view = TestView(frame: CGRect.zero) + + superview.addSubview(view) + } + + it("Views should align to safe area edges") { + constrain(view) { view in + view.edges == view.superview!.safeAreaLayoutGuide.edges + } + + superview.layoutIfNeeded() + + expect(view.frame.minX) == (superview.safeAreaLayoutGuide.layoutFrame.minX) + expect(view.frame.minY) == (superview.safeAreaLayoutGuide.layoutFrame.maxX) + expect(view.frame.width) == (superview.safeAreaLayoutGuide.layoutFrame.width) + expect(view.frame.height) == (superview.safeAreaLayoutGuide.layoutFrame.height) + } + + it("View should center in safe area") { + constrain(view) { view in + view.center == view.superview!.safeAreaLayoutGuide.center + view.width == 200 + view.height == 200 + } + + superview.layoutIfNeeded() + + expect(view.frame.midX) == (superview.safeAreaLayoutGuide.layoutFrame.midX) + expect(view.frame.midY) == (superview.safeAreaLayoutGuide.layoutFrame.midY) + expect(view.frame.width) == 200 + expect(view.frame.height) == 200 + } + } + } +}