Commit a89797ae by Daniel Jonathan Committed by GitHub

Merge pull request #1185 from OrkhanAlikhanov/layout-system

Reworked layout system
parents 091d4306 3721e66e
......@@ -174,6 +174,9 @@
9D054A6520D175AC00D0528D /* Material+UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D054A6320D175AC00D0528D /* Material+UIButton.swift */; };
9D054A6620D175AC00D0528D /* Material+UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D054A6420D175AC00D0528D /* Material+UILabel.swift */; };
9D39A81B20FE8ED100BA8FA1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */; };
9D494A38217F6B63003D66F1 /* LayoutAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D494A37217F6B63003D66F1 /* LayoutAttribute.swift */; };
9D494A3A217F6B70003D66F1 /* LayoutAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D494A39217F6B70003D66F1 /* LayoutAnchor.swift */; };
9D494A3C217F6B7D003D66F1 /* LayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D494A3B217F6B7D003D66F1 /* LayoutConstraint.swift */; };
9D9089B92118914500605DC9 /* Editor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D9089B82118914500605DC9 /* Editor.swift */; };
9DE25DE02170D7AF000C04DF /* Dialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE25DDF2170D7AF000C04DF /* Dialog.swift */; };
9DE25DE22170D7C0000C04DF /* DialogController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE25DE12170D7C0000C04DF /* DialogController.swift */; };
......@@ -300,6 +303,9 @@
9D054A6320D175AC00D0528D /* Material+UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIButton.swift"; sourceTree = "<group>"; };
9D054A6420D175AC00D0528D /* Material+UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UILabel.swift"; sourceTree = "<group>"; };
9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
9D494A37217F6B63003D66F1 /* LayoutAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutAttribute.swift; sourceTree = "<group>"; };
9D494A39217F6B70003D66F1 /* LayoutAnchor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutAnchor.swift; sourceTree = "<group>"; };
9D494A3B217F6B7D003D66F1 /* LayoutConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutConstraint.swift; sourceTree = "<group>"; };
9D9089B82118914500605DC9 /* Editor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Editor.swift; sourceTree = "<group>"; };
9DE25DDF2170D7AF000C04DF /* Dialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dialog.swift; sourceTree = "<group>"; };
9DE25DE12170D7C0000C04DF /* DialogController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DialogController.swift; sourceTree = "<group>"; };
......@@ -652,6 +658,9 @@
isa = PBXGroup;
children = (
96BCB7811CB40DC500C806FE /* Layout.swift */,
9D494A39217F6B70003D66F1 /* LayoutAnchor.swift */,
9D494A37217F6B63003D66F1 /* LayoutAttribute.swift */,
9D494A3B217F6B7D003D66F1 /* LayoutConstraint.swift */,
);
name = Layout;
sourceTree = "<group>";
......@@ -993,6 +1002,7 @@
9DF352461FED210000B2A11B /* CheckButton.swift in Sources */,
965E810A1DD4D5C800D61E4B /* Font.swift in Sources */,
965E810B1DD4D5C800D61E4B /* RobotoFont.swift in Sources */,
9D494A3C217F6B7D003D66F1 /* LayoutConstraint.swift in Sources */,
965E810C1DD4D5C800D61E4B /* DynamicFontType.swift in Sources */,
965E81101DD4D5C800D61E4B /* NavigationBar.swift in Sources */,
965E81111DD4D5C800D61E4B /* NavigationController.swift in Sources */,
......@@ -1015,6 +1025,7 @@
965E81211DD4D5C800D61E4B /* TextStorage.swift in Sources */,
965E81221DD4D5C800D61E4B /* TextView.swift in Sources */,
965E80E71DD4C55200D61E4B /* Material+UIView.swift in Sources */,
9D494A38217F6B63003D66F1 /* LayoutAttribute.swift in Sources */,
96B8D22D20CF82D5008BD149 /* FABMenu.swift in Sources */,
965E80E81DD4C55200D61E4B /* Material+CALayer.swift in Sources */,
965E80E91DD4C55200D61E4B /* Material+String.swift in Sources */,
......@@ -1054,6 +1065,7 @@
965E80D21DD4C50600D61E4B /* Color.swift in Sources */,
96BFC1541E5E486F0075DE1F /* SpringAnimation.swift in Sources */,
9D054A6620D175AC00D0528D /* Material+UILabel.swift in Sources */,
9D494A3A217F6B70003D66F1 /* LayoutAnchor.swift in Sources */,
965E80D31DD4C50600D61E4B /* Device.swift in Sources */,
965E80FD1DD4D59500D61E4B /* Toolbar.swift in Sources */,
965E80D41DD4C50600D61E4B /* Divider.swift in Sources */,
......
......@@ -69,9 +69,10 @@ open class ErrorTextField: TextField {
get {
return !errorLabel.isHidden
}
set {
errorLabel.isHidden = !newValue
detailLabel.isHidden = newValue
set(value) {
errorLabel.isHidden = !value
detailLabel.isHidden = value
layoutSubviews()
}
}
......
......@@ -31,970 +31,797 @@
import UIKit
import Motion
public class Layout {
/// Parent UIView context.
internal weak var parent: UIView?
/// A protocol that's conformed by UIView and UILayoutGuide.
public protocol Constraintable: class { }
/// Child UIView context.
internal weak var child: UIView?
@available(iOS 9.0, *)
extension UILayoutGuide: Constraintable { }
extension UIView: Constraintable { }
/// Layout extension for UIView.
public extension UIView {
/**
An initializer that takes in a parent context.
- Parameter parent: An optional parent UIView.
Used to chain layout constraints on a child context.
- Parameter child: A child UIView to layout.
- Returns: A Layout instance.
*/
public init(parent: UIView?) {
self.parent = parent
func layout(_ child: UIView) -> Layout {
addSubview(child)
child.translatesAutoresizingMaskIntoConstraints = false
return child.layout
}
/// Layout instance for the view.
var layout: Layout {
return Layout(constraintable: self)
}
/// Anchor instance for the view.
var anchor: LayoutAnchor {
return LayoutAnchor(constraintable: self)
}
/**
An initializer that takes in a parent context and child context.
- Parameter parent: An optional parent UIView.
- Parameter child: An optional child UIView.
Anchor instance for safeAreaLayoutGuide.
Below iOS 11, it will be same as view.anchor.
*/
public init(parent: UIView?, child: UIView?) {
self.parent = parent
self.child = child
var safeAnchor: LayoutAnchor {
if #available(iOS 11.0, *) {
return LayoutAnchor(constraintable: safeAreaLayoutGuide)
} else {
return anchor
}
}
}
public struct Layout {
/// A weak reference to the constraintable.
weak var constraintable: Constraintable?
/// Parent view of the view.
var parent: UIView? {
return (constraintable as? UIView)?.superview
}
/// Returns the view that is being laied out.
private var view: UIView? {
var v = constraintable as? UIView
if #available(iOS 9.0, *), v == nil {
v = (constraintable as? UILayoutGuide)?.owningView
}
return v
}
/**
Prints a debug message when the parent context is not available.
- Parameter function: A String representation of the function that
caused the issue.
- Returns: The current Layout instance.
An initializer taking Constraintable.
- Parameter view: A Constraintable.
*/
internal func debugParentNotAvailableMessage(function: String = #function) -> Layout {
debugPrint("[Material Layout Error: Parent view context is not available for \(function).")
return self
init(constraintable: Constraintable) {
self.constraintable = constraintable
}
}
public extension Layout {
/**
Prints a debug message when the child context is not available.
- Parameter function: A String representation of the function that
caused the issue.
- Returns: The current Layout instance.
Sets multiplier of the last created constraint.
Not meant for updating the multiplier as it will re-create the constraint.
- Parameter _ multiplier: A CGFloat multiplier.
- Returns: A Layout instance to allow chaining.
*/
internal func debugChildNotAvailableMessage(function: String = #function) -> Layout {
debugPrint("[Material Layout Error: Child view context is not available for \(function).")
return self
func multiply(_ multiplier: CGFloat) -> Layout {
return resetLastConstraint(multiplier: multiplier)
}
/**
Sets the width of a view.
- Parameter child: A child UIView to layout.
- Parameter width: A CGFloat value.
- Returns: The current Layout instance.
Sets priority of the last created constraint.
Not meant for updating the multiplier as it will re-create the constraint.
- Parameter _ value: A Float priority.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func width(_ child: UIView, width: CGFloat) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.width(parent: v, child: child, width: width)
return self
func priority(_ value: Float) -> Layout {
return priority(.init(rawValue: value))
}
/**
Sets the width of a view assuming a child context view.
- Parameter width: A CGFloat value.
- Returns: The current Layout instance.
Sets priority of the last created constraint.
Not meant for updating the priority as it will re-create the constraint.
- Parameter _ priority: A UILayoutPriority.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func width(_ width: CGFloat) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return self.width(v, width: width)
func priority(_ priority: UILayoutPriority) -> Layout {
return resetLastConstraint(priority: priority)
}
/**
Sets the height of a view.
- Parameter child: A child UIView to layout.
- Parameter height: A CGFloat value.
- Returns: The current Layout instance.
Removes the last created constraint and creates new one with the new multiplier and/or priority (if provided).
- Parameter multiplier: An optional CGFloat.
- Parameter priority: An optional UILayoutPriority.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func height(_ child: UIView, height: CGFloat) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
private func resetLastConstraint(multiplier: CGFloat? = nil, priority: UILayoutPriority? = nil) -> Layout {
guard let v = view?.lastConstraint, v.isActive else {
return self
}
self.child = child
Layout.height(parent: v, child: child, height: height)
v.isActive = false
let newV = NSLayoutConstraint(item: v.firstItem as Any,
attribute: v.firstAttribute,
relatedBy: v.relation,
toItem: v.secondItem,
attribute: v.secondAttribute,
multiplier: multiplier ?? v.multiplier,
constant: v.constant)
newV.priority = priority ?? v.priority
newV.isActive = true
view?.lastConstraint = newV
return self
}
}
public extension Layout {
/**
Sets the height of a view assuming a child context view.
- Parameter height: A CGFloat value.
- Returns: The current Layout instance.
Constraints top of the view to its parent's.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func height(_ height: CGFloat) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return self.height(v, height: height)
func top(_ offset: CGFloat = 0) -> Layout {
return constraint(.top, constant: offset)
}
/**
Sets the width and height of a view.
- Parameter child: A child UIView to layout.
- Parameter size: A CGSize value.
- Returns: The current Layout instance.
Constraints left of the view to its parent's.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func size(_ child: UIView, size: CGSize) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.size(parent: v, child: child, size: size)
return self
func left(_ offset: CGFloat = 0) -> Layout {
return constraint(.left, constant: offset)
}
/**
Sets the width and height of a view assuming a child context view.
- Parameter size: A CGSize value.
- Returns: The current Layout instance.
Constraints right of the view to its parent.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func size(_ size: CGSize = CGSize.zero) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return self.size(v, size: size)
func right(_ offset: CGFloat = 0) -> Layout {
return constraint(.right, constant: -offset)
}
/**
A collection of children views are horizontally stretched with optional left,
right padding and interim interimSpace.
- Parameter children: An Array UIView to layout.
- Parameter left: A CGFloat value for padding the left side.
- Parameter right: A CGFloat value for padding the right side.
- Parameter interimSpace: A CGFloat value for interim interimSpace.
- Returns: The current Layout instance.
Constraints leading of the view to its parent's.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func horizontally(_ children: [UIView], left: CGFloat = 0, right: CGFloat = 0, interimSpace: InterimSpace = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
Layout.horizontally(parent: v, children: children, left: left, right: right, interimSpace: interimSpace)
return self
func leading(_ offset: CGFloat = 0) -> Layout {
return constraint(.leading, constant: offset)
}
/**
A collection of children views are vertically stretched with optional top,
bottom padding and interim interimSpace.
- Parameter children: An Array UIView to layout.
- Parameter top: A CGFloat value for padding the top side.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Parameter interimSpace: A CGFloat value for interim interimSpace.
- Returns: The current Layout instance.
Constraints trailing of the view to its parent.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func vertically(_ children: [UIView], top: CGFloat = 0, bottom: CGFloat = 0, interimSpace: InterimSpace = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
Layout.vertically(parent: v, children: children, top: top, bottom: bottom, interimSpace: interimSpace)
return self
func trailing(_ offset: CGFloat = 0) -> Layout {
return constraint(.trailing, constant: -offset)
}
/**
A child view is horizontally stretched with optional left and right padding.
- Parameter child: A child UIView to layout.
- Parameter left: A CGFloat value for padding the left side.
- Parameter right: A CGFloat value for padding the right side.
- Returns: The current Layout instance.
Constraints bottom of the view to its parent's.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func horizontally(_ child: UIView, left: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.horizontally(parent: v, child: child, left: left, right: right)
return self
func bottom(_ offset: CGFloat = 0) -> Layout {
return constraint(.bottom, constant: -offset)
}
/**
A child view is horizontally stretched with optional left and right padding.
- Parameter left: A CGFloat value for padding the left side.
- Parameter right: A CGFloat value for padding the right side.
- Returns: The current Layout instance.
Constraints top-left of the view to its parent's.
- Parameter top: A CGFloat offset for top.
- Parameter left: A CGFloat offset for left.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func horizontally(left: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return horizontally(v, left: left, right: right)
func topLeft(top: CGFloat = 0, left: CGFloat = 0) -> Layout {
return constraint(.topLeft, constants: top, left)
}
/**
A child view is vertically stretched with optional left and right padding.
- Parameter child: A child UIView to layout.
- Parameter top: A CGFloat value for padding the top side.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Returns: The current Layout instance.
Constraints top-right of the view to its parent's.
- Parameter top: A CGFloat offset for top.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func vertically(_ child: UIView, top: CGFloat = 0, bottom: CGFloat = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.vertically(parent: v, child: child, top: top, bottom: bottom)
return self
func topRight(top: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.topRight, constants: top, -right)
}
/**
A child view is vertically stretched with optional left and right padding.
- Parameter top: A CGFloat value for padding the top side.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Returns: The current Layout instance.
Constraints bottom-left of the view to its parent's.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter left: A CGFloat offset for left.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func vertically(top: CGFloat = 0, bottom: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return vertically(v, top: top, bottom: bottom)
func bottomLeft(bottom: CGFloat = 0, left: CGFloat = 0) -> Layout {
return constraint(.bottomLeft, constants: -bottom, left)
}
/**
A child view is vertically and horizontally stretched with optional top, left, bottom and right padding.
- Parameter child: A child UIView to layout.
- Parameter top: A CGFloat value for padding the top side.
- Parameter left: A CGFloat value for padding the left side.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Parameter right: A CGFloat value for padding the right side.
- Returns: The current Layout instance.
Constraints bottom-right of the view to its parent's.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func edges(_ child: UIView, top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.edges(parent: v, child: child, top: top, left: left, bottom: bottom, right: right)
return self
func bottomRight(bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.bottomRight, constants: -bottom, -right)
}
/**
A child view is vertically and horizontally stretched with optional top, left, bottom and right padding.
- Parameter child: A child UIView to layout.
- Parameter top: A CGFloat value for padding the top side.
- Parameter left: A CGFloat value for padding the left side.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Parameter right: A CGFloat value for padding the right side.
- Returns: The current Layout instance.
Constraints left and right of the view to its parent's.
- Parameter left: A CGFloat offset for left.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func edges(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return edges(v, top: top, left: left, bottom: bottom, right: right)
func leftRight(left: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.leftRight, constants: left, -right)
}
/**
A child view is aligned from the top with optional top padding.
- Parameter child: A child UIView to layout.
- Parameter top: A CGFloat value for padding the top side.
- Returns: The current Layout instance.
Constraints top-leading of the view to its parent's.
- Parameter top: A CGFloat offset for top.
- Parameter leading: A CGFloat offset for leading.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func top(_ child: UIView, top: CGFloat = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.top(parent: v, child: child, top: top)
return self
func topLeading(top: CGFloat = 0, leading: CGFloat = 0) -> Layout {
return constraint(.topLeading, constants: top, leading)
}
/**
A child view is aligned from the top with optional top padding.
- Parameter top: A CGFloat value for padding the top side.
- Returns: The current Layout instance.
Constraints top-trailing of the view to its parent's.
- Parameter top: A CGFloat offset for top.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func top(_ top: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return self.top(v, top: top)
func topTrailing(top: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
return constraint(.topTrailing, constants: top, -trailing)
}
/**
A child view is aligned from the left with optional left padding.
- Parameter child: A child UIView to layout.
- Parameter left: A CGFloat value for padding the left side.
- Returns: The current Layout instance.
Constraints bottom-leading of the view to its parent's.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter leading: A CGFloat offset for leading.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func left(_ child: UIView, left: CGFloat = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.left(parent: v, child: child, left: left)
return self
func bottomLeading(bottom: CGFloat = 0, leading: CGFloat = 0) -> Layout {
return constraint(.bottomLeading, constants: -bottom, leading)
}
/**
A child view is aligned from the left with optional left padding.
- Parameter left: A CGFloat value for padding the left side.
- Returns: The current Layout instance.
Constraints bottom-trailing of the view to its parent's.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func left(_ left: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return self.left(v, left: left)
func bottomTrailing(bottom: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
return constraint(.bottomTrailing, constants: -bottom, -trailing)
}
/**
A child view is aligned from the bottom with optional bottom padding.
- Parameter child: A child UIView to layout.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Returns: The current Layout instance.
Constraints leading and trailing of the view to its parent's.
- Parameter leading: A CGFloat offset for leading.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func bottom(_ child: UIView, bottom: CGFloat = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.bottom(parent: v, child: child, bottom: bottom)
return self
func leadingTrailing(leading: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
return constraint(.leadingTrailing, constants: leading, -trailing)
}
/**
A child view is aligned from the bottom with optional bottom padding.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Returns: The current Layout instance.
Constraints top and bottom of the view to its parent's.
- Parameter top: A CGFloat offset for top.
- Parameter bottom: A CGFloat offset for bottom.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func bottom(_ bottom: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return self.bottom(v, bottom: bottom)
func topBottom(top: CGFloat = 0, bottom: CGFloat = 0) -> Layout {
return constraint(.topBottom, constants: top, -bottom)
}
/**
A child view is aligned from the right with optional right padding.
- Parameter child: A child UIView to layout.
- Parameter right: A CGFloat value for padding the right side.
- Returns: The current Layout instance.
Constraints center of the view to its parent's.
- Parameter offsetX: A CGFloat offset for horizontal center.
- Parameter offsetY: A CGFloat offset for vertical center.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func right(_ child: UIView, right: CGFloat = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.right(parent: v, child: child, right: right)
return self
func center(offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout {
return constraint(.center, constants: offsetX, offsetY)
}
/**
A child view is aligned from the right with optional right padding.
- Parameter right: A CGFloat value for padding the right side.
- Returns: The current Layout instance.
Constraints horizontal center of the view to its parent's.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func right(_ right: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return self.right(v, right: right)
func centerX(_ offset: CGFloat = 0) -> Layout {
return constraint(.centerX, constant: offset)
}
/**
A child view is aligned from the top left with optional top and left padding.
- Parameter child: A child UIView to layout.
- Parameter top: A CGFloat value for padding the top side.
- Parameter left: A CGFloat value for padding the left side.
- Returns: The current Layout instance.
Constraints vertical center of the view to its parent's.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func topLeft(_ child: UIView, top: CGFloat = 0, left: CGFloat = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.topLeft(parent: v, child: child, top: top, left: left)
return self
func centerY(_ offset: CGFloat = 0) -> Layout {
return constraint(.centerY, constant: offset)
}
/**
A child view is aligned from the top left with optional top and left padding.
- Parameter top: A CGFloat value for padding the top side.
- Parameter left: A CGFloat value for padding the left side.
- Returns: The current Layout instance.
Constraints width of the view to its parent's.
- Parameter offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func topLeft(top: CGFloat = 0, left: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return topLeft(v, top: top, left: left)
func width(offset: CGFloat = 0) -> Layout {
return constraint(.width, constant: offset)
}
/**
A child view is aligned from the top right with optional top and right padding.
- Parameter child: A child UIView to layout.
- Parameter top: A CGFloat value for padding the top side.
- Parameter right: A CGFloat value for padding the right side.
- Returns: The current Layout instance.
Constraints height of the view to its parent's.
- Parameter offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func topRight(_ child: UIView, top: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.topRight(parent: v, child: child, top: top, right: right)
return self
func height(offset: CGFloat = 0) -> Layout {
return constraint(.height, constant: offset)
}
/**
A child view is aligned from the top right with optional top and right padding.
- Parameter top: A CGFloat value for padding the top side.
- Parameter right: A CGFloat value for padding the right side.
- Returns: The current Layout instance.
Constraints edges of the view to its parent's.
- Parameter top: A CGFloat offset for top.
- Parameter left: A CGFloat offset for left.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func topRight(top: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return topRight(v, top: top, right: right)
func edges(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.edges, constants: top, left, -bottom, -right)
}
}
public extension Layout {
/**
A child view is aligned from the bottom left with optional bottom and left padding.
- Parameter child: A child UIView to layout.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Parameter left: A CGFloat value for padding the left side.
- Returns: The current Layout instance.
Constraints width of the view to a constant value.
- Parameter _ width: A CGFloat value.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func bottomLeft(_ child: UIView, bottom: CGFloat = 0, left: CGFloat = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.bottomLeft(parent: v, child: child, bottom: bottom, left: left)
return self
func width(_ width: CGFloat) -> Layout {
return constraint(.constantWidth, constants: width)
}
/**
A child view is aligned from the bottom left with optional bottom and left padding.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Parameter left: A CGFloat value for padding the left side.
- Returns: The current Layout instance.
Constraints height of the view to a constant value.
- Parameter _ height: A CGFloat value.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func bottomLeft(bottom: CGFloat = 0, left: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return bottomLeft(v, bottom: bottom, left: left)
func height(_ height: CGFloat) -> Layout {
return constraint(.constantHeight, constants: height)
}
}
public extension Layout {
/**
A child view is aligned from the bottom right with optional bottom and right padding.
- Parameter child: A child UIView to layout.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Parameter right: A CGFloat value for padding the right side.
- Returns: The current Layout instance.
Constraints top of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func bottomRight(_ child: UIView, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.bottomRight(parent: v, child: child, bottom: bottom, right: right)
return self
func top(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
return constraint(.top, to: anchor, constant: offset)
}
/**
A child view is aligned from the bottom right with optional bottom and right padding.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Parameter right: A CGFloat value for padding the right side.
- Returns: The current Layout instance.
Constraints left of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func bottomRight(bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return bottomRight(v, bottom: bottom, right: right)
func left(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
return constraint(.left, to: anchor, constant: offset)
}
/**
A child view is aligned at the center with an optional offsetX and offsetY value.
- Parameter child: A child UIView to layout.
- Parameter offsetX: A CGFloat value for the offset along the x axis.
- Parameter offsetX: A CGFloat value for the offset along the y axis.
- Returns: The current Layout instance.
Constraints right of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func center(_ child: UIView, offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.center(parent: v, child: child, offsetX: offsetX, offsetY: offsetY)
return self
func right(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
return constraint(.right, to: anchor, constant: -offset)
}
/**
A child view is aligned at the center with an optional offsetX and offsetY value.
- Parameter offsetX: A CGFloat value for the offset along the x axis.
- Parameter offsetX: A CGFloat value for the offset along the y axis.
- Returns: The current Layout instance.
Constraints leading of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func center(offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return center(v, offsetX: offsetX, offsetY: offsetY)
func leading(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
return constraint(.leading, to: anchor, constant: offset)
}
/**
A child view is aligned at the center horizontally with an optional offset value.
- Parameter child: A child UIView to layout.
- Parameter offset: A CGFloat value for the offset along the x axis.
- Returns: The current Layout instance.
Constraints trailing of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func centerHorizontally(_ child: UIView, offset: CGFloat = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.centerHorizontally(parent: v, child: child, offset: offset)
return self
func trailing(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
return constraint(.trailing, to: anchor, constant: -offset)
}
/**
A child view is aligned at the center horizontally with an optional offset value.
- Parameter offset: A CGFloat value for the offset along the x axis.
- Returns: The current Layout instance.
Constraints bottom of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func centerHorizontally(offset: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return centerHorizontally(v, offset: offset)
func bottom(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
return constraint(.bottom, to: anchor, constant: -offset)
}
/**
A child view is aligned at the center vertically with an optional offset value.
- Parameter child: A child UIView to layout.
- Parameter offset: A CGFloat value for the offset along the y axis.
- Returns: The current Layout instance.
Constraints top-leading of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter top: A CGFloat offset for top.
- Parameter leading: A CGFloat offset for leading.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func centerVertically(_ child: UIView, offset: CGFloat = 0) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.centerVertically(parent: v, child: child, offset: offset)
return self
func topLeading(_ anchor: LayoutAnchorable, top: CGFloat = 0, leading: CGFloat = 0) -> Layout {
return constraint(.topLeading, to: anchor, constants: top, leading)
}
/**
A child view is aligned at the center vertically with an optional offset value.
- Parameter offset: A CGFloat value for the offset along the y axis.
- Returns: The current Layout instance.
Constraints top-trailing of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter top: A CGFloat offset for top.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/
@discardableResult
public func centerVertically(offset: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return centerVertically(v, offset: offset)
func topTrailing(_ anchor: LayoutAnchorable, top: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
return constraint(.topTrailing, to: anchor, constants: top, -trailing)
}
}
fileprivate extension Layout {
/**
Updates the consraints for a given view.
- Parameter for view: A UIView.
Constraints bottom-leading of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter leading: A CGFloat offset for leading.
- Returns: A Layout instance to allow chaining.
*/
class func updateConstraints(for view: UIView) {
view.setNeedsUpdateConstraints()
view.updateConstraintsIfNeeded()
view.setNeedsLayout()
view.layoutIfNeeded()
@discardableResult
func bottomLeading(_ anchor: LayoutAnchorable, bottom: CGFloat = 0, leading: CGFloat = 0) -> Layout {
return constraint(.bottomLeading, to: anchor, constants: -bottom, leading)
}
/**
Updates the constraints for a given Array of views.
- Parameter for [view]: An Array of UIViews.
Constraints bottom-trailing of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/
class func updateConstraints(for views: [UIView]) {
for v in views {
updateConstraints(for: v)
}
@discardableResult
func bottomTrailing(_ anchor: LayoutAnchorable, bottom: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
return constraint(.bottomTrailing, to: anchor, constants: -bottom, -trailing)
}
}
/// Layout
extension Layout {
/**
Sets the width of a view.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter width: A CGFloat value.
Constraints leading and trailing of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter leading: A CGFloat offset for leading.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/
public class func width(parent: UIView, child: UIView, width: CGFloat = 0) {
prepareForConstraint(parent, child: child)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: width))
updateConstraints(for: child)
@discardableResult
func leadingTrailing(_ anchor: LayoutAnchorable, leading: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
return constraint(.leadingTrailing, to: anchor, constants: leading, -trailing)
}
/**
Sets the height of a view.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter height: A CGFloat value.
Constraints top-left of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter top: A CGFloat offset for top.
- Parameter left: A CGFloat offset for left.
- Returns: A Layout instance to allow chaining.
*/
public class func height(parent: UIView, child: UIView, height: CGFloat = 0) {
prepareForConstraint(parent, child: child)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: height))
updateConstraints(for: child)
@discardableResult
func topLeft(_ anchor: LayoutAnchorable, top: CGFloat = 0, left: CGFloat = 0) -> Layout {
return constraint(.topLeft, to: anchor, constants: top, left)
}
/**
Sets the width and height of a view.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter size: A CGSize value.
Constraints top-right of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter top: A CGFloat offset for top.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
public class func size(parent: UIView, child: UIView, size: CGSize = CGSize.zero) {
Layout.width(parent: parent, child: child, width: size.width)
Layout.height(parent: parent, child: child, height: size.height)
@discardableResult
func topRight(_ anchor: LayoutAnchorable, top: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.topRight, to: anchor, constants: top, -right)
}
/**
A collection of children views are horizontally stretched with optional left,
right padding and interim interimSpace.
- Parameter parent: A parent UIView context.
- Parameter children: An Array UIView to layout.
- Parameter left: A CGFloat value for padding the left side.
- Parameter right: A CGFloat value for padding the right side.
- Parameter interimSpace: A CGFloat value for interim interimSpace.
Constraints bottom-left of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter left: A CGFloat offset for left.
- Returns: A Layout instance to allow chaining.
*/
public class func horizontally(parent: UIView, children: [UIView], left: CGFloat = 0, right: CGFloat = 0, interimSpace: InterimSpace = 0) {
prepareForConstraint(parent, children: children)
if 0 < children.count {
parent.addConstraint(NSLayoutConstraint(item: children[0], attribute: .left, relatedBy: .equal, toItem: parent, attribute: .left, multiplier: 1, constant: left))
for i in 1..<children.count {
parent.addConstraint(NSLayoutConstraint(item: children[i], attribute: .left, relatedBy: .equal, toItem: children[i - 1], attribute: .right, multiplier: 1, constant: interimSpace))
parent.addConstraint(NSLayoutConstraint(item: children[i], attribute: .width, relatedBy: .equal, toItem: children[0], attribute: .width, multiplier: 1, constant: 0))
}
parent.addConstraint(NSLayoutConstraint(item: children[children.count - 1], attribute: .right, relatedBy: .equal, toItem: parent, attribute: .right, multiplier: 1, constant: -right))
}
updateConstraints(for: children)
@discardableResult
func bottomLeft(_ anchor: LayoutAnchorable, bottom: CGFloat = 0, left: CGFloat = 0) -> Layout {
return constraint(.bottomLeft, to: anchor, constants: -bottom, left)
}
/**
A collection of children views are vertically stretched with optional top,
bottom padding and interim interimSpace.
- Parameter parent: A parent UIView context.
- Parameter children: An Array UIView to layout.
- Parameter top: A CGFloat value for padding the top side.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Parameter interimSpace: A CGFloat value for interim interimSpace.
Constraints bottom-right of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
public class func vertically(parent: UIView, children: [UIView], top: CGFloat = 0, bottom: CGFloat = 0, interimSpace: InterimSpace = 0) {
prepareForConstraint(parent, children: children)
if 0 < children.count {
parent.addConstraint(NSLayoutConstraint(item: children[0], attribute: .top, relatedBy: .equal, toItem: parent, attribute: .top, multiplier: 1, constant: top))
for i in 1..<children.count {
parent.addConstraint(NSLayoutConstraint(item: children[i], attribute: .top, relatedBy: .equal, toItem: children[i - 1], attribute: .bottom, multiplier: 1, constant: interimSpace))
parent.addConstraint(NSLayoutConstraint(item: children[i], attribute: .height, relatedBy: .equal, toItem: children[0], attribute: .height, multiplier: 1, constant: 0))
}
parent.addConstraint(NSLayoutConstraint(item: children[children.count - 1], attribute: .bottom, relatedBy: .equal, toItem: parent, attribute: .bottom, multiplier: 1, constant: -bottom))
}
updateConstraints(for: children)
@discardableResult
func bottomRight(_ anchor: LayoutAnchorable, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.bottomRight, to: anchor, constants: -bottom, -right)
}
/**
A child view is horizontally stretched with optional left and right padding.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter left: A CGFloat value for padding the left side.
- Parameter right: A CGFloat value for padding the right side.
Constraints left and right of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter left: A CGFloat offset for left.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
public class func horizontally(parent: UIView, child: UIView, left: CGFloat = 0, right: CGFloat = 0) {
prepareForConstraint(parent, child: child)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .left, relatedBy: .equal, toItem: parent, attribute: .left, multiplier: 1, constant: left))
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .right, relatedBy: .equal, toItem: parent, attribute: .right, multiplier: 1, constant: -right))
updateConstraints(for: child)
@discardableResult
func leftRight(_ anchor: LayoutAnchorable, left: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.leftRight, to: anchor, constants: left, -right)
}
/**
A child view is vertically stretched with optional left and right padding.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter top: A CGFloat value for padding the top side.
- Parameter bottom: A CGFloat value for padding the bottom side.
Constraints top and bottom of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter top: A CGFloat offset for top.
- Parameter bottom: A CGFloat offset for bottom.
- Returns: A Layout instance to allow chaining.
*/
public class func vertically(parent: UIView, child: UIView, top: CGFloat = 0, bottom: CGFloat = 0) {
prepareForConstraint(parent, child: child)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .top, relatedBy: .equal, toItem: parent, attribute: .top, multiplier: 1, constant: top))
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .bottom, relatedBy: .equal, toItem: parent, attribute: .bottom, multiplier: 1, constant: -bottom))
updateConstraints(for: child)
@discardableResult
func topBottom(_ anchor: LayoutAnchorable, top: CGFloat = 0, bottom: CGFloat = 0) -> Layout {
return constraint(.topBottom, to: anchor, constants: top, -bottom)
}
/**
A child view is vertically and horizontally stretched with optional top, left, bottom and right padding.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter top: A CGFloat value for padding the top side.
- Parameter left: A CGFloat value for padding the left side.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Parameter right: A CGFloat value for padding the right side.
Constraints center of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter offsetX: A CGFloat offset for horizontal center.
- Parameter offsetY: A CGFloat offset for vertical center.
- Returns: A Layout instance to allow chaining.
*/
public class func edges(parent: UIView, child: UIView, top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) {
horizontally(parent: parent, child: child, left: left, right: right)
vertically(parent: parent, child: child, top: top, bottom: bottom)
@discardableResult
func center(_ anchor: LayoutAnchorable, offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout {
return constraint(.center, to: anchor, constants: offsetX, offsetY)
}
/**
A child view is aligned from the top with optional top padding.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter top: A CGFloat value for padding the top side.
- Returns: The current Layout instance.
Constraints horizontal center of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
public class func top(parent: UIView, child: UIView, top: CGFloat = 0) {
prepareForConstraint(parent, child: child)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .top, relatedBy: .equal, toItem: parent, attribute: .top, multiplier: 1, constant: top))
updateConstraints(for: child)
@discardableResult
func centerX(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
return constraint(.centerX, to: anchor, constant: offset)
}
/**
A child view is aligned from the left with optional left padding.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter left: A CGFloat value for padding the left side.
- Returns: The current Layout instance.
Constraints vertical center of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
public class func left(parent: UIView, child: UIView, left: CGFloat = 0) {
prepareForConstraint(parent, child: child)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .left, relatedBy: .equal, toItem: parent, attribute: .left, multiplier: 1, constant: left))
updateConstraints(for: child)
@discardableResult
func centerY(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
return constraint(.centerY, to: anchor, constant: offset)
}
/**
A child view is aligned from the bottom with optional bottom padding.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Returns: The current Layout instance.
Constraints height of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
public class func bottom(parent: UIView, child: UIView, bottom: CGFloat = 0) {
prepareForConstraint(parent, child: child)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .bottom, relatedBy: .equal, toItem: parent, attribute: .bottom, multiplier: 1, constant: -bottom))
updateConstraints(for: child)
@discardableResult
func width(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
return constraint(.width, to: anchor, constant: offset)
}
/**
A child view is aligned from the right with optional right padding.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter right: A CGFloat value for padding the right side.
- Returns: The current Layout instance.
Constraints height of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/
public class func right(parent: UIView, child: UIView, right: CGFloat = 0) {
prepareForConstraint(parent, child: child)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .right, relatedBy: .equal, toItem: parent, attribute: .right, multiplier: 1, constant: -right))
updateConstraints(for: child)
@discardableResult
func height(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
return constraint(.height, to: anchor, constant: offset)
}
/**
A child view is aligned from the top left with optional top and left padding.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter top: A CGFloat value for padding the top side.
- Parameter left: A CGFloat value for padding the left side.
- Returns: The current Layout instance.
Constraints edges of the view to the given anchor.
- Parameter _ anchor: A LayoutAnchorable.
- Parameter top: A CGFloat offset for top.
- Parameter left: A CGFloat offset for left.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/
public class func topLeft(parent: UIView, child: UIView, top t: CGFloat = 0, left l: CGFloat = 0) {
top(parent: parent, child: child, top: t)
left(parent: parent, child: child, left: l)
@discardableResult
func edges(_ anchor: LayoutAnchorable, top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.edges, to: anchor, constants: top, left, -bottom, -right)
}
}
private extension Layout {
/**
A child view is aligned from the top right with optional top and right padding.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter top: A CGFloat value for padding the top side.
- Parameter right: A CGFloat value for padding the right side.
- Returns: The current Layout instance.
Constraints the view to its parent according to the provided attribute.
If the constraint already exists, will update its constant.
- Parameter _ attribute: A LayoutAttribute.
- Parameter constant: A CGFloat.
- Returns: A Layout instance to allow chaining.
*/
public class func topRight(parent: UIView, child: UIView, top t: CGFloat = 0, right r: CGFloat = 0) {
top(parent: parent, child: child, top: t)
right(parent: parent, child: child, right: r)
func constraint(_ attribute: LayoutAttribute, constant: CGFloat) -> Layout {
return constraint([attribute], constants: constant)
}
/**
A child view is aligned from the bottom left with optional bottom and left padding.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Parameter left: A CGFloat value for padding the left side.
- Returns: The current Layout instance.
Constraints the view to its parent according to the provided attributes.
If any of the constraints already exists, will update its constant.
- Parameter _ attributes: An array of LayoutAttribute.
- Parameter constants: A list of CGFloat.
- Returns: A Layout instance to allow chaining.
*/
public class func bottomLeft(parent: UIView, child: UIView, bottom b: CGFloat = 0, left l: CGFloat = 0) {
bottom(parent: parent, child: child, bottom: b)
left(parent: parent, child: child, left: l)
}
func constraint(_ attributes: [LayoutAttribute], constants: CGFloat...) -> Layout {
var attributes = attributes
var anchor: LayoutAnchor!
/**
A child view is aligned from the bottom right with optional bottom and right padding.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Parameter right: A CGFloat value for padding the right side.
- Returns: The current Layout instance.
*/
public class func bottomRight(parent: UIView, child: UIView, bottom b: CGFloat = 0, right r: CGFloat = 0) {
bottom(parent: parent, child: child, bottom: b)
right(parent: parent, child: child, right: r)
if attributes == .constantHeight || attributes == .constantWidth {
attributes.removeLast()
anchor = LayoutAnchor(constraintable: nil, attributes: [.notAnAttribute])
} else {
guard parent != nil else {
fatalError("[Material Error: Constraint requires view to have parent.")
}
/**
A child view is aligned at the center with an optional offsetX and offsetY value.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter offsetX: A CGFloat value for the offset along the x axis.
- Parameter offsetX: A CGFloat value for the offset along the y axis.
- Returns: The current Layout instance.
*/
public class func center(parent: UIView, child: UIView, offsetX: CGFloat = 0, offsetY: CGFloat = 0) {
centerHorizontally(parent: parent, child: child, offset: offsetX)
centerVertically(parent: parent, child: child, offset: offsetY)
anchor = LayoutAnchor(constraintable: parent, attributes: attributes)
}
return constraint(attributes, to: anchor, constants: constants)
}
/**
A child view is aligned at the center horizontally with an optional offset value.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter offset: A CGFloat value for the offset along the y axis.
- Returns: The current Layout instance.
Constraints the view to the given anchor according to the provided attribute.
If the constraint already exists, will update its constant.
- Parameter _ attribute: A LayoutAttribute.
- Parameter to anchor: A LayoutAnchorable.
- Parameter constant: A CGFloat.
- Returns: A Layout instance to allow chaining.
*/
public class func centerHorizontally(parent: UIView, child: UIView, offset: CGFloat = 0) {
prepareForConstraint(parent, child: child)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .centerX, relatedBy: .equal, toItem: parent, attribute: .centerX, multiplier: 1, constant: offset))
updateConstraints(for: child)
func constraint(_ attribute: LayoutAttribute, to anchor: LayoutAnchorable, constant: CGFloat) -> Layout {
return constraint([attribute], to: anchor, constants: constant)
}
/**
A child view is aligned at the center vertically with an optional offset value.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter offset: A CGFloat value for the offset along the y axis.
- Returns: The current Layout instance.
Constraints the view to the given anchor according to the provided attributes.
If any of the constraints already exists, will update its constant.
- Parameter _ attributes: An array of LayoutAttribute.
- Parameter to anchor: A LayoutAnchorable.
- Parameter constants: A list of CGFloat.
- Returns: A Layout instance to allow chaining.
*/
public class func centerVertically(parent: UIView, child: UIView, offset: CGFloat = 0) {
prepareForConstraint(parent, child: child)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .centerY, relatedBy: .equal, toItem: parent, attribute: .centerY, multiplier: 1, constant: offset))
updateConstraints(for: child)
func constraint(_ attributes: [LayoutAttribute], to anchor: LayoutAnchorable, constants: CGFloat...) -> Layout {
return constraint(attributes, to: anchor, constants: constants)
}
/**
Creats an Array with a NSLayoutConstraint value.
- Parameter format: The VFL format string.
- Parameter options: Additional NSLayoutFormatOptions.
- Parameter metrics: An optional Dictionary<String, Any> of metric key / value pairs.
- Parameter views: A Dictionary<String, Any> of view key / value pairs.
- Returns: The Array<NSLayoutConstraint> instance.
Constraints the view to the given anchor according to the provided attributes.
If any of the constraints already exists, will update its constant.
- Parameter _ attributes: An array of LayoutAttribute.
- Parameter to anchor: A LayoutAnchorable.
- Parameter constants: An array of CGFloat.
- Returns: A Layout instance to allow chaining.
*/
public class func constraint(format: String, options: NSLayoutConstraint.FormatOptions, metrics: [String: Any]?, views: [String: Any]) -> [NSLayoutConstraint] {
for (_, a) in views {
if let v = a as? UIView {
v.translatesAutoresizingMaskIntoConstraints = false
}
func constraint(_ attributes: [LayoutAttribute], to anchor: LayoutAnchorable, constants: [CGFloat]) -> Layout {
let from = LayoutAnchor(constraintable: constraintable, attributes: attributes)
var to = anchor as? LayoutAnchor
if to?.attributes.isEmpty ?? true {
let v = (anchor as? UIView) ?? (anchor as? LayoutAnchor)?.constraintable
to = LayoutAnchor(constraintable: v, attributes: attributes)
}
return NSLayoutConstraint.constraints(
withVisualFormat: format,
options: options,
metrics: metrics,
views: views
)
let constraint = LayoutConstraint(fromAnchor: from, toAnchor: to!, constants: constants)
let constraints = (view?.constraints ?? []) + (view?.superview?.constraints ?? [])
let newConstraints = constraint.constraints
for newConstraint in newConstraints {
guard let activeConstraint = constraints.first(where: { $0.equalTo(newConstraint) }) else {
newConstraint.isActive = true
view?.lastConstraint = newConstraint
continue
}
/**
Prepares the relationship between the parent view context and child view
to layout. If the child is not already added to the view hierarchy as the
parent's child, then it is added.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
*/
private class func prepareForConstraint(_ parent: UIView, child: UIView) {
if parent != child.superview {
child.removeFromSuperview()
parent.addSubview(child)
activeConstraint.constant = newConstraint.constant
}
child.translatesAutoresizingMaskIntoConstraints = false
return self
}
}
private extension NSLayoutConstraint {
/**
Prepares the relationship between the parent view context and an Array of
child UIViews.
- Parameter parent: A parent UIView context.
- Parameter children: An Array of UIViews.
Checks if the constraint is equal to given constraint.
- Parameter _ other: An NSLayoutConstraint.
- Returns: A Bool indicating whether constraints are equal.
*/
private class func prepareForConstraint(_ parent: UIView, children: [UIView]) {
for v in children {
prepareForConstraint(parent, child: v)
}
func equalTo(_ other: NSLayoutConstraint) -> Bool {
return firstItem === other.firstItem
&& secondItem === other.secondItem
&& firstAttribute == other.firstAttribute
&& secondAttribute == other.secondAttribute
}
}
/// A memory reference to the LayoutKey instance for UIView extensions.
fileprivate var LayoutKey: UInt8 = 0
/// A memory reference to the lastConstraint of UIView.
private var LastConstraintKey: UInt8 = 0
/// Layout extension for UIView.
extension UIView {
/// Layout reference.
public private(set) var layout: Layout {
private extension UIView {
/**
The last consntraint that's created by Layout system.
Used to set multiplier/priority on the last constraint.
*/
var lastConstraint: NSLayoutConstraint? {
get {
return AssociatedObject.get(base: self, key: &LayoutKey) {
return Layout(parent: self)
return AssociatedObject.get(base: self, key: &LastConstraintKey) {
nil
}
}
set(value) {
AssociatedObject.set(base: self, key: &LayoutKey, value: value)
}
AssociatedObject.set(base: self, key: &LastConstraintKey, value: value)
}
/**
Used to chain layout constraints on a child context.
- Parameter child: A child UIView to layout.
- Returns: The current Layout instance.
*/
public func layout(_ child: UIView) -> Layout {
return Layout(parent: self, child: child)
}
}
/*
* Copyright (C) 2015 - 2018, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import UIKit
/// A protocol that's conformed by UIView, LayoutAnchor, and Layout.
public protocol LayoutAnchorable { }
extension UIView: LayoutAnchorable { }
extension Layout: LayoutAnchorable { }
extension LayoutAnchor: LayoutAnchorable { }
public struct LayoutAnchor {
/// A weak reference to the constraintable.
weak var constraintable: Constraintable?
/// An array of LayoutAttribute for the view.
let attributes: [LayoutAttribute]
/**
An initializer taking constraintable and anchor attributes.
- Parameter view: A Constraintable.
- Parameter attributes: An array of LayoutAtrribute.
*/
init(constraintable: Constraintable?, attributes: [LayoutAttribute] = []) {
self.constraintable = constraintable
self.attributes = attributes
}
}
public extension LayoutAnchor {
/// A layout anchor representing top of the view.
var top: LayoutAnchor {
return anchor(.top)
}
/// A layout anchor representing bottom of the view.
var bottom: LayoutAnchor {
return anchor(.bottom)
}
/// A layout anchor representing left of the view.
var left: LayoutAnchor {
return anchor(.left)
}
/// A layout anchor representing right of the view.
var right: LayoutAnchor {
return anchor(.right)
}
/// A layout anchor representing leading of the view.
var leading: LayoutAnchor {
return anchor(.leading)
}
/// A layout anchor representing trailing of the view.
var trailing: LayoutAnchor {
return anchor(.trailing)
}
/// A layout anchor representing top-left of the view.
var topLeft: LayoutAnchor {
return acnhor(.topLeft)
}
/// A layout anchor representing top-right of the view.
var topRight: LayoutAnchor {
return acnhor(.topRight)
}
/// A layout anchor representing bottom-left of the view.
var bottomLeft: LayoutAnchor {
return acnhor(.bottomLeft)
}
/// A layout anchor representing bottom-right of the view.
var bottomRight: LayoutAnchor {
return acnhor(.bottomRight)
}
/// A layout anchor representing top-leading of the view.
var topLeading: LayoutAnchor {
return acnhor(.topLeading)
}
/// A layout anchor representing top-trailing of the view.
var topTrailing: LayoutAnchor {
return acnhor(.topTrailing)
}
/// A layout anchor representing bottom-leading of the view.
var bottomLeading: LayoutAnchor {
return acnhor(.bottomLeading)
}
/// A layout anchor representing bottom-trailing of the view.
var bottomTrailing: LayoutAnchor {
return acnhor(.bottomTrailing)
}
/// A layout anchor representing top and bottom of the view.
var topBottom: LayoutAnchor {
return acnhor(.topBottom)
}
/// A layout anchor representing left and right of the view.
var leftRight: LayoutAnchor {
return acnhor(.leftRight)
}
/// A layout anchor representing leading and trailing of the view.
var leadingTrailing: LayoutAnchor {
return acnhor(.leadingTrailing)
}
/// A layout anchor representing center of the view.
var center: LayoutAnchor {
return acnhor(.center)
}
/// A layout anchor representing horizontal center of the view.
var centerX: LayoutAnchor {
return anchor(.centerX)
}
/// A layout anchor representing vertical center of the view.
var centerY: LayoutAnchor {
return anchor(.centerY)
}
/// A layout anchor representing top, left, bottom and right of the view.
var edges: LayoutAnchor {
return acnhor(.edges)
}
/// A layout anchor representing width of the view.
var width: LayoutAnchor {
return anchor(.width)
}
/// A layout anchor representing height of the view.
var height: LayoutAnchor {
return anchor(.height)
}
}
private extension LayoutAnchor {
/**
Creates LayoutAnchor with the given attribute.
- Parameter attribute: A LayoutAttribute.
- Returns: A LayoutAnchor.
*/
func anchor(_ attribute: LayoutAttribute) -> LayoutAnchor {
return LayoutAnchor(constraintable: constraintable, attributes: [attribute])
}
/**
Creates LayoutAnchor with the given attributes.
- Parameter attributes: An array of LayoutAttribute.
- Returns: A LayoutAnchor.
*/
func acnhor(_ attributes: [LayoutAttribute]) -> LayoutAnchor {
return LayoutAnchor(constraintable: constraintable, attributes: attributes)
}
}
/*
* Copyright (C) 2015 - 2018, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import UIKit
/// A typealias for NSLayoutConstraint.Attribute
internal typealias LayoutAttribute = NSLayoutConstraint.Attribute
internal extension Array where Element == LayoutAttribute {
/// A LayoutAttribute array containing top and left.
static var topLeft: [LayoutAttribute] {
return [.top, .left]
}
/// A LayoutAttribute array containing top and right.
static var topRight: [LayoutAttribute] {
return [.top, .right]
}
/// A LayoutAttribute array containing bottom and left.
static var bottomLeft: [LayoutAttribute] {
return [.bottom, .left]
}
/// A LayoutAttribute array containing bottom and right.
static var bottomRight: [LayoutAttribute] {
return [.bottom, .right]
}
/// A LayoutAttribute array containing left and right.
static var leftRight: [LayoutAttribute] {
return [.left, .right]
}
/// A LayoutAttribute array containing top and leading.
static var topLeading: [LayoutAttribute] {
return [.top, .leading]
}
/// A LayoutAttribute array containing top and trailing.
static var topTrailing: [LayoutAttribute] {
return [.top, .trailing]
}
/// A LayoutAttribute array containing bottom and leading.
static var bottomLeading: [LayoutAttribute] {
return [.bottom, .leading]
}
/// A LayoutAttribute array containing bottom and trailing.
static var bottomTrailing: [LayoutAttribute] {
return [.bottom, .trailing]
}
/// A LayoutAttribute array containing left and trailing.
static var leadingTrailing: [LayoutAttribute] {
return [.leading, .trailing]
}
/// A LayoutAttribute array containing top and bottom.
static var topBottom: [LayoutAttribute] {
return [.top, .bottom]
}
/// A LayoutAttribute array containing centerX and centerY.
static var center: [LayoutAttribute] {
return [.centerX, .centerY]
}
/// A LayoutAttribute array containing top, left, bottom and right.
static var edges: [LayoutAttribute] {
return [.top, .left, .bottom, .right]
}
/// A LayoutAttribute array for constant height.
static var constantHeight: [LayoutAttribute] {
return [.height, .notAnAttribute]
}
/// A LayoutAttribute array for constant width.
static var constantWidth: [LayoutAttribute] {
return [.width, .notAnAttribute]
}
}
/*
* Copyright (C) 2015 - 2018, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import UIKit
internal struct LayoutConstraint {
/// `From` anchor for the constraint.
private let fromAnchor: LayoutAnchor
/// `To` anchor for the constraint.
private let toAnchor: LayoutAnchor
/// An array of constants for the constraint.
private var constants: [CGFloat]
/**
An initializer taking `from` and `to` anchors and constants for the constraint.
- Parameter fromAnchor: A LayoutAnchor.
- Parameter toAnchor: A LayoutAnchor.
- Parameter constants: An array of CGFloat.
*/
init(fromAnchor: LayoutAnchor, toAnchor: LayoutAnchor, constants: [CGFloat]) {
self.fromAnchor = fromAnchor
self.toAnchor = toAnchor
self.constants = constants
}
}
internal extension LayoutConstraint {
/// Creates an array of NSLayoutConstraint from a LayoutConstraint.
var constraints: [NSLayoutConstraint] {
guard fromAnchor.attributes.count == toAnchor.attributes.count else {
fatalError("[Material Error: The number of attributes of anchors does not match.]")
}
guard fromAnchor.attributes.count == constants.count else {
fatalError("[Material Error: The number of constants does not match the number of constraints.]")
}
var v: [NSLayoutConstraint] = []
zip(zip(fromAnchor.attributes, toAnchor.attributes), constants).forEach {
v.append(NSLayoutConstraint(item: fromAnchor.constraintable as Any,
attribute: $0.0,
relatedBy: .equal,
toItem: toAnchor.constraintable,
attribute: $0.1,
multiplier: 1,
constant: $1))
}
return v
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment