Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Cartography.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,13 @@
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 */; };
9AE90A0429FAF2D700944F10 /* Edge+OffsetOperator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE90A0329FAF2D700944F10 /* Edge+OffsetOperator.swift */; };
9AE90A0529FAF2D700944F10 /* Edge+OffsetOperator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE90A0329FAF2D700944F10 /* Edge+OffsetOperator.swift */; };
9AE90A0629FAF2D700944F10 /* Edge+OffsetOperator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE90A0329FAF2D700944F10 /* Edge+OffsetOperator.swift */; };
9AE90A0829FAF36E00944F10 /* AnchorSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE90A0729FAF36E00944F10 /* AnchorSupport.swift */; };
9AE90A0929FAF36E00944F10 /* AnchorSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE90A0729FAF36E00944F10 /* AnchorSupport.swift */; };
9AE90A0A29FAF36E00944F10 /* AnchorSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE90A0729FAF36E00944F10 /* AnchorSupport.swift */; };
9AE90A0C29FB03DC00944F10 /* OffsetDimensionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE90A0B29FB03DC00944F10 /* OffsetDimensionSpec.swift */; };
A75B6143FF12C54FF3223B47 /* Pods_TestPods_Cartography_tvOS_tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0827A83361EACF1E6062607E /* Pods_TestPods_Cartography_tvOS_tests.framework */; };
D63BD7B82089B6FA00061239 /* LayoutItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE85314F1F9363DC003EC021 /* LayoutItem.swift */; };
EE8531501F936462003EC021 /* LayoutItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE85314F1F9363DC003EC021 /* LayoutItem.swift */; };
Expand Down Expand Up @@ -272,6 +279,9 @@
97E7F0A81F8D598A004857CE /* ViewProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewProxy.swift; sourceTree = "<group>"; };
97F50E511F962CF300C6DCF5 /* LayoutProxy+TypeErasure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LayoutProxy+TypeErasure.swift"; sourceTree = "<group>"; };
97F50E531F9633AA00C6DCF5 /* ViewLayoutGuideSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewLayoutGuideSpec.swift; sourceTree = "<group>"; };
9AE90A0329FAF2D700944F10 /* Edge+OffsetOperator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Edge+OffsetOperator.swift"; sourceTree = "<group>"; };
9AE90A0729FAF36E00944F10 /* AnchorSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnchorSupport.swift; sourceTree = "<group>"; };
9AE90A0B29FB03DC00944F10 /* OffsetDimensionSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OffsetDimensionSpec.swift; sourceTree = "<group>"; };
EE85314F1F9363DC003EC021 /* LayoutItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutItem.swift; sourceTree = "<group>"; };
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 */
Expand Down Expand Up @@ -418,6 +428,7 @@
546E9E8C1950A31100B16707 /* Dimension.swift */,
5467916E1A93962000DC9BF7 /* Distribute.swift */,
546E9E901950A76C00B16707 /* Edge.swift */,
9AE90A0329FAF2D700944F10 /* Edge+OffsetOperator.swift */,
545F858C195322EA00791F75 /* Edges.swift */,
546E9E941950A97F00B16707 /* Expression.swift */,
54B093961A7165F2008A1102 /* Extensions.swift */,
Expand All @@ -437,6 +448,7 @@
97D17CA91F8E779300C57CE1 /* LayoutGuideProxy.swift */,
97D17C991F8E774700C57CE1 /* CartographyTests */,
54C96A14195063CD000CDD27 /* Supporting Files */,
9AE90A0729FAF36E00944F10 /* AnchorSupport.swift */,
);
path = Cartography;
sourceTree = "<group>";
Expand Down Expand Up @@ -466,6 +478,7 @@
979F29C91F94DC6B00257363 /* LayoutGuideSpec.swift */,
97D17C9D1F8E774700C57CE1 /* PrioritySpec.swift */,
54C96A23195063CD000CDD27 /* DimensionSpec.swift */,
9AE90A0B29FB03DC00944F10 /* OffsetDimensionSpec.swift */,
97D17C9E1F8E774700C57CE1 /* TestView.swift */,
97D17C9F1F8E774700C57CE1 /* Matchers.swift */,
97D17CA01F8E774700C57CE1 /* DistributeSpec.swift */,
Expand Down Expand Up @@ -863,6 +876,7 @@
97D17C961F8E774400C57CE1 /* LayoutGuide.swift in Sources */,
547BC85719E2EB9B007BEE9E /* Constraint.swift in Sources */,
547BC85519E2DD06007BEE9E /* Context.swift in Sources */,
9AE90A0429FAF2D700944F10 /* Edge+OffsetOperator.swift in Sources */,
97F50E521F962CF300C6DCF5 /* LayoutProxy+TypeErasure.swift in Sources */,
EE8531501F936462003EC021 /* LayoutItem.swift in Sources */,
977C36821F9FAC890057A057 /* AutoresizingMaskLayoutProxy.swift in Sources */,
Expand All @@ -883,6 +897,7 @@
54BF29B01A9348B30066ED10 /* Align.swift in Sources */,
546E9E951950A97F00B16707 /* Expression.swift in Sources */,
97D17CAA1F8E779300C57CE1 /* LayoutGuideProxy.swift in Sources */,
9AE90A0829FAF36E00944F10 /* AnchorSupport.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -894,6 +909,7 @@
97F50E541F9633AA00C6DCF5 /* ViewLayoutGuideSpec.swift in Sources */,
1B319C622162740100DD91D4 /* ViewProxyTests.swift in Sources */,
9795590B1F9701CD0096BBEA /* ConstraintGroupSpec.swift in Sources */,
9AE90A0C29FB03DC00944F10 /* OffsetDimensionSpec.swift in Sources */,
979559081F9701C90096BBEA /* MemoryLeakSpec.swift in Sources */,
979558FC1F97019E0096BBEA /* Matchers.swift in Sources */,
979559021F9701B10096BBEA /* PointSpec.swift in Sources */,
Expand Down Expand Up @@ -921,6 +937,7 @@
54F6A856195C213A00313D24 /* Edges.swift in Sources */,
54F6A858195C213A00313D24 /* LayoutProxy.swift in Sources */,
54BF29B51A9350170066ED10 /* Align.swift in Sources */,
9AE90A0529FAF2D700944F10 /* Edge+OffsetOperator.swift in Sources */,
97F50E561F96401200C6DCF5 /* LayoutProxy+TypeErasure.swift in Sources */,
9722953C1F950A1E00FA4AF9 /* ViewProxy.swift in Sources */,
977C36831F9FC2900057A057 /* AutoresizingMaskLayoutProxy.swift in Sources */,
Expand All @@ -941,6 +958,7 @@
549B47B61A58198B002498C7 /* Context.swift in Sources */,
54F6A854195C213A00313D24 /* Dimension.swift in Sources */,
54F6A857195C213A00313D24 /* Expression.swift in Sources */,
9AE90A0929FAF36E00944F10 /* AnchorSupport.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -976,6 +994,7 @@
632F09371BF1F127002431A3 /* Compound.swift in Sources */,
632F09381BF1F127002431A3 /* Constrain.swift in Sources */,
632F09391BF1F127002431A3 /* Constraint.swift in Sources */,
9AE90A0629FAF2D700944F10 /* Edge+OffsetOperator.swift in Sources */,
632F093A1BF1F127002431A3 /* ConstraintGroup.swift in Sources */,
97D17C921F8E740B00C57CE1 /* LayoutSupportProxy.swift in Sources */,
97F50E571F96401300C6DCF5 /* LayoutProxy+TypeErasure.swift in Sources */,
Expand All @@ -996,6 +1015,7 @@
632F09461BF1F127002431A3 /* Size.swift in Sources */,
4A3756C91D67445F0036190E /* LayoutSupport.swift in Sources */,
632F09471BF1F127002431A3 /* View.swift in Sources */,
9AE90A0A29FAF36E00944F10 /* AnchorSupport.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
87 changes: 87 additions & 0 deletions Cartography/AnchorSupport.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//
// AnchorSupport.swift
// Cartography
//
// Created by Jayson Rhynas on 2023-04-27.
// Copyright © 2023 Robert Böhnke. All rights reserved.
//

import Foundation

#if os(iOS) || os(tvOS)
import UIKit
#elseif os(OSX)
import AppKit
#endif

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol AnchorSupport: AnyObject {}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol SupportsTopAnchor: AnchorSupport {
var topAnchor: NSLayoutYAxisAnchor { get }
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol SupportsBottomAnchor: AnchorSupport {
var bottomAnchor: NSLayoutYAxisAnchor { get }
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol SupportsRightAnchor: AnchorSupport {
var rightAnchor: NSLayoutXAxisAnchor { get }
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol SupportsLeftAnchor: AnchorSupport {
var leftAnchor: NSLayoutXAxisAnchor { get }
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol SupportsLeadingAnchor: AnchorSupport {
var leadingAnchor: NSLayoutXAxisAnchor { get }
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol SupportsTrailingAnchor: AnchorSupport {
var trailingAnchor: NSLayoutXAxisAnchor { get }
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol SupportsEdgeAnchors: SupportsTopAnchor, SupportsBottomAnchor, SupportsLeadingAnchor, SupportsTrailingAnchor, SupportsLeftAnchor, SupportsRightAnchor {}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol SupportsCenterXAnchor: AnchorSupport {
var centerXAnchor: NSLayoutXAxisAnchor { get }
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol SupportsCenterYAnchor: AnchorSupport {
var centerYAnchor: NSLayoutYAxisAnchor { get }
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol SupportsCenteringAnchors: SupportsCenterXAnchor, SupportsCenterYAnchor {}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol SupportsWidthAnchor: AnchorSupport {
var widthAnchor: NSLayoutDimension { get }
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol SupportsHeightAnchor: AnchorSupport {
var heightAnchor: NSLayoutDimension { get }
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol SupportsSizeAnchors: SupportsWidthAnchor, SupportsHeightAnchor {}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol SupportsBaselineAnchors: AnchorSupport {
var firstBaselineAnchor: NSLayoutYAxisAnchor { get }

var lastBaselineAnchor: NSLayoutYAxisAnchor { get }
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
public protocol SupportsPositioningAnchors: SupportsEdgeAnchors, SupportsSizeAnchors, SupportsCenteringAnchors {}
47 changes: 46 additions & 1 deletion Cartography/Context.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,32 @@ public class Context {
fromItem.translatesAutoresizingMaskIntoConstraints = false
}

let layoutConstraint = NSLayoutConstraint(item: from.item,
func constrainDimensionAnchors() -> NSLayoutConstraint? {
guard #available(iOS 10.0, macOS 10.12, tvOS 10.0, *) else {
return nil
}

guard from.attribute == .notAnAttribute || to?.attribute == .notAnAttribute else {
return nil
}

guard let fromAnchor = (from as? Dimension)?.anchor else {
return nil
}

if let to = to {
guard let toAnchor = (to as? Dimension)?.anchor else {
return nil
}

return addConstraint(from: fromAnchor, to: toAnchor, coefficients: coefficients, relation: relation)
} else {
return addConstraint(from: fromAnchor, to: coefficients.constant, relation: relation)
}
}

let layoutConstraint = constrainDimensionAnchors()
?? NSLayoutConstraint(item: from.item,
attribute: from.attribute,
relatedBy: relation,
toItem: to?.item,
Expand All @@ -48,4 +73,24 @@ public class Context {

return results
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
private func addConstraint(from dimension: NSLayoutDimension, to constant: CGFloat, relation: LayoutRelation) -> NSLayoutConstraint {
switch relation {
case .lessThanOrEqual: return dimension.constraint(lessThanOrEqualToConstant: constant)
case.greaterThanOrEqual: return dimension.constraint(greaterThanOrEqualToConstant: constant)
case .equal: return dimension.constraint(equalToConstant: constant)
@unknown default: fatalError("Unknown relation \(relation)")
}
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
private func addConstraint(from dimension: NSLayoutDimension, to other: NSLayoutDimension, coefficients: Coefficients, relation: LayoutRelation) -> NSLayoutConstraint {
switch relation {
case .lessThanOrEqual: return dimension.constraint(lessThanOrEqualTo: other, multiplier: coefficients.multiplier, constant: coefficients.constant)
case.greaterThanOrEqual: return dimension.constraint(greaterThanOrEqualTo: other, multiplier: coefficients.multiplier, constant: coefficients.constant)
case .equal: return dimension.constraint(equalTo: other, multiplier: coefficients.multiplier, constant: coefficients.constant)
@unknown default: fatalError("Unknown relation \(relation)")
}
}
}
28 changes: 27 additions & 1 deletion Cartography/Dimension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,36 @@ public struct Dimension : Property, NumericalEquality, RelativeEquality, Numeric
public let attribute: LayoutAttribute
public let context: Context
public let item: AnyObject


private let _anchor: AnyObject?

internal init(_ context: Context, _ item: AnyObject, _ attribute: LayoutAttribute) {
self.attribute = attribute
self.context = context
self.item = item
self._anchor = nil
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
internal init(_ context: Context, _ anchor: NSLayoutDimension) {
self.attribute = .notAnAttribute
self.context = context
self.item = NSNull()
self._anchor = anchor
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
internal var anchor: NSLayoutDimension? {
if let anchor = self._anchor as? NSLayoutDimension { return anchor }

if attribute == .width, let item = item as? SupportsWidthAnchor {
return item.widthAnchor
}
else if attribute == .height, let item = item as? SupportsHeightAnchor {
return item.heightAnchor
}
else {
return nil
}
}
}
77 changes: 77 additions & 0 deletions Cartography/Edge+OffsetOperator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//
// Edge+OffsetOperator.swift
// Cartography
//
// Created by Jayson Rhynas on 2023-04-27.
// Copyright © 2023 Robert Böhnke. All rights reserved.
//

import Foundation

#if os(iOS) || os(tvOS)
import UIKit
#else
import AppKit
#endif

precedencegroup OffsetPrecedence {
higherThan: MultiplicationPrecedence
associativity: none
}

infix operator |-|: OffsetPrecedence

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
extension Edge {
public static func |-|(lhs: Edge, rhs: Edge) -> Dimension {
if let first = lhs.xAnchor, let second = rhs.xAnchor {
return Dimension(lhs.context, first.anchorWithOffset(to: second))
} else if let first = lhs.yAnchor, let second = rhs.yAnchor {
return Dimension(lhs.context, first.anchorWithOffset(to: second))
} else {
return Dimension(lhs.context, lhs.item, .notAnAttribute)
}
}

internal var xAnchor: NSLayoutXAxisAnchor? {
if attribute == .left, let item = item as? SupportsLeftAnchor {
return item.leftAnchor
}
else if attribute == .right, let item = item as? SupportsRightAnchor {
return item.rightAnchor
}
else if attribute == .leading, let item = item as? SupportsLeadingAnchor {
return item.leadingAnchor
}
else if attribute == .trailing, let item = item as? SupportsTrailingAnchor {
return item.trailingAnchor
}
else if attribute == .centerX, let item = item as? SupportsCenterXAnchor {
return item.centerXAnchor
}
else {
return nil
}
}

internal var yAnchor: NSLayoutYAxisAnchor? {
if attribute == .top, let item = item as? SupportsTopAnchor {
return item.topAnchor
}
else if attribute == .bottom, let item = item as? SupportsBottomAnchor {
return item.bottomAnchor
}
else if attribute == .centerY, let item = item as? SupportsCenterYAnchor {
return item.centerYAnchor
}
else if attribute == .firstBaseline, let item = item as? SupportsBaselineAnchors {
return item.firstBaselineAnchor
}
else if attribute == .lastBaseline, let item = item as? SupportsBaselineAnchors {
return item.lastBaselineAnchor
}
else {
return nil
}
}
}
4 changes: 2 additions & 2 deletions Cartography/LayoutGuide.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public typealias LayoutGuide = UILayoutGuide

@available(iOS, introduced: 9.0)
@available(tvOS, introduced: 9.0)
extension UILayoutGuide: LayoutItem {
extension UILayoutGuide: LayoutItem, SupportsPositioningAnchors {

@available(iOS, introduced: 9.0)
@available(tvOS, introduced: 9.0)
Expand All @@ -30,7 +30,7 @@ import AppKit
public typealias LayoutGuide = NSLayoutGuide

@available(OSX, introduced: 10.11)
extension NSLayoutGuide: LayoutItem {
extension NSLayoutGuide: LayoutItem, SupportsPositioningAnchors {

@available(OSX, introduced: 10.11)
public func asProxy(context: Context) -> LayoutGuideProxy {
Expand Down
Loading