Commit 492b64fe by Orkhan Alikhanov

Reworked layout system

parent 091d4306
......@@ -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 */,
......
......@@ -31,970 +31,518 @@
import UIKit
import Motion
public class Layout {
/// Parent UIView context.
internal weak var parent: UIView?
/// Child UIView context.
internal weak var child: UIView?
/// 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
}
/**
An initializer that takes in a parent context and child context.
- Parameter parent: An optional parent UIView.
- Parameter child: An optional child UIView.
*/
public init(parent: UIView?, child: UIView?) {
self.parent = parent
self.child = child
/// Layout instance for the view.
var layout: Layout {
return Layout(view: self)
}
}
public struct Layout {
/// A weak reference to the view.
weak var view: UIView?
/**
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.
*/
internal func debugParentNotAvailableMessage(function: String = #function) -> Layout {
debugPrint("[Material Layout Error: Parent view context is not available for \(function).")
return self
/// Parent view of the view.
var parent: UIView? {
return view?.superview
}
/**
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.
An initializer taking UIView.
- Parameter view: A UIView.
*/
internal func debugChildNotAvailableMessage(function: String = #function) -> Layout {
debugPrint("[Material Layout Error: Child view context is not available for \(function).")
return self
init(view: UIView) {
self.view = view
}
}
public extension Layout {
/**
Sets the width of a view.
- Parameter child: A child UIView to layout.
- Parameter width: 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 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 top(_ offset: CGFloat = 0) -> Layout {
return constraint(.top, constant: offset)
}
/**
Sets the width of a view assuming a child context view.
- Parameter width: A CGFloat 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 width(_ width: CGFloat) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return self.width(v, width: width)
func left(_ offset: CGFloat = 0) -> Layout {
return constraint(.left, constant: offset)
}
/**
Sets the height of a view.
- Parameter child: A child UIView to layout.
- Parameter height: A CGFloat 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 height(_ child: UIView, height: CGFloat) -> Layout {
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.height(parent: v, child: child, height: height)
return self
func right(_ offset: CGFloat = 0) -> Layout {
return constraint(.right, constant: offset)
}
/**
Sets the height of a view assuming a child context view.
- Parameter height: A CGFloat value.
- 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 height(_ height: CGFloat) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return self.height(v, height: height)
func bottom(_ offset: CGFloat = 0) -> Layout {
return constraint(.bottom, 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 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 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 topLeft(top: CGFloat = 0, left: CGFloat = 0) -> Layout {
return constraint(.topLeft, constants: top, left)
}
/**
Sets the width and height of a view assuming a child context view.
- Parameter size: A CGSize value.
- 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 size(_ size: CGSize = CGSize.zero) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return self.size(v, size: size)
func topRight(top: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.topRight, constants: top, right)
}
/**
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 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 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 bottomLeft(bottom: CGFloat = 0, left: CGFloat = 0) -> Layout {
return constraint(.bottomLeft, constants: bottom, left)
}
/**
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 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 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 bottomRight(bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.bottomRight, constants: bottom, right)
}
/**
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 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 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 leftRight(left: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.leftRight, constants: left, right)
}
/**
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 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 horizontally(left: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return horizontally(v, left: left, right: right)
func topBottom(top: CGFloat = 0, bottom: CGFloat = 0) -> Layout {
return constraint(.topBottom, constants: top, bottom)
}
/**
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 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 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 center(offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout {
return constraint(.center, constants: offsetX, offsetY)
}
/**
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 horizontal center of the view to its parent's.
- Parameter _ offset: A CGFloat offset.
- 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 centerX(_ offset: CGFloat = 0) -> Layout {
return constraint(.centerX, constant: offset)
}
/**
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 vertical center of the view to its parent's.
- Parameter _ offset: A CGFloat offset.
- 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 centerY(_ offset: CGFloat = 0) -> Layout {
return constraint(.centerY, constant: offset)
}
/**
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 width of the view to its parent's.
- Parameter offset: A CGFloat offset.
- 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 width(offset: CGFloat = 0) -> Layout {
return constraint(.width, constant: offset)
}
/**
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 height of the view to its parent's.
- Parameter offset: A CGFloat offset.
- 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 height(offset: CGFloat = 0) -> Layout {
return constraint(.height, constant: offset)
}
/**
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 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 top(_ top: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return self.top(v, top: top)
func edges(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.edges, constants: top, left, bottom, right)
}
/**
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.
*/
@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
}
}
public extension Layout {
/**
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 width of the view to a constant value.
- Parameter _ width: A CGFloat value.
- 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 width(_ width: CGFloat) -> Layout {
return constraint(.constantWidth, constants: width)
}
/**
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 height of the view to a constant value.
- Parameter _ height: A CGFloat value.
- 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 height(_ height: CGFloat) -> Layout {
return constraint(.constantHeight, constants: height)
}
}
public extension Layout {
/**
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 of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- Parameter _ offset: A CGFloat offset.
- 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 top(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
return constraint(.top, to: anchor, constant: offset)
}
/**
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 left of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- Parameter _ offset: A CGFloat offset.
- 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 left(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
return constraint(.left, to: anchor, constant: offset)
}
/**
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 right of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- 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 right(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
return constraint(.right, to: anchor, 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 bottom of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- 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 bottom(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
return constraint(.bottom, to: anchor, 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 top-left of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- Parameter top: A CGFloat offset for top.
- Parameter left: A CGFloat offset for left.
- 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 topLeft(_ anchor: LayoutAnchor, top: CGFloat = 0, left: CGFloat = 0) -> Layout {
return constraint(.topLeft, to: anchor, constants: top, left)
}
/**
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 top-right of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- Parameter top: A CGFloat offset for top.
- Parameter right: A CGFloat offset for right.
- 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 topRight(_ anchor: LayoutAnchor, top: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.topRight, to: anchor, constants: top, right)
}
/**
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 bottom-left of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter left: A CGFloat offset for left.
- 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 bottomLeft(_ anchor: LayoutAnchor, bottom: CGFloat = 0, left: CGFloat = 0) -> Layout {
return constraint(.bottomLeft, to: anchor, constants: bottom, left)
}
/**
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 bottom-right of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- Parameter bottom: A CGFloat offset for bottom.
- Parameter right: A CGFloat offset for right.
- 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 bottomRight(_ anchor: LayoutAnchor, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.bottomRight, to: anchor, constants: bottom, right)
}
/**
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 left and right of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- Parameter left: A CGFloat offset for left.
- Parameter right: A CGFloat offset for right.
- 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 leftRight(_ anchor: LayoutAnchor, left: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.leftRight, to: anchor, constants: left, right)
}
/**
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 and bottom of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- Parameter top: A CGFloat offset for top.
- Parameter bottom: A CGFloat offset for bottom.
- 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 topBottom(_ anchor: LayoutAnchor, top: CGFloat = 0, bottom: CGFloat = 0) -> Layout {
return constraint(.topBottom, to: anchor, constants: top, bottom)
}
/**
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 center of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- 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 bottomRight(bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return bottomRight(v, bottom: bottom, right: right)
func center(_ anchor: LayoutAnchor, offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout {
return constraint(.center, to: anchor, constants: offsetX, offsetY)
}
/**
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 horizontal center of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- 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 centerX(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
return constraint(.centerX, 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 vertical center of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- 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 centerY(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
return constraint(.centerY, 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 height of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- 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 width(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
return constraint(.width, 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 height of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- 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 height(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
return constraint(.height, 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 edges of the view to the given anchor.
- Parameter _ acnhor: A LayoutAnchor.
- 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 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
}
/**
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.
*/
@discardableResult
public func centerVertically(offset: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return centerVertically(v, offset: offset)
func edges(_ anchor: LayoutAnchor, top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
return constraint(.edges, to: anchor, constants: top, left, bottom, right)
}
}
fileprivate extension Layout {
private extension Layout {
/**
Updates the consraints for a given view.
- Parameter for view: A UIView.
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.
*/
class func updateConstraints(for view: UIView) {
view.setNeedsUpdateConstraints()
view.updateConstraintsIfNeeded()
view.setNeedsLayout()
view.layoutIfNeeded()
func constraint(_ attribute: LayoutAttribute, constant: CGFloat) -> Layout {
return constraint([attribute], constants: constant)
}
/**
Updates the constraints for a given Array of views.
- Parameter for [view]: An Array of UIViews.
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.
*/
class func updateConstraints(for views: [UIView]) {
for v in views {
updateConstraints(for: v)
func constraint(_ attributes: [LayoutAttribute], constants: CGFloat...) -> Layout {
var attributes = attributes
var anchor: LayoutAnchor!
if attributes == .constantHeight || attributes == .constantWidth {
attributes.removeLast()
anchor = LayoutAnchor(view: nil, attributes: [.notAnAttribute])
} else {
anchor = LayoutAnchor(view: parent, attributes: attributes)
}
}
}
/// 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.
*/
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)
}
/**
Sets the height of a view.
- Parameter parent: A parent UIView context.
- Parameter child: A child UIView to layout.
- Parameter height: A CGFloat value.
*/
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)
return constraint(attributes, to: anchor, constants: constants)
}
/**
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 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 LayoutAnchor.
- Parameter constant: A CGFloat.
- 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)
func constraint(_ attribute: LayoutAttribute, to anchor: LayoutAnchor, constant: CGFloat) -> Layout {
return constraint([attribute], to: anchor, constants: constant)
}
/**
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 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 LayoutAnchor.
- Parameter constants: A list of CGFloat.
- 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)
func constraint(_ attributes: [LayoutAttribute], to anchor: LayoutAnchor, constants: CGFloat...) -> Layout {
return constraint(attributes, to: anchor, constants: constants)
}
/**
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 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 LayoutAnchor.
- Parameter constants: An array of CGFloat.
- 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)
func constraint(_ attributes: [LayoutAttribute], to anchor: LayoutAnchor, constants: [CGFloat]) -> Layout {
let from = LayoutAnchor(view: view, attributes: attributes)
let constraint = LayoutConstraint(fromAnchor: from, toAnchor: anchor, constants: constants)
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))
let constraints = (view?.constraints ?? []) + (parent?.constraints ?? [])
let newConstraints = constraint.constraints
for newConstraint in newConstraints {
guard let activeConstraint = constraints.first(where: { $0.equalTo(newConstraint) }) else {
newConstraint.isActive = true
continue
}
parent.addConstraint(NSLayoutConstraint(item: children[children.count - 1], attribute: .bottom, relatedBy: .equal, toItem: parent, attribute: .bottom, multiplier: 1, constant: -bottom))
activeConstraint.constant = newConstraint.constant
}
updateConstraints(for: children)
}
/**
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.
*/
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)
}
/**
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.
*/
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)
}
/**
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.
*/
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)
}
/**
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.
*/
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)
}
/**
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.
*/
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)
}
/**
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.
*/
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)
}
/**
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.
*/
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)
}
/**
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.
*/
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)
}
/**
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.
*/
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)
}
/**
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.
*/
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)
}
/**
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)
}
/**
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)
}
/**
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.
*/
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)
}
/**
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.
*/
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)
}
/**
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.
*/
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
}
}
return NSLayoutConstraint.constraints(
withVisualFormat: format,
options: options,
metrics: metrics,
views: views
)
}
/**
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
}
/**
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.
*/
private class func prepareForConstraint(_ parent: UIView, children: [UIView]) {
for v in children {
prepareForConstraint(parent, child: v)
}
return self
}
}
/// A memory reference to the LayoutKey instance for UIView extensions.
fileprivate var LayoutKey: UInt8 = 0
/// Layout extension for UIView.
extension UIView {
/// Layout reference.
public private(set) var layout: Layout {
get {
return AssociatedObject.get(base: self, key: &LayoutKey) {
return Layout(parent: self)
}
}
set(value) {
AssociatedObject.set(base: self, key: &LayoutKey, value: value)
}
}
private extension NSLayoutConstraint {
/**
Used to chain layout constraints on a child context.
- Parameter child: A child UIView to layout.
- Returns: The current Layout instance.
Checks if the constraint is equal to given constraint.
- Parameter _ other: An NSLayoutConstraint.
- Returns: A Bool indicating whether constraints are equal.
*/
public func layout(_ child: UIView) -> Layout {
return Layout(parent: self, child: child)
func equalTo(_ other: NSLayoutConstraint) -> Bool {
return firstItem === other.firstItem
&& secondItem === other.secondItem
&& firstAttribute == other.firstAttribute
&& secondAttribute == other.secondAttribute
}
}
/*
* 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
public struct LayoutAnchor {
/// A weak reference to the view.
weak var view: UIView?
/// An array of LayoutAttribute for the view.
let attributes: [LayoutAttribute]
/**
An initializer taking view and anchor attributes.
- Parameter view: A UIView.
- Parameter attributes: An array of LayoutAtrribute.
*/
init(view: UIView?, attributes: [LayoutAttribute]) {
self.view = view
self.attributes = attributes
}
}
public extension Layout {
/// A layout anchor representing top of the view.
var top: LayoutAnchor {
return attribute(.top)
}
/// A layout anchor representing bottom of the view.
var bottom: LayoutAnchor {
return attribute(.bottom)
}
/// A layout anchor representing left of the view.
var left: LayoutAnchor {
return attribute(.left)
}
/// A layout anchor representing right of the view.
var right: LayoutAnchor {
return attribute(.right)
}
/// A layout anchor representing top-left of the view.
var topLeft: LayoutAnchor {
return attribute(.topLeft)
}
/// A layout anchor representing top-right of the view.
var topRight: LayoutAnchor {
return attribute(.topRight)
}
/// A layout anchor representing bottom-left of the view.
var bottomLeft: LayoutAnchor {
return attribute(.bottomLeft)
}
/// A layout anchor representing bottom-right of the view.
var bottomRight: LayoutAnchor {
return attribute(.bottomRight)
}
/// A layout anchor representing top and bottom of the view.
var topBottom: LayoutAnchor {
return attribute(.topBottom)
}
/// A layout anchor representing left and right of the view.
var leftRight: LayoutAnchor {
return attribute(.leftRight)
}
/// A layout anchor representing center of the view.
var center: LayoutAnchor {
return attribute(.center)
}
/// A layout anchor representing horizontal center of the view.
var centerX: LayoutAnchor {
return attribute(.centerX)
}
/// A layout anchor representing vertical center of the view.
var centerY: LayoutAnchor {
return attribute(.centerY)
}
/// A layout anchor representing top, left, bottom and right of the view.
var edges: LayoutAnchor {
return attribute(.edges)
}
/// A layout anchor representing width of the view.
var width: LayoutAnchor {
return attribute(.width)
}
/// A layout anchor representing height of the view.
var height: LayoutAnchor {
return attribute(.height)
}
}
private extension Layout {
/**
Creates LayoutAnchor with the given attribute.
- Parameter attribute: A LayoutAttribute.
- Returns: A LayoutAnchor.
*/
func attribute(_ attribute: LayoutAttribute) -> LayoutAnchor {
return LayoutAnchor(view: view, attributes: [attribute])
}
/**
Creates LayoutAnchor with the given attributes.
- Parameter attributes: An array of LayoutAttribute.
- Returns: A LayoutAnchor.
*/
func attribute(_ attributes: [LayoutAttribute]) -> LayoutAnchor {
return LayoutAnchor(view: view, 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 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.view as Any,
attribute: $0.0,
relatedBy: .equal,
toItem: toAnchor.view,
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