Commit 492b64fe by Orkhan Alikhanov

Reworked layout system

parent 091d4306
...@@ -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 */,
......
...@@ -31,970 +31,518 @@ ...@@ -31,970 +31,518 @@
import UIKit import UIKit
import Motion import Motion
public class Layout { /// Layout extension for UIView.
/// Parent UIView context. public extension UIView {
internal weak var parent: UIView?
/// Child UIView context.
internal weak var child: 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.
An initializer that takes in a parent context and child context. var layout: Layout {
- Parameter parent: An optional parent UIView. return Layout(view: self)
- Parameter child: An optional child UIView.
*/
public init(parent: UIView?, child: UIView?) {
self.parent = parent
self.child = child
} }
}
/** public struct Layout {
Prints a debug message when the parent context is not available. /// A weak reference to the view.
- Parameter function: A String representation of the function that weak var view: UIView?
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.
Prints a debug message when the child context is not available. var parent: UIView? {
- Parameter function: A String representation of the function that return view?.superview
caused the issue.
- Returns: The current Layout instance.
*/
internal func debugChildNotAvailableMessage(function: String = #function) -> Layout {
debugPrint("[Material Layout Error: Child view context is not available for \(function).")
return self
} }
/** /**
Sets the width of a view. An initializer taking UIView.
- Parameter child: A child UIView to layout. - Parameter view: A UIView.
- Parameter width: A CGFloat value.
- Returns: The current Layout instance.
*/ */
@discardableResult init(view: UIView) {
public func width(_ child: UIView, width: CGFloat) -> Layout { self.view = view
guard let v = parent else {
return debugParentNotAvailableMessage()
}
self.child = child
Layout.width(parent: v, child: child, width: width)
return self
} }
}
public extension Layout {
/** /**
Sets the width of a view assuming a child context view. Constraints top of the view to its parent's.
- Parameter width: A CGFloat value. - Parameter _ offset: A CGFloat offset.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func width(_ width: CGFloat) -> Layout { func top(_ offset: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.top, constant: offset)
return debugChildNotAvailableMessage()
}
return self.width(v, width: width)
} }
/** /**
Sets the 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 height: A CGFloat value. - Returns: A Layout instance to allow chaining.
- Returns: The current Layout instance.
*/ */
@discardableResult @discardableResult
public func height(_ child: UIView, height: CGFloat) -> Layout { func left(_ offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.left, constant: offset)
return debugParentNotAvailableMessage()
}
self.child = child
Layout.height(parent: v, child: child, height: height)
return self
} }
/** /**
Sets the height of a view assuming a child context view. Constraints right of the view to its parent.
- 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 right(_ offset: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.right, constant: offset)
return debugChildNotAvailableMessage()
}
return self.height(v, height: height)
} }
/** /**
Sets the width and height of a view. Constraints bottom 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 bottom(_ offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.bottom, 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 top-left of the view to its parent's.
- Parameter size: A CGSize value. - Parameter top: A CGFloat offset for top.
- Returns: The current Layout instance. - Parameter left: A CGFloat offset for left.
- Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func size(_ size: CGSize = CGSize.zero) -> Layout { func topLeft(top: CGFloat = 0, left: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.topLeft, constants: top, left)
return debugChildNotAvailableMessage()
}
return self.size(v, size: size)
} }
/** /**
A collection of children views are horizontally stretched with optional left, Constraints top-right of the view to its parent's.
right padding and interim interimSpace. - Parameter top: A CGFloat offset for top.
- Parameter children: An Array UIView to layout. - 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 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 topRight(top: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.topRight, constants: top, right)
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 bottom-left of the view to its parent's.
bottom padding and interim interimSpace. - Parameter bottom: A CGFloat offset for bottom.
- Parameter children: An Array UIView to layout. - Parameter left: A CGFloat offset for left.
- 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.
- Returns: The current Layout instance.
*/ */
@discardableResult @discardableResult
public func vertically(_ children: [UIView], top: CGFloat = 0, bottom: CGFloat = 0, interimSpace: InterimSpace = 0) -> Layout { func bottomLeft(bottom: CGFloat = 0, left: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.bottomLeft, constants: bottom, left)
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-right 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 right: A CGFloat offset for right.
- 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 horizontally(_ child: UIView, left: 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.horizontally(parent: v, child: child, left: left, right: right)
return self
} }
/** /**
A child view is horizontally stretched with optional left and right padding. Constraints left and right of the view to its parent's.
- Parameter left: A CGFloat value for padding the left side. - Parameter left: A CGFloat offset for left.
- Parameter right: A CGFloat value for padding the right side. - Parameter right: A CGFloat offset for right.
- 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 leftRight(left: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.leftRight, constants: left, right)
return debugChildNotAvailableMessage()
}
return horizontally(v, left: left, right: right)
} }
/** /**
A child view is vertically stretched with optional left and right padding. Constraints top and bottom 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 bottom: A CGFloat offset for bottom.
- 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 topBottom(top: CGFloat = 0, bottom: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.topBottom, constants: top, bottom)
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 center of the view to its parent's.
- Parameter top: A CGFloat value for padding the top side. - Parameter offsetX: A CGFloat offset for horizontal center.
- Parameter bottom: A CGFloat value for padding the bottom 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 vertically(top: CGFloat = 0, bottom: CGFloat = 0) -> Layout { func center(offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.center, constants: offsetX, offsetY)
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 horizontal 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.
- 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 centerX(_ offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.centerX, constant: offset)
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 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.
- 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 centerY(_ offset: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.centerY, constant: offset)
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 width 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.
- Returns: The current Layout instance.
*/ */
@discardableResult @discardableResult
public func top(_ child: UIView, top: CGFloat = 0) -> Layout { func width(offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.width, constant: offset)
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 height of the view to its parent's.
- Parameter top: A CGFloat value for padding the top side. - Parameter offset: A CGFloat offset.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func top(_ top: CGFloat = 0) -> Layout { func height(offset: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.height, constant: offset)
return debugChildNotAvailableMessage()
}
return self.top(v, top: top)
} }
/** /**
A child view is aligned from the left with optional left padding. Constraints edges of the view to its parent's.
- Parameter child: A child UIView to layout. - Parameter top: A CGFloat offset for top.
- Parameter left: A CGFloat value for padding the left 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 left(_ child: UIView, left: CGFloat = 0) -> Layout { func edges(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.edges, constants: top, left, bottom, right)
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.
- Parameter left: A CGFloat value for padding the left side.
- Returns: The current Layout instance.
*/
@discardableResult
public func left(_ left: CGFloat = 0) -> Layout {
guard let v = child else {
return debugChildNotAvailableMessage()
}
return self.left(v, left: left)
} }
}
public extension Layout {
/** /**
A child view is aligned from the bottom with optional bottom 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.
- Returns: The current Layout instance.
*/ */
@discardableResult @discardableResult
public func bottom(_ child: UIView, bottom: CGFloat = 0) -> Layout { func width(_ width: CGFloat) -> Layout {
guard let v = parent else { return constraint(.constantWidth, constants: width)
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 height of the view to a constant value.
- Parameter bottom: A CGFloat value for padding the bottom side. - Parameter _ height: A CGFloat value.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func bottom(_ bottom: CGFloat = 0) -> Layout { func height(_ height: CGFloat) -> Layout {
guard let v = child else { return constraint(.constantHeight, constants: height)
return debugChildNotAvailableMessage()
}
return self.bottom(v, bottom: bottom)
} }
}
public extension Layout {
/** /**
A child view is aligned from the right with optional right padding. Constraints top of the view to the given anchor.
- Parameter child: A child UIView to layout. - Parameter _ acnhor: A LayoutAnchor.
- 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(_ child: UIView, right: CGFloat = 0) -> Layout { func top(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.top, to: anchor, constant: offset)
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 left of the view to the given anchor.
- Parameter right: A CGFloat value for padding the right side. - Parameter _ acnhor: A LayoutAnchor.
- Returns: The current Layout instance. - Parameter _ offset: A CGFloat offset.
- Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func right(_ right: CGFloat = 0) -> Layout { func left(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.left, to: anchor, 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 right of the view to the given anchor.
- Parameter child: A child UIView to layout. - Parameter _ acnhor: A LayoutAnchor.
- 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(_ child: UIView, top: CGFloat = 0, left: CGFloat = 0) -> Layout { func right(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.right, to: anchor, 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 bottom of the view to the given anchor.
- Parameter top: A CGFloat value for padding the top side. - Parameter _ acnhor: A LayoutAnchor.
- Parameter left: A CGFloat value for padding the left side. - Parameter _ offset: A CGFloat offset.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func topLeft(top: CGFloat = 0, left: CGFloat = 0) -> Layout { func bottom(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.bottom, to: anchor, 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 top-left of the view to the given anchor.
- Parameter child: A child UIView to layout. - Parameter _ acnhor: A LayoutAnchor.
- 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. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func topRight(_ child: UIView, top: CGFloat = 0, right: CGFloat = 0) -> Layout { func topLeft(_ anchor: LayoutAnchor, top: CGFloat = 0, left: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.topLeft, to: anchor, constants: top, left)
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 top-right of the view to the given anchor.
- Parameter top: A CGFloat value for padding the top side. - Parameter _ acnhor: A LayoutAnchor.
- Parameter right: A CGFloat value for padding the right side. - Parameter top: A CGFloat offset for top.
- Returns: The current Layout instance. - 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 topRight(_ anchor: LayoutAnchor, top: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.topRight, to: anchor, constants: top, right)
return debugChildNotAvailableMessage()
}
return topRight(v, top: top, right: right)
} }
/** /**
A child view is aligned from the bottom left with optional bottom and left padding. Constraints bottom-left of the view to the given anchor.
- Parameter child: A child UIView to layout. - Parameter _ acnhor: A LayoutAnchor.
- Parameter bottom: A CGFloat value for padding the bottom side. - Parameter bottom: A CGFloat offset for bottom.
- Parameter left: A CGFloat value for padding the left side. - Parameter left: A CGFloat offset for left.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func bottomLeft(_ child: UIView, bottom: CGFloat = 0, left: CGFloat = 0) -> Layout { func bottomLeft(_ anchor: LayoutAnchor, bottom: CGFloat = 0, left: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.bottomLeft, to: anchor, constants: bottom, left)
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 bottom-right of the view to the given anchor.
- Parameter bottom: A CGFloat value for padding the bottom side. - Parameter _ acnhor: A LayoutAnchor.
- 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.
*/ */
@discardableResult @discardableResult
public func bottomLeft(bottom: CGFloat = 0, left: CGFloat = 0) -> Layout { func bottomRight(_ anchor: LayoutAnchor, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.bottomRight, to: anchor, constants: bottom, right)
return debugChildNotAvailableMessage()
}
return bottomLeft(v, bottom: bottom, left: left)
} }
/** /**
A child view is aligned from the bottom right with optional bottom and right padding. Constraints left and right of the view to the given anchor.
- Parameter child: A child UIView to layout. - Parameter _ acnhor: A LayoutAnchor.
- Parameter bottom: A CGFloat value for padding the bottom side. - Parameter left: A CGFloat offset for left.
- Parameter right: A CGFloat value for padding the right side. - Parameter right: A CGFloat offset for right.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func bottomRight(_ child: UIView, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout { func leftRight(_ anchor: LayoutAnchor, left: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.leftRight, to: anchor, constants: left, right)
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 top and bottom of the view to the given anchor.
- Parameter bottom: A CGFloat value for padding the bottom side. - Parameter _ acnhor: A LayoutAnchor.
- Parameter right: A CGFloat value for padding the right 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 bottomRight(bottom: CGFloat = 0, right: CGFloat = 0) -> Layout { func topBottom(_ anchor: LayoutAnchor, top: CGFloat = 0, bottom: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.topBottom, to: anchor, constants: top, bottom)
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 center of the view to the given anchor.
- Parameter child: A child UIView to layout. - Parameter _ acnhor: A LayoutAnchor.
- Parameter offsetX: A CGFloat value for the offset along the x axis. - Parameter offsetX: A CGFloat offset for horizontal center.
- Parameter offsetX: A CGFloat value for the offset along the y axis. - Parameter offsetY: A CGFloat offset for vertical center.
- Returns: The current Layout instance. - Returns: A Layout instance to allow chaining.
*/ */
@discardableResult @discardableResult
public func center(_ child: UIView, offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout { func center(_ anchor: LayoutAnchor, offsetX: CGFloat = 0, offsetY: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.center, to: anchor, constants: offsetX, offsetY)
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 horizontal center of the view to the given anchor.
- Parameter offsetX: A CGFloat value for the offset along the x axis. - Parameter _ acnhor: A LayoutAnchor.
- 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 centerX(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.centerX, 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 vertical center of the view to the given anchor.
- Parameter child: A child UIView to layout. - Parameter _ acnhor: A LayoutAnchor.
- 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 centerY(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.centerY, 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 height of the view to the given anchor.
- Parameter offset: A CGFloat value for the offset along the x axis. - Parameter _ acnhor: A LayoutAnchor.
- 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 width(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.width, 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 height of the view to the given anchor.
- Parameter child: A child UIView to layout. - Parameter _ acnhor: A LayoutAnchor.
- Parameter offset: 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 centerVertically(_ child: UIView, offset: CGFloat = 0) -> Layout { func height(_ anchor: LayoutAnchor, _ offset: CGFloat = 0) -> Layout {
guard let v = parent else { return constraint(.height, to: anchor, constant: offset)
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 edges of the view to the given anchor.
- Parameter offset: A CGFloat value for the offset along the y axis. - Parameter _ acnhor: A LayoutAnchor.
- Returns: The current Layout instance. - 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 @discardableResult
public func centerVertically(offset: CGFloat = 0) -> Layout { func edges(_ anchor: LayoutAnchor, top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> Layout {
guard let v = child else { return constraint(.edges, to: anchor, constants: top, left, bottom, right)
return debugChildNotAvailableMessage()
}
return centerVertically(v, offset: offset)
}
}
fileprivate extension Layout {
/**
Updates the consraints for a given view.
- Parameter for view: A UIView.
*/
class func updateConstraints(for view: UIView) {
view.setNeedsUpdateConstraints()
view.updateConstraintsIfNeeded()
view.setNeedsLayout()
view.layoutIfNeeded()
}
/**
Updates the constraints for a given Array of views.
- Parameter for [view]: An Array of UIViews.
*/
class func updateConstraints(for views: [UIView]) {
for v in views {
updateConstraints(for: v)
}
} }
} }
/// Layout private extension 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. 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 height: A CGFloat value. - Parameter constant: A CGFloat.
*/ - Returns: A Layout instance to allow chaining.
public class func height(parent: UIView, child: UIView, height: CGFloat = 0) {
prepareForConstraint(parent, child: child)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: height))
updateConstraints(for: child)
}
/**
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.
*/ */
public class func size(parent: UIView, child: UIView, size: CGSize = CGSize.zero) { func constraint(_ attribute: LayoutAttribute, constant: CGFloat) -> Layout {
Layout.width(parent: parent, child: child, width: size.width) return constraint([attribute], constants: constant)
Layout.height(parent: parent, child: child, height: size.height)
} }
/** /**
A collection of children views are horizontally stretched with optional left, Constraints the view to its parent according to the provided attributes.
right padding and interim interimSpace. If any of the constraints already exists, will update its constant.
- Parameter parent: A parent UIView context. - Parameter _ attributes: An array of LayoutAttribute.
- Parameter children: An Array UIView to layout. - Parameter constants: A list of CGFloat.
- 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) { func constraint(_ attributes: [LayoutAttribute], constants: CGFloat...) -> Layout {
prepareForConstraint(parent, children: children) var attributes = attributes
var anchor: LayoutAnchor!
if 0 < children.count { if attributes == .constantHeight || attributes == .constantWidth {
parent.addConstraint(NSLayoutConstraint(item: children[0], attribute: .left, relatedBy: .equal, toItem: parent, attribute: .left, multiplier: 1, constant: left)) attributes.removeLast()
for i in 1..<children.count { anchor = LayoutAnchor(view: nil, attributes: [.notAnAttribute])
parent.addConstraint(NSLayoutConstraint(item: children[i], attribute: .left, relatedBy: .equal, toItem: children[i - 1], attribute: .right, multiplier: 1, constant: interimSpace)) } else {
parent.addConstraint(NSLayoutConstraint(item: children[i], attribute: .width, relatedBy: .equal, toItem: children[0], attribute: .width, multiplier: 1, constant: 0)) anchor = LayoutAnchor(view: parent, attributes: attributes)
} }
parent.addConstraint(NSLayoutConstraint(item: children[children.count - 1], attribute: .right, relatedBy: .equal, toItem: parent, attribute: .right, multiplier: 1, constant: -right)) return constraint(attributes, to: anchor, constants: constants)
}
updateConstraints(for: children)
} }
/** /**
A collection of children views are vertically stretched with optional top, Constraints the view to the given anchor according to the provided attribute.
bottom padding and interim interimSpace. If the constraint already exists, will update its constant.
- Parameter parent: A parent UIView context. - Parameter _ attribute: A LayoutAttribute.
- Parameter children: An Array UIView to layout. - Parameter to anchor: A LayoutAnchor.
- Parameter top: A CGFloat value for padding the top side. - Parameter constant: A CGFloat.
- Parameter bottom: A CGFloat value for padding the bottom side. - Returns: A Layout instance to allow chaining.
- 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) { func constraint(_ attribute: LayoutAttribute, to anchor: LayoutAnchor, constant: CGFloat) -> Layout {
prepareForConstraint(parent, children: children) return constraint([attribute], to: anchor, constants: constant)
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 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 left: A CGFloat value for padding the left side. - Parameter to anchor: A LayoutAnchor.
- Parameter right: A CGFloat value for padding the right side. - Parameter constants: A list of CGFloat.
- Returns: A Layout instance to allow chaining.
*/ */
public class func horizontally(parent: UIView, child: UIView, left: CGFloat = 0, right: CGFloat = 0) { func constraint(_ attributes: [LayoutAttribute], to anchor: LayoutAnchor, constants: CGFloat...) -> Layout {
prepareForConstraint(parent, child: child) return constraint(attributes, to: anchor, constants: constants)
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. 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 top: A CGFloat value for padding the top side. - Parameter to anchor: A LayoutAnchor.
- Parameter bottom: A CGFloat value for padding the bottom side. - Parameter constants: An array of CGFloat.
- Returns: A Layout instance to allow chaining.
*/ */
public class func vertically(parent: UIView, child: UIView, top: CGFloat = 0, bottom: CGFloat = 0) { func constraint(_ attributes: [LayoutAttribute], to anchor: LayoutAnchor, constants: [CGFloat]) -> Layout {
prepareForConstraint(parent, child: child) let from = LayoutAnchor(view: view, attributes: attributes)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .top, relatedBy: .equal, toItem: parent, attribute: .top, multiplier: 1, constant: top)) let constraint = LayoutConstraint(fromAnchor: from, toAnchor: anchor, constants: constants)
parent.addConstraint(NSLayoutConstraint(item: child, attribute: .bottom, relatedBy: .equal, toItem: parent, attribute: .bottom, multiplier: 1, constant: -bottom))
updateConstraints(for: child)
}
/** let constraints = (view?.constraints ?? []) + (parent?.constraints ?? [])
A child view is vertically and horizontally stretched with optional top, left, bottom and right padding. let newConstraints = constraint.constraints
- Parameter parent: A parent UIView context. for newConstraint in newConstraints {
- Parameter child: A child UIView to layout. guard let activeConstraint = constraints.first(where: { $0.equalTo(newConstraint) }) else {
- Parameter top: A CGFloat value for padding the top side. newConstraint.isActive = true
- Parameter left: A CGFloat value for padding the left side. continue
- 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)
} }
/** activeConstraint.constant = newConstraint.constant
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)
} }
/** return self
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)
}
} }
} }
/// A memory reference to the LayoutKey instance for UIView extensions. private extension NSLayoutConstraint {
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)
}
}
/** /**
Used to chain layout constraints on a child context. Checks if the constraint is equal to given constraint.
- Parameter child: A child UIView to layout. - Parameter _ other: An NSLayoutConstraint.
- Returns: The current Layout instance. - Returns: A Bool indicating whether constraints are equal.
*/ */
public func layout(_ child: UIView) -> Layout { func equalTo(_ other: NSLayoutConstraint) -> Bool {
return Layout(parent: self, child: child) 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