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 @@ ...@@ -174,6 +174,9 @@
9D054A6520D175AC00D0528D /* Material+UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D054A6320D175AC00D0528D /* Material+UIButton.swift */; }; 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 */; }; 9D054A6620D175AC00D0528D /* Material+UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D054A6420D175AC00D0528D /* Material+UILabel.swift */; };
9D39A81B20FE8ED100BA8FA1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D39A81A20FE8ED100BA8FA1 /* ViewController.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 */; }; 9D9089B92118914500605DC9 /* Editor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D9089B82118914500605DC9 /* Editor.swift */; };
9DE25DE02170D7AF000C04DF /* Dialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE25DDF2170D7AF000C04DF /* Dialog.swift */; }; 9DE25DE02170D7AF000C04DF /* Dialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE25DDF2170D7AF000C04DF /* Dialog.swift */; };
9DE25DE22170D7C0000C04DF /* DialogController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE25DE12170D7C0000C04DF /* DialogController.swift */; }; 9DE25DE22170D7C0000C04DF /* DialogController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE25DE12170D7C0000C04DF /* DialogController.swift */; };
...@@ -300,6 +303,9 @@ ...@@ -300,6 +303,9 @@
9D054A6320D175AC00D0528D /* Material+UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIButton.swift"; sourceTree = "<group>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 9DE25DE12170D7C0000C04DF /* DialogController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DialogController.swift; sourceTree = "<group>"; };
...@@ -652,6 +658,9 @@ ...@@ -652,6 +658,9 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
96BCB7811CB40DC500C806FE /* Layout.swift */, 96BCB7811CB40DC500C806FE /* Layout.swift */,
9D494A39217F6B70003D66F1 /* LayoutAnchor.swift */,
9D494A37217F6B63003D66F1 /* LayoutAttribute.swift */,
9D494A3B217F6B7D003D66F1 /* LayoutConstraint.swift */,
); );
name = Layout; name = Layout;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -993,6 +1002,7 @@ ...@@ -993,6 +1002,7 @@
9DF352461FED210000B2A11B /* CheckButton.swift in Sources */, 9DF352461FED210000B2A11B /* CheckButton.swift in Sources */,
965E810A1DD4D5C800D61E4B /* Font.swift in Sources */, 965E810A1DD4D5C800D61E4B /* Font.swift in Sources */,
965E810B1DD4D5C800D61E4B /* RobotoFont.swift in Sources */, 965E810B1DD4D5C800D61E4B /* RobotoFont.swift in Sources */,
9D494A3C217F6B7D003D66F1 /* LayoutConstraint.swift in Sources */,
965E810C1DD4D5C800D61E4B /* DynamicFontType.swift in Sources */, 965E810C1DD4D5C800D61E4B /* DynamicFontType.swift in Sources */,
965E81101DD4D5C800D61E4B /* NavigationBar.swift in Sources */, 965E81101DD4D5C800D61E4B /* NavigationBar.swift in Sources */,
965E81111DD4D5C800D61E4B /* NavigationController.swift in Sources */, 965E81111DD4D5C800D61E4B /* NavigationController.swift in Sources */,
...@@ -1015,6 +1025,7 @@ ...@@ -1015,6 +1025,7 @@
965E81211DD4D5C800D61E4B /* TextStorage.swift in Sources */, 965E81211DD4D5C800D61E4B /* TextStorage.swift in Sources */,
965E81221DD4D5C800D61E4B /* TextView.swift in Sources */, 965E81221DD4D5C800D61E4B /* TextView.swift in Sources */,
965E80E71DD4C55200D61E4B /* Material+UIView.swift in Sources */, 965E80E71DD4C55200D61E4B /* Material+UIView.swift in Sources */,
9D494A38217F6B63003D66F1 /* LayoutAttribute.swift in Sources */,
96B8D22D20CF82D5008BD149 /* FABMenu.swift in Sources */, 96B8D22D20CF82D5008BD149 /* FABMenu.swift in Sources */,
965E80E81DD4C55200D61E4B /* Material+CALayer.swift in Sources */, 965E80E81DD4C55200D61E4B /* Material+CALayer.swift in Sources */,
965E80E91DD4C55200D61E4B /* Material+String.swift in Sources */, 965E80E91DD4C55200D61E4B /* Material+String.swift in Sources */,
...@@ -1054,6 +1065,7 @@ ...@@ -1054,6 +1065,7 @@
965E80D21DD4C50600D61E4B /* Color.swift in Sources */, 965E80D21DD4C50600D61E4B /* Color.swift in Sources */,
96BFC1541E5E486F0075DE1F /* SpringAnimation.swift in Sources */, 96BFC1541E5E486F0075DE1F /* SpringAnimation.swift in Sources */,
9D054A6620D175AC00D0528D /* Material+UILabel.swift in Sources */, 9D054A6620D175AC00D0528D /* Material+UILabel.swift in Sources */,
9D494A3A217F6B70003D66F1 /* LayoutAnchor.swift in Sources */,
965E80D31DD4C50600D61E4B /* Device.swift in Sources */, 965E80D31DD4C50600D61E4B /* Device.swift in Sources */,
965E80FD1DD4D59500D61E4B /* Toolbar.swift in Sources */, 965E80FD1DD4D59500D61E4B /* Toolbar.swift in Sources */,
965E80D41DD4C50600D61E4B /* Divider.swift in Sources */, 965E80D41DD4C50600D61E4B /* Divider.swift in Sources */,
......
...@@ -69,9 +69,10 @@ open class ErrorTextField: TextField { ...@@ -69,9 +69,10 @@ open class ErrorTextField: TextField {
get { get {
return !errorLabel.isHidden return !errorLabel.isHidden
} }
set { set(value) {
errorLabel.isHidden = !newValue errorLabel.isHidden = !value
detailLabel.isHidden = newValue detailLabel.isHidden = value
layoutSubviews()
} }
} }
......
...@@ -31,970 +31,797 @@ ...@@ -31,970 +31,797 @@
import UIKit import UIKit
import Motion import Motion
public class Layout { /// A protocol that's conformed by UIView and UILayoutGuide.
/// Parent UIView context. public protocol Constraintable: class { }
internal weak var parent: UIView?
/// Child UIView context. @available(iOS 9.0, *)
internal weak var child: UIView? extension UILayoutGuide: Constraintable { }
extension UIView: Constraintable { }
/// Layout extension for UIView.
public extension UIView {
/** /**
An initializer that takes in a parent context. Used to chain layout constraints on a child context.
- Parameter parent: An optional parent UIView. - Parameter child: A child UIView to layout.
- Returns: A Layout instance.
*/ */
public init(parent: UIView?) { func layout(_ child: UIView) -> Layout {
self.parent = parent 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. Anchor instance for safeAreaLayoutGuide.
- Parameter parent: An optional parent UIView. Below iOS 11, it will be same as view.anchor.
- Parameter child: An optional child UIView.
*/ */
public init(parent: UIView?, child: UIView?) { var safeAnchor: LayoutAnchor {
self.parent = parent if #available(iOS 11.0, *) {
self.child = child 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. An initializer taking Constraintable.
- Parameter function: A String representation of the function that - Parameter view: A Constraintable.
caused the issue.
- Returns: The current Layout instance.
*/ */
internal func debugParentNotAvailableMessage(function: String = #function) -> Layout { init(constraintable: Constraintable) {
debugPrint("[Material Layout Error: Parent view context is not available for \(function).") self.constraintable = constraintable
return self
} }
}
public extension Layout {
/** /**
Prints a debug message when the child context is not available. Sets multiplier of the last created constraint.
- Parameter function: A String representation of the function that Not meant for updating the multiplier as it will re-create the constraint.
caused the issue. - Parameter _ multiplier: A CGFloat multiplier.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
internal func debugChildNotAvailableMessage(function: String = #function) -> Layout { func multiply(_ multiplier: CGFloat) -> Layout {
debugPrint("[Material Layout Error: Child view context is not available for \(function).") return resetLastConstraint(multiplier: multiplier)
return self
} }
/** /**
Sets the width of a view. Sets priority of the last created constraint.
- Parameter child: A child UIView to layout. Not meant for updating the multiplier as it will re-create the constraint.
- Parameter width: A CGFloat value. - Parameter _ value: A Float priority.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult func priority(_ value: Float) -> Layout {
public func width(_ child: UIView, width: CGFloat) -> Layout { return priority(.init(rawValue: value))
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.width(parent: v, child: child, width: width)
return self
} }
/** /**
Sets the width of a view assuming a child context view. Sets priority of the last created constraint.
- Parameter width: A CGFloat value. Not meant for updating the priority as it will re-create the constraint.
- Returns: The current Layout instance. - Parameter _ priority: A UILayoutPriority.
- Returns: A Layout instance to allow chaining.
*/ */
@discardableResult func priority(_ priority: UILayoutPriority) -> Layout {
public func width(_ width: CGFloat) -> Layout { return resetLastConstraint(priority: priority)
guard let v = child else {
return debugChildNotAvailableMessage()
}
return self.width(v, width: width)
} }
/** /**
Sets the height of a view. Removes the last created constraint and creates new one with the new multiplier and/or priority (if provided).
- Parameter child: A child UIView to layout. - Parameter multiplier: An optional CGFloat.
- Parameter height: A CGFloat value. - Parameter priority: An optional UILayoutPriority.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult private func resetLastConstraint(multiplier: CGFloat? = nil, priority: UILayoutPriority? = nil) -> Layout {
public func height(_ child: UIView, height: CGFloat) -> Layout { guard let v = view?.lastConstraint, v.isActive else {
guard let v = parent else { return self
return debugParentNotAvailableMessage()
} }
self.child = child v.isActive = false
Layout.height(parent: v, child: child, height: height) 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 return self
} }
}
public extension Layout {
/** /**
Sets the height of a view assuming a child context view. Constraints top of the view to its parent's.
- Parameter height: A CGFloat value. - Parameter _ offset: A CGFloat offset.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func height(_ height: CGFloat) -> Layout { func top(_ offset: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.top, constant: offset)
return debugChildNotAvailableMessage()
}
return self.height(v, height: height)
} }
/** /**
Sets the width and height of a view. Constraints left of the view to its parent's.
- Parameter child: A child UIView to layout. - Parameter _ offset: A CGFloat offset.
- Parameter size: A CGSize value. - Returns: A Layout instance to allow chaining.
- Returns: The current Layout instance.
*/ */
@discardableResult @discardableResult
public func size(_ child: UIView, size: CGSize) -> Layout { func left(_ offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.left, constant: offset)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.size(parent: v, child: child, size: size)
return self
} }
/** /**
Sets the width and height of a view assuming a child context view. Constraints right of the view to its parent.
- Parameter size: A CGSize value. - Parameter _ offset: A CGFloat offset.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func size(_ size: CGSize = CGSize.zero) -> Layout { func right(_ offset: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.right, constant: -offset)
return debugChildNotAvailableMessage()
}
return self.size(v, size: size)
} }
/** /**
A collection of children views are horizontally stretched with optional left, Constraints leading of the view to its parent's.
right padding and interim interimSpace. - Parameter _ offset: A CGFloat offset.
- Parameter children: An Array UIView to layout. - Returns: A Layout instance to allow chaining.
- 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.
*/ */
@discardableResult @discardableResult
public func horizontally(_ children: [UIView], left: CGFloat = 0, right: CGFloat = 0, interimSpace: InterimSpace = 0) -> Layout { func leading(_ offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.leading, constant: offset)
return debugParentNotAvailableMessage()
}
Layout.horizontally(parent: v, children: children, left: left, right: right, interimSpace: interimSpace)
return self
} }
/** /**
A collection of children views are vertically stretched with optional top, Constraints trailing of the view to its parent.
bottom padding and interim interimSpace. - Parameter _ offset: A CGFloat offset.
- Parameter children: An Array UIView to layout. - Returns: A Layout instance to allow chaining.
- 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.
*/ */
@discardableResult @discardableResult
public func vertically(_ children: [UIView], top: CGFloat = 0, bottom: CGFloat = 0, interimSpace: InterimSpace = 0) -> Layout { func trailing(_ offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.trailing, constant: -offset)
return debugParentNotAvailableMessage()
}
Layout.vertically(parent: v, children: children, top: top, bottom: bottom, interimSpace: interimSpace)
return self
} }
/** /**
A child view is horizontally stretched with optional left and right padding. Constraints bottom of the view to its parent's.
- Parameter child: A child UIView to layout. - Parameter _ offset: A CGFloat offset.
- Parameter left: A CGFloat value for padding the left side. - Returns: A Layout instance to allow chaining.
- Parameter right: A CGFloat value for padding the right side.
- Returns: The current Layout instance.
*/ */
@discardableResult @discardableResult
public func horizontally(_ child: UIView, left: CGFloat = 0, right: CGFloat = 0) -> Layout { func bottom(_ offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.bottom, constant: -offset)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.horizontally(parent: v, child: child, left: left, right: right)
return self
} }
/** /**
A child view is horizontally stretched with optional left and right padding. Constraints top-left of the view to its parent's.
- Parameter left: A CGFloat value for padding the left side. - Parameter top: A CGFloat offset for top.
- Parameter right: A CGFloat value for padding the right side. - Parameter left: A CGFloat offset for left.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func horizontally(left: CGFloat = 0, right: CGFloat = 0) -> Layout { func topLeft(top: CGFloat = 0, left: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.topLeft, constants: top, left)
return debugChildNotAvailableMessage()
}
return horizontally(v, left: left, right: right)
} }
/** /**
A child view is vertically stretched with optional left and right padding. Constraints top-right of the view to its parent's.
- Parameter child: A child UIView to layout. - Parameter top: A CGFloat offset for top.
- Parameter top: A CGFloat value for padding the top side. - Parameter right: A CGFloat offset for right.
- Parameter bottom: A CGFloat value for padding the bottom side. - Returns: A Layout instance to allow chaining.
- Returns: The current Layout instance.
*/ */
@discardableResult @discardableResult
public func vertically(_ child: UIView, top: CGFloat = 0, bottom: CGFloat = 0) -> Layout { func topRight(top: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.topRight, constants: top, -right)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.vertically(parent: v, child: child, top: top, bottom: bottom)
return self
} }
/** /**
A child view is vertically stretched with optional left and right padding. Constraints bottom-left of the view to its parent's.
- Parameter top: A CGFloat value for padding the top side. - Parameter bottom: A CGFloat offset for bottom.
- Parameter bottom: A CGFloat value for padding the bottom side. - Parameter left: A CGFloat offset for left.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func vertically(top: CGFloat = 0, bottom: CGFloat = 0) -> Layout { func bottomLeft(bottom: CGFloat = 0, left: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.bottomLeft, constants: -bottom, left)
return debugChildNotAvailableMessage()
}
return vertically(v, top: top, bottom: bottom)
} }
/** /**
A child view is vertically and horizontally stretched with optional top, left, bottom and right padding. Constraints bottom-right of the view to its parent's.
- Parameter child: A child UIView to layout. - Parameter bottom: A CGFloat offset for bottom.
- Parameter top: A CGFloat value for padding the top side. - Parameter right: A CGFloat offset for right.
- Parameter left: A CGFloat value for padding the left side. - Returns: A Layout instance to allow chaining.
- 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.
*/ */
@discardableResult @discardableResult
public func edges(_ child: UIView, top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout { func bottomRight(bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.bottomRight, constants: -bottom, -right)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.edges(parent: v, child: child, top: top, left: left, bottom: bottom, right: right)
return self
} }
/** /**
A child view is vertically and horizontally stretched with optional top, left, bottom and right padding. Constraints left and right of the view to its parent's.
- Parameter child: A child UIView to layout. - Parameter left: A CGFloat offset for left.
- Parameter top: A CGFloat value for padding the top side. - Parameter right: A CGFloat offset for right.
- Parameter left: A CGFloat value for padding the left side. - Returns: A Layout instance to allow chaining.
- 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.
*/ */
@discardableResult @discardableResult
public func edges(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout { func leftRight(left: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.leftRight, constants: left, -right)
return debugChildNotAvailableMessage()
}
return edges(v, top: top, left: left, bottom: bottom, right: right)
} }
/** /**
A child view is aligned from the top with optional top padding. Constraints top-leading of the view to its parent's.
- Parameter child: A child UIView to layout. - Parameter top: A CGFloat offset for top.
- Parameter top: A CGFloat value for padding the top side. - Parameter leading: A CGFloat offset for leading.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func top(_ child: UIView, top: CGFloat = 0) -> Layout { func topLeading(top: CGFloat = 0, leading: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.topLeading, constants: top, leading)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.top(parent: v, child: child, top: top)
return self
} }
/** /**
A child view is aligned from the top with optional top padding. Constraints top-trailing of the view to its parent's.
- Parameter top: A CGFloat value for padding the top side. - Parameter top: A CGFloat offset for top.
- Returns: The current Layout instance. - Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func top(_ top: CGFloat = 0) -> Layout { func topTrailing(top: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.topTrailing, constants: top, -trailing)
return debugChildNotAvailableMessage()
}
return self.top(v, top: top)
} }
/** /**
A child view is aligned from the left with optional left padding. Constraints bottom-leading of the view to its parent's.
- Parameter child: A child UIView to layout. - Parameter bottom: A CGFloat offset for bottom.
- Parameter left: A CGFloat value for padding the left side. - Parameter leading: A CGFloat offset for leading.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func left(_ child: UIView, left: CGFloat = 0) -> Layout { func bottomLeading(bottom: CGFloat = 0, leading: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.bottomLeading, constants: -bottom, leading)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.left(parent: v, child: child, left: left)
return self
} }
/** /**
A child view is aligned from the left with optional left padding. Constraints bottom-trailing of the view to its parent's.
- Parameter left: A CGFloat value for padding the left side. - Parameter bottom: A CGFloat offset for bottom.
- Returns: The current Layout instance. - Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func left(_ left: CGFloat = 0) -> Layout { func bottomTrailing(bottom: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.bottomTrailing, constants: -bottom, -trailing)
return debugChildNotAvailableMessage()
}
return self.left(v, left: left)
} }
/** /**
A child view is aligned from the bottom with optional bottom padding. Constraints leading and trailing of the view to its parent's.
- Parameter child: A child UIView to layout. - Parameter leading: A CGFloat offset for leading.
- Parameter bottom: A CGFloat value for padding the bottom side. - Parameter trailing: A CGFloat offset for trailing.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func bottom(_ child: UIView, bottom: CGFloat = 0) -> Layout { func leadingTrailing(leading: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.leadingTrailing, constants: leading, -trailing)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.bottom(parent: v, child: child, bottom: bottom)
return self
} }
/** /**
A child view is aligned from the bottom with optional bottom padding. Constraints top and bottom of the view to its parent's.
- Parameter bottom: A CGFloat value for padding the bottom side. - Parameter top: A CGFloat offset for top.
- Returns: The current Layout instance. - Parameter bottom: A CGFloat offset for bottom.
- Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func bottom(_ bottom: CGFloat = 0) -> Layout { func topBottom(top: CGFloat = 0, bottom: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.topBottom, constants: top, -bottom)
return debugChildNotAvailableMessage()
}
return self.bottom(v, bottom: bottom)
} }
/** /**
A child view is aligned from the right with optional right padding. Constraints center of the view to its parent's.
- Parameter child: A child UIView to layout. - Parameter offsetX: A CGFloat offset for horizontal center.
- Parameter right: A CGFloat value for padding the right side. - Parameter offsetY: A CGFloat offset for vertical center.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func right(_ child: UIView, right: CGFloat = 0) -> Layout { func center(offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.center, constants: offsetX, offsetY)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.right(parent: v, child: child, right: right)
return self
} }
/** /**
A child view is aligned from the right with optional right padding. Constraints horizontal center of the view to its parent's.
- Parameter right: A CGFloat value for padding the right side. - Parameter _ offset: A CGFloat offset.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func right(_ right: CGFloat = 0) -> Layout { func centerX(_ offset: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.centerX, constant: offset)
return debugChildNotAvailableMessage()
}
return self.right(v, right: right)
} }
/** /**
A child view is aligned from the top left with optional top and left padding. Constraints vertical center of the view to its parent's.
- Parameter child: A child UIView to layout. - Parameter _ offset: A CGFloat offset.
- Parameter top: A CGFloat value for padding the top side. - Returns: A Layout instance to allow chaining.
- Parameter left: A CGFloat value for padding the left side.
- Returns: The current Layout instance.
*/ */
@discardableResult @discardableResult
public func topLeft(_ child: UIView, top: CGFloat = 0, left: CGFloat = 0) -> Layout { func centerY(_ offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.centerY, constant: offset)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.topLeft(parent: v, child: child, top: top, left: left)
return self
} }
/** /**
A child view is aligned from the top left with optional top and left padding. Constraints width of the view to its parent's.
- Parameter top: A CGFloat value for padding the top side. - Parameter offset: A CGFloat offset.
- Parameter left: A CGFloat value for padding the left side. - Returns: A Layout instance to allow chaining.
- Returns: The current Layout instance.
*/ */
@discardableResult @discardableResult
public func topLeft(top: CGFloat = 0, left: CGFloat = 0) -> Layout { func width(offset: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.width, constant: offset)
return debugChildNotAvailableMessage()
}
return topLeft(v, top: top, left: left)
} }
/** /**
A child view is aligned from the top right with optional top and right padding. Constraints height of the view to its parent's.
- Parameter child: A child UIView to layout. - Parameter offset: A CGFloat offset.
- Parameter top: A CGFloat value for padding the top side. - Returns: A Layout instance to allow chaining.
- Parameter right: A CGFloat value for padding the right side.
- Returns: The current Layout instance.
*/ */
@discardableResult @discardableResult
public func topRight(_ child: UIView, top: CGFloat = 0, right: CGFloat = 0) -> Layout { func height(offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.height, constant: offset)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.topRight(parent: v, child: child, top: top, right: right)
return self
} }
/** /**
A child view is aligned from the top right with optional top and right padding. Constraints edges of the view to its parent's.
- Parameter top: A CGFloat value for padding the top side. - Parameter top: A CGFloat offset for top.
- Parameter right: A CGFloat value for padding the right side. - Parameter left: A CGFloat offset for left.
- Returns: The current Layout instance. - Parameter bottom: A CGFloat offset for bottom.
- Parameter right: A CGFloat offset for right.
- Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func topRight(top: CGFloat = 0, right: CGFloat = 0) -> Layout { func edges(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.edges, constants: top, left, -bottom, -right)
return debugChildNotAvailableMessage()
}
return topRight(v, top: top, right: right)
} }
}
public extension Layout {
/** /**
A child view is aligned from the bottom left with optional bottom and left padding. Constraints width of the view to a constant value.
- Parameter child: A child UIView to layout. - Parameter _ width: A CGFloat value.
- Parameter bottom: A CGFloat value for padding the bottom side. - Returns: A Layout instance to allow chaining.
- Parameter left: A CGFloat value for padding the left side.
- Returns: The current Layout instance.
*/ */
@discardableResult @discardableResult
public func bottomLeft(_ child: UIView, bottom: CGFloat = 0, left: CGFloat = 0) -> Layout { func width(_ width: CGFloat) -> Layout {
guard let v = parent else { return constraint(.constantWidth, constants: width)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.bottomLeft(parent: v, child: child, bottom: bottom, left: left)
return self
} }
/** /**
A child view is aligned from the bottom left with optional bottom and left padding. Constraints height of the view to a constant value.
- Parameter bottom: A CGFloat value for padding the bottom side. - Parameter _ height: A CGFloat value.
- Parameter left: A CGFloat value for padding the left side. - Returns: A Layout instance to allow chaining.
- Returns: The current Layout instance.
*/ */
@discardableResult @discardableResult
public func bottomLeft(bottom: CGFloat = 0, left: CGFloat = 0) -> Layout { func height(_ height: CGFloat) -> Layout {
guard let v = child else { return constraint(.constantHeight, constants: height)
return debugChildNotAvailableMessage()
}
return bottomLeft(v, bottom: bottom, left: left)
} }
}
public extension Layout {
/** /**
A child view is aligned from the bottom right with optional bottom and right padding. Constraints top of the view to the given anchor.
- Parameter child: A child UIView to layout. - Parameter _ anchor: A LayoutAnchorable.
- Parameter bottom: A CGFloat value for padding the bottom side. - Parameter _ offset: A CGFloat offset.
- Parameter right: A CGFloat value for padding the right side. - Returns: A Layout instance to allow chaining.
- Returns: The current Layout instance.
*/ */
@discardableResult @discardableResult
public func bottomRight(_ child: UIView, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout { func top(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.top, to: anchor, constant: offset)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.bottomRight(parent: v, child: child, bottom: bottom, right: right)
return self
} }
/** /**
A child view is aligned from the bottom right with optional bottom and right padding. Constraints left of the view to the given anchor.
- Parameter bottom: A CGFloat value for padding the bottom side. - Parameter _ anchor: A LayoutAnchorable.
- Parameter right: A CGFloat value for padding the right side. - Parameter _ offset: A CGFloat offset.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func bottomRight(bottom: CGFloat = 0, right: CGFloat = 0) -> Layout { func left(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.left, to: anchor, constant: offset)
return debugChildNotAvailableMessage()
}
return bottomRight(v, bottom: bottom, right: right)
} }
/** /**
A child view is aligned at the center with an optional offsetX and offsetY value. Constraints right of the view to the given anchor.
- Parameter child: A child UIView to layout. - Parameter _ anchor: A LayoutAnchorable.
- Parameter offsetX: A CGFloat value for the offset along the x axis. - Parameter _ offset: A CGFloat offset.
- Parameter offsetX: A CGFloat value for the offset along the y axis. - Returns: A Layout instance to allow chaining.
- Returns: The current Layout instance.
*/ */
@discardableResult @discardableResult
public func center(_ child: UIView, offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout { func right(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.right, to: anchor, constant: -offset)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.center(parent: v, child: child, offsetX: offsetX, offsetY: offsetY)
return self
} }
/** /**
A child view is aligned at the center with an optional offsetX and offsetY value. Constraints leading of the view to the given anchor.
- Parameter offsetX: A CGFloat value for the offset along the x axis. - Parameter _ anchor: A LayoutAnchorable.
- Parameter offsetX: A CGFloat value for the offset along the y axis. - Parameter _ offset: A CGFloat offset.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func center(offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout { func leading(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.leading, to: anchor, constant: offset)
return debugChildNotAvailableMessage()
}
return center(v, offsetX: offsetX, offsetY: offsetY)
} }
/** /**
A child view is aligned at the center horizontally with an optional offset value. Constraints trailing of the view to the given anchor.
- Parameter child: A child UIView to layout. - Parameter _ anchor: A LayoutAnchorable.
- Parameter offset: A CGFloat value for the offset along the x axis. - Parameter _ offset: A CGFloat offset.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func centerHorizontally(_ child: UIView, offset: CGFloat = 0) -> Layout { func trailing(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.trailing, to: anchor, constant: -offset)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.centerHorizontally(parent: v, child: child, offset: offset)
return self
} }
/** /**
A child view is aligned at the center horizontally with an optional offset value. Constraints bottom of the view to the given anchor.
- Parameter offset: A CGFloat value for the offset along the x axis. - Parameter _ anchor: A LayoutAnchorable.
- Returns: The current Layout instance. - Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func centerHorizontally(offset: CGFloat = 0) -> Layout { func bottom(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.bottom, to: anchor, constant: -offset)
return debugChildNotAvailableMessage()
}
return centerHorizontally(v, offset: offset)
} }
/** /**
A child view is aligned at the center vertically with an optional offset value. Constraints top-leading of the view to the given anchor.
- Parameter child: A child UIView to layout. - Parameter _ anchor: A LayoutAnchorable.
- Parameter offset: A CGFloat value for the offset along the y axis. - Parameter top: A CGFloat offset for top.
- Returns: The current Layout instance. - Parameter leading: A CGFloat offset for leading.
- Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func centerVertically(_ child: UIView, offset: CGFloat = 0) -> Layout { func topLeading(_ anchor: LayoutAnchorable, top: CGFloat = 0, leading: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.topLeading, to: anchor, constants: top, leading)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.centerVertically(parent: v, child: child, offset: offset)
return self
} }
/** /**
A child view is aligned at the center vertically with an optional offset value. Constraints top-trailing of the view to the given anchor.
- Parameter offset: A CGFloat value for the offset along the y axis. - Parameter _ anchor: A LayoutAnchorable.
- Returns: The current Layout instance. - Parameter top: A CGFloat offset for top.
- Parameter trailing: A CGFloat offset for trailing.
- Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func centerVertically(offset: CGFloat = 0) -> Layout { func topTrailing(_ anchor: LayoutAnchorable, top: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.topTrailing, to: anchor, constants: top, -trailing)
return debugChildNotAvailableMessage()
}
return centerVertically(v, offset: offset)
} }
}
fileprivate extension Layout {
/** /**
Updates the consraints for a given view. Constraints bottom-leading of the view to the given anchor.
- Parameter for view: A UIView. - 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) { @discardableResult
view.setNeedsUpdateConstraints() func bottomLeading(_ anchor: LayoutAnchorable, bottom: CGFloat = 0, leading: CGFloat = 0) -> Layout {
view.updateConstraintsIfNeeded() return constraint(.bottomLeading, to: anchor, constants: -bottom, leading)
view.setNeedsLayout()
view.layoutIfNeeded()
} }
/** /**
Updates the constraints for a given Array of views. Constraints bottom-trailing of the view to the given anchor.
- Parameter for [view]: An Array of UIViews. - 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]) { @discardableResult
for v in views { func bottomTrailing(_ anchor: LayoutAnchorable, bottom: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
updateConstraints(for: v) return constraint(.bottomTrailing, to: anchor, constants: -bottom, -trailing)
}
} }
}
/// Layout
extension Layout {
/** /**
Sets the width of a view. Constraints leading and trailing of the view to the given anchor.
- Parameter parent: A parent UIView context. - Parameter _ anchor: A LayoutAnchorable.
- Parameter child: A child UIView to layout. - Parameter leading: A CGFloat offset for leading.
- Parameter width: A CGFloat value. - 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) { @discardableResult
prepareForConstraint(parent, child: child) func leadingTrailing(_ anchor: LayoutAnchorable, leading: CGFloat = 0, trailing: CGFloat = 0) -> Layout {
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: width)) return constraint(.leadingTrailing, to: anchor, constants: leading, -trailing)
updateConstraints(for: child)
} }
/** /**
Sets the height of a view. Constraints top-left of the view to the given anchor.
- Parameter parent: A parent UIView context. - Parameter _ anchor: A LayoutAnchorable.
- Parameter child: A child UIView to layout. - Parameter top: A CGFloat offset for top.
- Parameter height: A CGFloat value. - 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) { @discardableResult
prepareForConstraint(parent, child: child) func topLeft(_ anchor: LayoutAnchorable, top: CGFloat = 0, left: CGFloat = 0) -> Layout {
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: height)) return constraint(.topLeft, to: anchor, constants: top, left)
updateConstraints(for: child)
} }
/** /**
Sets the width and height of a view. Constraints top-right of the view to the given anchor.
- Parameter parent: A parent UIView context. - Parameter _ anchor: A LayoutAnchorable.
- Parameter child: A child UIView to layout. - Parameter top: A CGFloat offset for top.
- Parameter size: A CGSize value. - 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) { @discardableResult
Layout.width(parent: parent, child: child, width: size.width) func topRight(_ anchor: LayoutAnchorable, top: CGFloat = 0, right: CGFloat = 0) -> Layout {
Layout.height(parent: parent, child: child, height: size.height) return constraint(.topRight, to: anchor, constants: top, -right)
} }
/** /**
A collection of children views are horizontally stretched with optional left, Constraints bottom-left of the view to the given anchor.
right padding and interim interimSpace. - Parameter _ anchor: A LayoutAnchorable.
- Parameter parent: A parent UIView context. - Parameter bottom: A CGFloat offset for bottom.
- Parameter children: An Array UIView to layout. - Parameter left: A CGFloat offset for left.
- Parameter left: A CGFloat value for padding the left side. - Returns: A Layout instance to allow chaining.
- Parameter right: A CGFloat value for padding the right side.
- Parameter interimSpace: A CGFloat value for interim interimSpace.
*/ */
public class func horizontally(parent: UIView, children: [UIView], left: CGFloat = 0, right: CGFloat = 0, interimSpace: InterimSpace = 0) { @discardableResult
prepareForConstraint(parent, children: children) func bottomLeft(_ anchor: LayoutAnchorable, bottom: CGFloat = 0, left: CGFloat = 0) -> Layout {
return constraint(.bottomLeft, to: anchor, constants: -bottom, left)
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)
} }
/** /**
A collection of children views are vertically stretched with optional top, Constraints bottom-right of the view to the given anchor.
bottom padding and interim interimSpace. - Parameter _ anchor: A LayoutAnchorable.
- Parameter parent: A parent UIView context. - Parameter bottom: A CGFloat offset for bottom.
- Parameter children: An Array UIView to layout. - Parameter right: A CGFloat offset for right.
- Parameter top: A CGFloat value for padding the top side. - Returns: A Layout instance to allow chaining.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Parameter interimSpace: A CGFloat value for interim interimSpace.
*/ */
public class func vertically(parent: UIView, children: [UIView], top: CGFloat = 0, bottom: CGFloat = 0, interimSpace: InterimSpace = 0) { @discardableResult
prepareForConstraint(parent, children: children) func bottomRight(_ anchor: LayoutAnchorable, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.bottomRight, to: anchor, constants: -bottom, -right)
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)
} }
/** /**
A child view is horizontally stretched with optional left and right padding. Constraints left and right of the view to the given anchor.
- Parameter parent: A parent UIView context. - Parameter _ anchor: A LayoutAnchorable.
- Parameter child: A child UIView to layout. - Parameter left: A CGFloat offset for left.
- Parameter left: A CGFloat value for padding the left side. - Parameter right: A CGFloat offset for right.
- Parameter right: A CGFloat value for padding the right side. - Returns: A Layout instance to allow chaining.
*/ */
public class func horizontally(parent: UIView, child: UIView, left: CGFloat = 0, right: CGFloat = 0) { @discardableResult
prepareForConstraint(parent, child: child) func leftRight(_ anchor: LayoutAnchorable, left: CGFloat = 0, right: CGFloat = 0) -> Layout {
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .left, relatedBy: .equal, toItem: parent, attribute: .left, multiplier: 1, constant: left)) return constraint(.leftRight, to: anchor, constants: left, -right)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .right, relatedBy: .equal, toItem: parent, attribute: .right, multiplier: 1, constant: -right))
updateConstraints(for: child)
} }
/** /**
A child view is vertically stretched with optional left and right padding. Constraints top and bottom of the view to the given anchor.
- Parameter parent: A parent UIView context. - Parameter _ anchor: A LayoutAnchorable.
- Parameter child: A child UIView to layout. - Parameter top: A CGFloat offset for top.
- Parameter top: A CGFloat value for padding the top side. - Parameter bottom: A CGFloat offset for bottom.
- Parameter bottom: A CGFloat value for padding the bottom side. - Returns: A Layout instance to allow chaining.
*/ */
public class func vertically(parent: UIView, child: UIView, top: CGFloat = 0, bottom: CGFloat = 0) { @discardableResult
prepareForConstraint(parent, child: child) func topBottom(_ anchor: LayoutAnchorable, top: CGFloat = 0, bottom: CGFloat = 0) -> Layout {
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .top, relatedBy: .equal, toItem: parent, attribute: .top, multiplier: 1, constant: top)) return constraint(.topBottom, to: anchor, constants: top, -bottom)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .bottom, relatedBy: .equal, toItem: parent, attribute: .bottom, multiplier: 1, constant: -bottom))
updateConstraints(for: child)
} }
/** /**
A child view is vertically and horizontally stretched with optional top, left, bottom and right padding. Constraints center of the view to the given anchor.
- Parameter parent: A parent UIView context. - Parameter _ anchor: A LayoutAnchorable.
- Parameter child: A child UIView to layout. - Parameter offsetX: A CGFloat offset for horizontal center.
- Parameter top: A CGFloat value for padding the top side. - Parameter offsetY: A CGFloat offset for vertical center.
- Parameter left: A CGFloat value for padding the left side. - Returns: A Layout instance to allow chaining.
- Parameter bottom: A CGFloat value for padding the bottom side.
- Parameter right: A CGFloat value for padding the right side.
*/ */
public class func edges(parent: UIView, child: UIView, top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) { @discardableResult
horizontally(parent: parent, child: child, left: left, right: right) func center(_ anchor: LayoutAnchorable, offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout {
vertically(parent: parent, child: child, top: top, bottom: bottom) return constraint(.center, to: anchor, constants: offsetX, offsetY)
} }
/** /**
A child view is aligned from the top with optional top padding. Constraints horizontal center of the view to the given anchor.
- Parameter parent: A parent UIView context. - Parameter _ anchor: A LayoutAnchorable.
- Parameter child: A child UIView to layout. - Parameter _ offset: A CGFloat offset.
- Parameter top: A CGFloat value for padding the top side. - Returns: A Layout instance to allow chaining.
- Returns: The current Layout instance.
*/ */
public class func top(parent: UIView, child: UIView, top: CGFloat = 0) { @discardableResult
prepareForConstraint(parent, child: child) func centerX(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .top, relatedBy: .equal, toItem: parent, attribute: .top, multiplier: 1, constant: top)) return constraint(.centerX, to: anchor, constant: offset)
updateConstraints(for: child)
} }
/** /**
A child view is aligned from the left with optional left padding. Constraints vertical center of the view to the given anchor.
- Parameter parent: A parent UIView context. - Parameter _ anchor: A LayoutAnchorable.
- Parameter child: A child UIView to layout. - Parameter _ offset: A CGFloat offset.
- Parameter left: A CGFloat value for padding the left side. - Returns: A Layout instance to allow chaining.
- Returns: The current Layout instance.
*/ */
public class func left(parent: UIView, child: UIView, left: CGFloat = 0) { @discardableResult
prepareForConstraint(parent, child: child) func centerY(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .left, relatedBy: .equal, toItem: parent, attribute: .left, multiplier: 1, constant: left)) return constraint(.centerY, to: anchor, constant: offset)
updateConstraints(for: child)
} }
/** /**
A child view is aligned from the bottom with optional bottom padding. Constraints height of the view to the given anchor.
- Parameter parent: A parent UIView context. - Parameter _ anchor: A LayoutAnchorable.
- Parameter child: A child UIView to layout. - Parameter offset: A CGFloat offset.
- Parameter bottom: A CGFloat value for padding the bottom side. - Returns: A Layout instance to allow chaining.
- Returns: The current Layout instance.
*/ */
public class func bottom(parent: UIView, child: UIView, bottom: CGFloat = 0) { @discardableResult
prepareForConstraint(parent, child: child) func width(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .bottom, relatedBy: .equal, toItem: parent, attribute: .bottom, multiplier: 1, constant: -bottom)) return constraint(.width, to: anchor, constant: offset)
updateConstraints(for: child)
} }
/** /**
A child view is aligned from the right with optional right padding. Constraints height of the view to the given anchor.
- Parameter parent: A parent UIView context. - Parameter _ anchor: A LayoutAnchorable.
- Parameter child: A child UIView to layout. - Parameter offset: A CGFloat offset.
- Parameter right: A CGFloat value for padding the right side. - Returns: A Layout instance to allow chaining.
- Returns: The current Layout instance.
*/ */
public class func right(parent: UIView, child: UIView, right: CGFloat = 0) { @discardableResult
prepareForConstraint(parent, child: child) func height(_ anchor: LayoutAnchorable, _ offset: CGFloat = 0) -> Layout {
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .right, relatedBy: .equal, toItem: parent, attribute: .right, multiplier: 1, constant: -right)) return constraint(.height, to: anchor, constant: offset)
updateConstraints(for: child)
} }
/** /**
A child view is aligned from the top left with optional top and left padding. Constraints edges of the view to the given anchor.
- Parameter parent: A parent UIView context. - Parameter _ anchor: A LayoutAnchorable.
- Parameter child: A child UIView to layout. - Parameter top: A CGFloat offset for top.
- Parameter top: A CGFloat value for padding the top side. - Parameter left: A CGFloat offset for left.
- Parameter left: A CGFloat value for padding the left side. - Parameter bottom: A CGFloat offset for bottom.
- Returns: The current Layout instance. - 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) { @discardableResult
top(parent: parent, child: child, top: t) func edges(_ anchor: LayoutAnchorable, top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
left(parent: parent, child: child, left: l) 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. Constraints the view to its parent according to the provided attribute.
- Parameter parent: A parent UIView context. If the constraint already exists, will update its constant.
- Parameter child: A child UIView to layout. - Parameter _ attribute: A LayoutAttribute.
- Parameter top: A CGFloat value for padding the top side. - Parameter constant: A CGFloat.
- Parameter right: A CGFloat value for padding the right side. - Returns: A Layout instance to allow chaining.
- Returns: The current Layout instance.
*/ */
public class func topRight(parent: UIView, child: UIView, top t: CGFloat = 0, right r: CGFloat = 0) { func constraint(_ attribute: LayoutAttribute, constant: CGFloat) -> Layout {
top(parent: parent, child: child, top: t) return constraint([attribute], constants: constant)
right(parent: parent, child: child, right: r)
} }
/** /**
A child view is aligned from the bottom left with optional bottom and left padding. Constraints the view to its parent according to the provided attributes.
- Parameter parent: A parent UIView context. If any of the constraints already exists, will update its constant.
- Parameter child: A child UIView to layout. - Parameter _ attributes: An array of LayoutAttribute.
- Parameter bottom: A CGFloat value for padding the bottom side. - Parameter constants: A list of CGFloat.
- Parameter left: A CGFloat value for padding the left side. - Returns: A Layout instance to allow chaining.
- Returns: The current Layout instance.
*/ */
public class func bottomLeft(parent: UIView, child: UIView, bottom b: CGFloat = 0, left l: CGFloat = 0) { func constraint(_ attributes: [LayoutAttribute], constants: CGFloat...) -> Layout {
bottom(parent: parent, child: child, bottom: b) var attributes = attributes
left(parent: parent, child: child, left: l) var anchor: LayoutAnchor!
}
/** if attributes == .constantHeight || attributes == .constantWidth {
A child view is aligned from the bottom right with optional bottom and right padding. attributes.removeLast()
- Parameter parent: A parent UIView context. anchor = LayoutAnchor(constraintable: nil, attributes: [.notAnAttribute])
- Parameter child: A child UIView to layout. } else {
- Parameter bottom: A CGFloat value for padding the bottom side.
- Parameter right: A CGFloat value for padding the right side. guard parent != nil else {
- Returns: The current Layout instance. fatalError("[Material Error: Constraint requires view to have parent.")
*/
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)
} }
/** anchor = LayoutAnchor(constraintable: parent, attributes: attributes)
A child view is aligned at the center with an optional offsetX and offsetY value. }
- Parameter parent: A parent UIView context. return constraint(attributes, to: anchor, constants: constants)
- 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)
} }
/** /**
A child view is aligned at the center horizontally with an optional offset value. Constraints the view to the given anchor according to the provided attribute.
- Parameter parent: A parent UIView context. If the constraint already exists, will update its constant.
- Parameter child: A child UIView to layout. - Parameter _ attribute: A LayoutAttribute.
- Parameter offset: A CGFloat value for the offset along the y axis. - Parameter to anchor: A LayoutAnchorable.
- Returns: The current Layout instance. - Parameter constant: A CGFloat.
- Returns: A Layout instance to allow chaining.
*/ */
public class func centerHorizontally(parent: UIView, child: UIView, offset: CGFloat = 0) { func constraint(_ attribute: LayoutAttribute, to anchor: LayoutAnchorable, constant: CGFloat) -> Layout {
prepareForConstraint(parent, child: child) return constraint([attribute], to: anchor, constants: constant)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .centerX, relatedBy: .equal, toItem: parent, attribute: .centerX, multiplier: 1, constant: offset))
updateConstraints(for: child)
} }
/** /**
A child view is aligned at the center vertically with an optional offset value. Constraints the view to the given anchor according to the provided attributes.
- Parameter parent: A parent UIView context. If any of the constraints already exists, will update its constant.
- Parameter child: A child UIView to layout. - Parameter _ attributes: An array of LayoutAttribute.
- Parameter offset: A CGFloat value for the offset along the y axis. - Parameter to anchor: A LayoutAnchorable.
- Returns: The current Layout instance. - Parameter constants: A list of CGFloat.
- Returns: A Layout instance to allow chaining.
*/ */
public class func centerVertically(parent: UIView, child: UIView, offset: CGFloat = 0) { func constraint(_ attributes: [LayoutAttribute], to anchor: LayoutAnchorable, constants: CGFloat...) -> Layout {
prepareForConstraint(parent, child: child) return constraint(attributes, to: anchor, constants: constants)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .centerY, relatedBy: .equal, toItem: parent, attribute: .centerY, multiplier: 1, constant: offset))
updateConstraints(for: child)
} }
/** /**
Creats an Array with a NSLayoutConstraint value. Constraints the view to the given anchor according to the provided attributes.
- Parameter format: The VFL format string. If any of the constraints already exists, will update its constant.
- Parameter options: Additional NSLayoutFormatOptions. - Parameter _ attributes: An array of LayoutAttribute.
- Parameter metrics: An optional Dictionary<String, Any> of metric key / value pairs. - Parameter to anchor: A LayoutAnchorable.
- Parameter views: A Dictionary<String, Any> of view key / value pairs. - Parameter constants: An array of CGFloat.
- Returns: The Array<NSLayoutConstraint> instance. - Returns: A Layout instance to allow chaining.
*/ */
public class func constraint(format: String, options: NSLayoutConstraint.FormatOptions, metrics: [String: Any]?, views: [String: Any]) -> [NSLayoutConstraint] { func constraint(_ attributes: [LayoutAttribute], to anchor: LayoutAnchorable, constants: [CGFloat]) -> Layout {
for (_, a) in views { let from = LayoutAnchor(constraintable: constraintable, attributes: attributes)
if let v = a as? UIView { var to = anchor as? LayoutAnchor
v.translatesAutoresizingMaskIntoConstraints = false if to?.attributes.isEmpty ?? true {
} let v = (anchor as? UIView) ?? (anchor as? LayoutAnchor)?.constraintable
to = LayoutAnchor(constraintable: v, attributes: attributes)
} }
return NSLayoutConstraint.constraints( let constraint = LayoutConstraint(fromAnchor: from, toAnchor: to!, constants: constants)
withVisualFormat: format,
options: options,
metrics: metrics, let constraints = (view?.constraints ?? []) + (view?.superview?.constraints ?? [])
views: views 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
} }
/** activeConstraint.constant = newConstraint.constant
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)
} }
child.translatesAutoresizingMaskIntoConstraints = false
return self
} }
}
private extension NSLayoutConstraint {
/** /**
Prepares the relationship between the parent view context and an Array of Checks if the constraint is equal to given constraint.
child UIViews. - Parameter _ other: An NSLayoutConstraint.
- Parameter parent: A parent UIView context. - Returns: A Bool indicating whether constraints are equal.
- Parameter children: An Array of UIViews.
*/ */
private class func prepareForConstraint(_ parent: UIView, children: [UIView]) { func equalTo(_ other: NSLayoutConstraint) -> Bool {
for v in children { return firstItem === other.firstItem
prepareForConstraint(parent, child: v) && secondItem === other.secondItem
} && firstAttribute == other.firstAttribute
&& secondAttribute == other.secondAttribute
} }
} }
/// A memory reference to the LayoutKey instance for UIView extensions. /// A memory reference to the lastConstraint of UIView.
fileprivate var LayoutKey: UInt8 = 0 private var LastConstraintKey: UInt8 = 0
/// Layout extension for UIView. private extension UIView {
extension UIView { /**
/// Layout reference. The last consntraint that's created by Layout system.
public private(set) var layout: Layout { Used to set multiplier/priority on the last constraint.
*/
var lastConstraint: NSLayoutConstraint? {
get { get {
return AssociatedObject.get(base: self, key: &LayoutKey) { return AssociatedObject.get(base: self, key: &LastConstraintKey) {
return Layout(parent: self) nil
} }
} }
set(value) { 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