mirego / PinLayout
- вторник, 6 июня 2017 г. в 03:12:31
Swift
Swift manual views layouting without auto layout, no magic, pure code, full control. Concise syntax, readable & chainable
Swift manual views layouting without auto layout, no magic, pure code, full control. Concise syntax, readable & chainable.
"No auto-layout constraints attached"
Manual layouting. No magic, pure code, full control.
Layout one view at a time.
Concise syntax. Layout most views using a single line.
Stateless
No constraints/No auto-layout
Before applying the new sets of attributes, PinLayout always start with the view’s current frame. So it’s possible to set the view’s size during the initialization (ex: view.pin.width(100).height(200)), and later only position the view (ex: view.pin.top(10).left(20)). This makes PinLayout really animation friendly.
Minimize as much as possible calculations and constants when layouting views.
Methods match as much as possible other layouting systems, including CSS, flexbox, reactive Flexbox, …
Shorter possible commands to layout views, but keeping english phrasing almost valid.
To integrate PinLayout into your Xcode project using CocoaPods, specify it in your Podfile
:
pod 'PinLayout'
Then, run pod install
.
To integrate PinLayout into your Xcode project using Carthage, specify it in your Cartfile
:
github "mirego/PinLayout"
Then, run carthage update
to build the framework and drag the built PinLayout.framework
into your Xcode project.
This example layout an image, a UISegmentedControl and a label.
override func layoutSubviews() {
super.layoutSubviews()
logo.pin.topLeft().size(100).margin(74, 10, 10)
segmented.pin.right(of: logo, aligned: .top).right().marginHorizontal(20)
textLabel.pin.below(of: segmented, aligned: .left).width(of: segmented).pinEdges().marginTop(10).sizeToFit()
}
PinLayout can position a view’s edge relative to its superview edges.
Methods:
top(_ value: CGFloat)
top(_ percent: Percent)
left(_ value: CGFloat)
left(_ percent: Percent)
bottom(_ value: CGFloat)
bottom(_ percent: Percent)
right(_ value: CGFloat)
right(_ percent: Percent)
hCenter(_ value: CGFloat)
hCenter(_ percent: Percent)
vCenter(_ value: CGFloat)
vCenter(_ percent: Percent)
view.pin.top(20).left(20)
view.pin.top(25%).hCenter(0)
view.pin.left(12).vCenter(100)
This example layout the view A to fit its superview frame with a margin of 10 pixels. It pins the top, left, bottom and right edges.
viewA.pin.top(10).left(10).bottom(10).right(10)
Another possible solution using other PinLayout's methods (more details later):
view.pin.topLeft().bottomRight().margin(10)
PinLayout also has shorter version that pins a view’s edge directly on its superview's corresponding edge.
Methods:
top()
left()
bottom()
right()
hCenter()
vCenter()
view.pin.top().left()
view.pin.bottom().right()
view.pin.hCenter().vCenter()
This example is similar to the previous example, but pins edges directly on superview’s edges. It will layout the view A to fit its superview frame with a margin of 10 pixels.
viewA.pin.top().left().bottom().right().margin(10)
PinLayout add anchors properties to UIViews. These properties are used to reference other view’s anchors.
PinLayout UIView’s anchors:
UIView.anchor.topLeft
UIView.anchor.topCenter
UIView.anchor.topRight
UIView.anchor.leftCenter
UIView.anchor.centers
UIView.anchor.rightCenter
UIView.anchor.bottomLeft
UIView.anchor.bottomCenter
UIView.anchor.bottomRight
PinLayout can use anchors to position view’s related to other views.
Following methods position the corresponding view anchor on another view’s anchor.
Methods:
topLeft(to anchor: Anchor)
topCenter(to anchor: Anchor)
topRight(to anchor: Anchor)
leftCenter(to anchor: Anchor)
center(to anchor: Anchor)
rightCenter(to anchor: Anchor)
bottomLeft(to anchor: Anchor)
bottomCenter(to anchor: Anchor)
bottomRight(to anchor: Anchor)
view.pin.topCenter(to: view1.anchor.bottomCenter)
view.pin.topLeft(to: view1.anchor.topLeft).bottomRight(to: view1.anchor.center)
Layout using an anchors. This example pins the view B topLeft anchor on the view A topRight anchor.
viewB.pin.topLeft(to: viewA.anchor.topRight)
Layout using multiple anchors.
It is also possible to combine two anchors to pin the position and the size of a view. The following example will position the view C between the view A and B with horizontal margins of 10px.
viewC.pin.topLeft(to: viewA.anchor.topRight)
.bottomRight(to: viewB.anchor.bottomLeft).marginHorizontal(10)
PinLayout also has a shorter version that pins a view's anchor directly on its corresponding superview’s anchor.
The following methods position the corresponding view's anchor on another view’s anchor.
Methods:
topLeft()
topCenter()
topRight()
leftCenter()
center()
rightCenter()
bottomLeft()
bottomCenter()
bottomRight()
For example .topRight() will pin the view’s topRight anchor on its superview’s topRight anchor..
viewA.pin.topRight()
This is equivalent to:
viewA.pin.topRight(to: superview.pin.topRight)
PinLayout adds edges properties to UIViews. These properties are used to reference other view’s edges.
PinLayout UIView’s edges:
UIView.edge.top
UIView.edge.left
UIView.edge.bottom
UIView.edge.right
PinLayout has methods to attach a UIView's edge (top, left, bottom or right edge) to another view’s edge.
Methods:
top(to edge: VerticalEdge)
left(to: edge: HorizontalEdge)
bottom(to edge: VerticalEdge)
right(to: edge: HorizontalEdge)
view.pin.left(to: view1.edge.right)
view.pin.left(to: view1.edge.right).top(to: view2.edge.right)
Layout using an edge.
The following example will layout the view B left edge on the view A right edge. It only change the view B left coordinate
viewB.pin.left(to: viewA.edge.right)
PinLayout also has methods to position relative to another view. This is similar to pinning to an edge with a slightly different syntax.
Methods:
above(of UIView)
below(of UIView)
left(of UIView)
right(of UIView)
view.pin.above(of: view2)
view.pin.left(of: view2, aligned: .top)
view.pin.left(of: view1).above(of: view2).below(of: view3).right(of: view4)
The following example will position the view C between the view A and B with margins of 10px using relative positioning methods.
viewC.pin.top().left(of: viewA).right(of: viewB).margin(10)
This is an equivalent solution using edges:
viewC.pin.top().left(to: viewA.edge.right).right(to: viewB.edge.left).margin(10)
This is also an equivalent solution using relative positioning and alignment explained in the next section:
viewC.pin.left(of: viewA, aligned: .top).right(of: viewB, aligned: top).marginHorizontal(10)
PinLayout also has methods to position relative to another view but with also the ability of specifying the alignment. This is similar to pinning to an anchor with a more natural syntax.
Methods:
above(of UIView, aligned: HorizontalAlignment)
below(of UIView, aligned: HorizontalAlignment)
left(of UIView, aligned: VerticalAlignment)
right(of UIView, aligned: VerticalAlignment)
view.pin.above(of: view2, aligned: .left)
view.pin.above(of: view2, aligned: .right)
view.pin.left(of: view2, aligned: .top)
The following example layout the view B below the view A aligned on its center.
viewB.pin.below(of: viewA, aligned: .center)
This is an equivalent solutions using anchors:
viewB.pin.topCenter(to: viewA.anchor.bottomCenter)
PinLayout has methods to set the view’s height and width.
Methods:
width(_ width: CGFloat)
The value specifies the width of the view in pixels. Value must be non-negative.
width(percent: Percent)
The value specifies the width of the view in percentage of its superview. Value must be non-negative.
width(of view: UIView)
Set the view’s width to match the referenced view’s width.
height(_ height: CGFloat)
The value specifies the height of the view in pixels.
height(percent: Percent)
The value specifies the height of the view in percentage of its superview. Value must be non-negative.
height(of view: UIView)
Set the view’s height to match the referenced view’s height
size(_ size: CGSize)
The value specifies the size (width and value) of the view in pixels. Values must be non-negative.
size(_ percent: Percent)
The value specifies the width and the height of the view in percentage of its superview. Values must be non-negative.
size(_ sideLength: CGFloat)
The value specifies the width and the height of the view in pixels, creating a square view. Values must be non-negative.
size(of view: UIView)
Set the view’s size to match the referenced view’s size
view.pin.width(50%)
view.pin.width(100)
view.pin.width(of: view1)
view.pin.height(100%)
view.pin.height(200)
view.pin.size(of: view1)
view.pin.size(50%)
view.pin.size(250)
PinLayout applies margins similar to CSS.
PinLayout has methods to apply margins.
Methods:
marginTop(_ value: CGFloat)
marginLeft(_ value: CGFloat)
marginBottom(_ value: CGFloat)
marginRight(_ value: CGFloat)
marginHorizontal(_ value: CGFloat)
marginVertical(_ value: CGFloat)
margin(_ value: CGFloat)
margin(_ vertical: CGFloat, _ horizontal: CGFloat)
margin(_ top: CGFloat, _ horizontal: CGFloat, _ bottom: CGFloat)
margin(_ top: CGFloat, _ left: CGFloat, _ bottom: CGFloat, _ right: CGFloat)
pinEdges()
view.pin.topLeft().margin(20)
view.pin.bottom().marginBottom(20)
view.pin.left().right().marginHorizontal(20)
view.pin.topLeft().bottomRight().margin(10, 12, 0, 12)
The following section explains how CSS/PinLayout margin rules are applied.
This table explains how and when left and right margins are applied depending on which view’s attribute has been pinned using PinLayout.
View’s pinned attributes | Left Margin | Right Margin |
---|---|---|
Left | Move view right | - |
Left and Width | Move view right | - |
Right | - | Move view left |
Right and Width | - | Move view left |
Left and Right | Reduce the width to apply the left margin | Reduce the width to apply the right margin |
NOTE: -
indicates that the margin is not applied.
This table explains how and when top and bottom margins are applied depending on which view’s attribute has been pinned using PinLayout.
View’s pinned attributes | Left Margin | Right Margin |
---|---|---|
Top | Move view down | - |
Top and Height | Move view down | - |
Bottom | - | Move view up |
Bottom and Height | - | Move view up |
Top and Bottom | Reduce the height to apply the top margin | Reduce the height to apply the bottom margin |
In this example, only the left margin is applied
view.pin.left().margin(10)
In this example, only the right margin is applied
view.pin.right().width(100).marginHorizontal(10)
In this example, the left and right margins are applied
view.pin.left().right().margin(10)
In this example, left, right and top margins are applied. Note that the view’s width has been reduced to apply left and right margins.
view.pin.topLeft().right().height(100).margin(10)
In this example, left, right, top and bottom margins are applied.
view.pin.topLeft().bottomRight().margin(10)
The pinEdges()
method pins the four edges (top, left, bottom and right edges) before applying margins.
This method is useful in situations where the width and/or the height attributes have been pinned. This method is an add-on, there is no equivalent in CSS.
pinEdges
Without pinEdges()
margins rules would be applied and the view would be moved to the left.
view.pin.left().width(percent: 100).marginHorizontal(20)
pinEdges
With pinEdges()
the left and right margins are applied even if only the left and width has been set. The reason is the call to pinEdges() has pinned the two horizontal edges at their position before applying margins.
view.pin.left().width(percent: 100).pinEdges().marginHorizontal(20)
NOTE: In that in that particular situation, the same results could have been achieved differently too:
view.pin.left().right().marginHorizontal(20)
In debug, PinLayout will display warnings when pin rules cannot be applied.
Warning reasons
view.pin.left(10).right(10).width(200)
width(200) won't be applied since it conflicts with the following already set properties: left: 0, right: 10.
view.pin.width(100).width(200)
width(200) won't be applied since it value has already been set to 100.
view.pin.width(100)
width(100) won't be applied, the view must be added as a sub-view before being layouted using this method.
view.pin.left(of: view2)
left(of: view2) won't be applied, the view must be added as a sub-view before being used as a reference.
view.pin.width(-100)
The width (-100) must be greater or equal to 0.
Warnings can be disabled in debug mode too by setting the boolean PinLayoutLogConflicts to false.
The following examples show how PinLayout can be used to adjust views size and position to the size of their container. in this case containers are cells.
Cell A:
a1.pin.topLeft().bottom().width(50)
a2.pin.right(of: a1, aligned: .top).bottomRight().marginLeft(10)
Cell B:
b2.pin.topRight().bottom().width(50)
b1.pin.left(of: b2, aligned: .top).bottomLeft().marginRight(10)
Cell C:
c2.pin.topCenter().width(50).bottom()
c1.pin.left(of: c1, aligned: .top).bottomLeft().marginRight(10)
c3.pin.right(of: c2, aligned: .top).bottomRight().marginLeft(10)
Cell D:
d1.pin.topLeft().bottom().width(25%)
d2.pin.right(of: d1, aligned: .top).bottom().width(50%).marginLeft(10)
d3.pin.right(of: d2, aligned: .top).bottomRight().marginLeft(10)
git checkout -b my-new-feature
git commit -am 'Add some feature'
git push origin my-new-feature
BSD 3-Clause License