Skip to content

Commit 641f97c

Browse files
author
Luc Dion
authored
Merge pull request #53 from mirego/min_max
Implementation of minWidth/maxWidth, minHeight/maxHeight, justity/align
2 parents 6742bcb + be1fdff commit 641f97c

23 files changed

+1581
-135
lines changed

Example/.swiftlint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ disabled_rules: # rule identifiers to exclude from running
1818
- force_cast
1919
- type_name
2020
# - todo
21-
# - file_length
21+
- file_length
2222
# - type_body_length
2323
# - valid_docs
2424
# - cyclomatic_complexity

PinLayout.xcodeproj/project.pbxproj

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
240F88BE1F0C066800280FC8 /* JustifyAlignSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 240F88BC1F0C042500280FC8 /* JustifyAlignSpec.swift */; };
11+
240F88C11F0C1F5000280FC8 /* WarningSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 240F88BF1F0C1ED900280FC8 /* WarningSpec.swift */; };
1012
241962391E7F582C00A0466C /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 241962371E7F582C00A0466C /* Nimble.framework */; };
1113
2419623A1E7F582C00A0466C /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 241962381E7F582C00A0466C /* Quick.framework */; };
1214
2419623C1E7F592800A0466C /* Nimble.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 241962371E7F582C00A0466C /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
1315
2419623D1E7F592800A0466C /* Quick.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 241962381E7F582C00A0466C /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
16+
242723731F008BF7006A5C3A /* MinMaxWidthHeightSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 242723711F008B85006A5C3A /* MinMaxWidthHeightSpec.swift */; };
1417
242E8DC31EED5AB2005935FB /* RelativePositionMultipleViewsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 242E8DC11EED5982005935FB /* RelativePositionMultipleViewsSpec.swift */; };
1518
244C6E151E776A0C0074FC74 /* MarginsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 244C6E141E776A0C0074FC74 /* MarginsSpec.swift */; };
1619
244DF3031EF46F6C0090508B /* InfoTVOS.plist in Resources */ = {isa = PBXBuildFile; fileRef = 244DF3011EF46F670090508B /* InfoTVOS.plist */; };
@@ -62,8 +65,11 @@
6265
/* End PBXCopyFilesBuildPhase section */
6366

6467
/* Begin PBXFileReference section */
68+
240F88BC1F0C042500280FC8 /* JustifyAlignSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JustifyAlignSpec.swift; sourceTree = "<group>"; };
69+
240F88BF1F0C1ED900280FC8 /* WarningSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WarningSpec.swift; sourceTree = "<group>"; };
6570
241962371E7F582C00A0466C /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = "<group>"; };
6671
241962381E7F582C00A0466C /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = "<group>"; };
72+
242723711F008B85006A5C3A /* MinMaxWidthHeightSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinMaxWidthHeightSpec.swift; sourceTree = "<group>"; };
6773
242E8DC11EED5982005935FB /* RelativePositionMultipleViewsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelativePositionMultipleViewsSpec.swift; sourceTree = "<group>"; };
6874
244C6E141E776A0C0074FC74 /* MarginsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarginsSpec.swift; sourceTree = "<group>"; };
6975
244DF2F81EF46C500090508B /* PinLayoutTVOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PinLayoutTVOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -168,14 +174,17 @@
168174
2469C5011E75D88500073BEE /* BasicView.swift */,
169175
245302061ED05FD000E13F29 /* AccurencyTests.swift */,
170176
2469C4FF1E75D74000073BEE /* AdjustSizeSpec.swift */,
171-
249EFE881E64FB4C00165E39 /* PinLayoutTests.swift */,
177+
240F88BC1F0C042500280FC8 /* JustifyAlignSpec.swift */,
172178
244C6E141E776A0C0074FC74 /* MarginsSpec.swift */,
179+
242723711F008B85006A5C3A /* MinMaxWidthHeightSpec.swift */,
180+
249EFE881E64FB4C00165E39 /* PinLayoutTests.swift */,
173181
DF7A36BC1E918301000F9856 /* PinEdgesSpec.swift */,
174182
2469C4FB1E74855D00073BEE /* PinEdgeCoordinateSpec.swift */,
175183
246D36471E6C46F50050F202 /* PinPointCoordinatesSpec.swift */,
176184
2469C5031E75DB7600073BEE /* RectNimbleMatcher.swift */,
177185
2482908B1E78CFFC00667D08 /* RelativePositionSpec.swift */,
178186
242E8DC11EED5982005935FB /* RelativePositionMultipleViewsSpec.swift */,
187+
240F88BF1F0C1ED900280FC8 /* WarningSpec.swift */,
179188
);
180189
path = Tests;
181190
sourceTree = "<group>";
@@ -382,10 +391,13 @@
382391
isa = PBXSourcesBuildPhase;
383392
buildActionMask = 2147483647;
384393
files = (
394+
240F88BE1F0C066800280FC8 /* JustifyAlignSpec.swift in Sources */,
385395
2469C5001E75D74000073BEE /* AdjustSizeSpec.swift in Sources */,
386396
2482908C1E78CFFC00667D08 /* RelativePositionSpec.swift in Sources */,
397+
240F88C11F0C1F5000280FC8 /* WarningSpec.swift in Sources */,
387398
242E8DC31EED5AB2005935FB /* RelativePositionMultipleViewsSpec.swift in Sources */,
388399
2469C5041E75DB7600073BEE /* RectNimbleMatcher.swift in Sources */,
400+
242723731F008BF7006A5C3A /* MinMaxWidthHeightSpec.swift in Sources */,
389401
245302071ED05FD000E13F29 /* AccurencyTests.swift in Sources */,
390402
2469C4FC1E74855D00073BEE /* PinEdgeCoordinateSpec.swift in Sources */,
391403
2469C5021E75D88500073BEE /* BasicView.swift in Sources */,

README.md

Lines changed: 125 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ Extremely Fast views layouting without auto layout. No magic, pure code, full co
3838
* [Edges](#edges)
3939
* [Relative positionning](#relative_positionning)
4040
* [Width, height and size](#width_height_size)
41+
* [minWidth, maxWidth, minHeight, maxHeight](#minmax_width_height_size)
42+
* [justify, align](#justify_align)
4143
* [Margins](#margins)
4244
* [Warnings](#warnings)
4345
* [More examples](#more_examples)
@@ -46,6 +48,7 @@ Extremely Fast views layouting without auto layout. No magic, pure code, full co
4648
* [FAQ](#faq)
4749
* [Comments, ideas, suggestions, issues, ....](#comments)
4850

51+
4952
<br>
5053

5154
:pushpin: PinLayout is actively updated, adding more features weekly. So please come often to see latest changes. You can also **Star** it to be able to retrieve it easily later.
@@ -127,10 +130,10 @@ This example layout an image, a UISegmentedControl, a label and a line separator
127130
override func layoutSubviews() {
128131
super.layoutSubviews()
129132

130-
logo.pin.topLeft().size(100).margin(topLayoutGuide + 10, 10, 10)
133+
logo.pin.topLeft().size(100).marginTop(10).marginLeft(1010)
131134
segmented.pin.right(of: logo, aligned: .top).right().marginHorizontal(10)
132135
textLabel.pin.below(of: segmented, aligned: .left).right().marginTop(10).marginRight(10).sizeToFit()
133-
separatorView.pin.below(of: logo, textLabel, aligned: .left).right(to: segmented.edge.right).marginTop(10)
136+
separatorView.pin.below(of: [logo, textLabel], aligned: .left).right(to: segmented.edge.right).marginTop(10)
134137
}
135138
```
136139

@@ -546,7 +549,7 @@ The following example contains a UISwitch. Below a UITextField that is visible o
546549

547550

548551
```swift
549-
formTitleLabel.pin.topCenter().margin(margin)
552+
formTitleLabel.pin.topCenter().marginTop(margin)
550553
nameField.pin.below(of: formTitleLabel).left().right().height(40).margin(margin)
551554

552555
ageSwitch.pin.below(of: nameField).left().right().height(40).margin(margin)
@@ -567,31 +570,23 @@ PinLayout has methods to set the view’s height and width.
567570

568571
**Methods:**
569572

570-
* `width(_ width: CGFloat)`
571-
The value specifies the width of the view in pixels. Value must be non-negative.
572-
573-
* `width(percent: Percent)`
574-
The value specifies the width of the view in percentage of its superview. Value must be non-negative.
575-
573+
* `width(_ width: CGFloat)` / `width(percent: Percent)`
574+
The value specifies the view's width in pixels or in percentage of its superview. Value must be non-negative.
576575
* `width(of view: UIView)`
577576
Set the view’s width to match the referenced view’s width.
578577

579-
* `height(_ height: CGFloat)`
580-
The value specifies the height of the view in pixels.
581-
* `height(percent: Percent)`
582-
The value specifies the height of the view in percentage of its superview. Value must be non-negative.
578+
* `height(_ height: CGFloat)` / `height(percent: Percent)`
579+
The value specifies the view's height in pixels or in percentage of its superview. Value must be non-negative.
583580
* `height(of view: UIView)`
584581
Set the view’s height to match the referenced view’s height
585-
586-
* `size(_ size: CGSize)`
587-
The value specifies the size (width and value) of the view in pixels. Values must be non-negative.
588-
* `size(_ percent: Percent)`
589-
The value specifies the width and the height of the view in percentage of its superview. Values must be non-negative.
582+
* `size(_ size: CGSize)` / `size(_ percent: Percent)`
583+
The value specifies view's width and the height in pixels or in percentage of its superview. Values must be non-negative.
590584
* `size(_ sideLength: CGFloat)`
591585
The value specifies the width and the height of the view in pixels, creating a square view. Values must be non-negative.
592586
* `size(of view: UIView)`
593587
Set the view’s size to match the referenced view’s size
594588

589+
:pushpin: width/height/size have a higher priority than edges and anchors positionning.
595590

596591
###### Usage examples:
597592
```swift
@@ -609,6 +604,111 @@ Set the view’s size to match the referenced view’s size
609604

610605
<br/>
611606

607+
## minWidth, maxWidth, minHeight, maxHeight <a name="minmax_width_height_size"></a>
608+
609+
PinLayout has methods to set the view’s minimum and maximum width, and minimum and maximum height.
610+
611+
**Methods:**
612+
613+
* `minWidth(_ width: CGFloat)` / `minWidth(_ percent: Percent)`
614+
The value specifies the view's minimum width of the view in pixels or in percentage of its superview. Value must be non-negative.
615+
616+
* `maxWidth(_ width: CGFloat)` / `maxWidth(_ percent: Percent)`
617+
The value specifies the view's maximum width of the view in pixels or in percentage of its superview. Value must be non-negative.
618+
619+
* `minHeight(_ height: CGFloat)` / `minHeight(_ percent: Percent)`
620+
The value specifies the view's minimum height of the view in pixels or in percentage of its superview. Value must be non-negative.
621+
622+
* `maxHeight(_ height: CGFloat)` / `maxHeight(_ percent: Percent)`
623+
The value specifies the view's maximum height of the view in pixels or in percentage of its superview. Value must be non-negative.
624+
625+
###### Usage examples:
626+
```swift
627+
view.pin.left(10).right(10).maxWidth(200)
628+
view.pin.width(100%).maxWidth(250)
629+
630+
view.pin.top().bottom().maxHeight(100)
631+
view.pin.top().height(50%).maxHeight(200)
632+
```
633+
634+
:pushpin: minWidth/maxWidth & minHeight/maxHeight have the highest priority. Higher than sizes (width/height/size) and edges positionning (top/left/bottom/right). Their values are always fullfilled.
635+
636+
637+
###### Example:
638+
This example layout a view 20 pixels from the top, and horizontally from left to right with a maximum width of 200 pixels. If the superview is smaller than 200 pixels, the view will take the full horizontal space, but for a larger superview, the view will be centered.
639+
640+
![](docs/pinlayout-example-maxWidth.png)
641+
642+
643+
```swift
644+
viewA.pin.top(20).hCenter().width(100%).maxWidth(200)
645+
```
646+
647+
This is an equivalent solutions using the `justify()` method. This method is explained in the next section:
648+
649+
```swift
650+
viewA.pin.top(20).left().right().maxWidth(200).justify(.center)
651+
```
652+
653+
<br/>
654+
655+
## justify() / align() <a name="justify_align"></a>
656+
657+
**Method:**
658+
659+
* `justify(_ : HorizontalAlign)`
660+
Justify the view horizontally. This method justify horizontally a view in situations where the left, right and the width has been set (using either width/minWidth/maxWidth). In this situation the view may be smaller than the space available between the left and the right edges. A view can be justified **left**, **center** or **right**.
661+
662+
* `align(_ : VerticalAlign)`
663+
Align the view vertically. This method align vertically a view in situations where the top, bottom and the height has been set (using either height/minHeight/maxHeight). In this situation the view may be smaller than the space available between the top and the bottom edges. A view can be aligned **top**, **center** or **bottom**.
664+
665+
###### Usage examples:
666+
```swift
667+
view.pin.left().right().marginHorizontal(20).maxWidth(200).justify(.center)
668+
view.pin.below(of: A).above(of: B).width(40).align(.center)
669+
```
670+
671+
672+
###### Example:
673+
This example layout a view between its superview left and right edges with a maximum size of 200 pixels. Without the usage of the `justify(:HorizontalAlign)` method, the view will be justified on the left:
674+
675+
![](docs/pinlayout-example-justify-left.png)
676+
677+
```swift
678+
viewA.pin.left().right().maxWidth(200)
679+
```
680+
681+
682+
The same example, but using `justify(.center)`:
683+
684+
![](docs/pinlayout-example-justify-center.png)
685+
686+
687+
```swift
688+
viewA.pin.left().right().maxWidth(200).justify(.center)
689+
```
690+
691+
And finally using `justify(.right)`:
692+
693+
![](docs/pinlayout-example-justify-right.png)
694+
695+
696+
```swift
697+
viewA.pin.left().right().maxWidth(200).justify(.right)
698+
```
699+
700+
###### Example:
701+
This example centered horizontally the view B in the space remaining at the right of the view A. The view B has a width of 100 pixels..
702+
703+
![](docs/pinlayout-example-justify-remaining-space.png)
704+
705+
```swift
706+
viewB.pin.left(of: viewA, aligned: .top).right().width(100).justify(.center)
707+
```
708+
709+
<br/>
710+
711+
612712
## Margins <a name="margins"></a>
613713
PinLayout applies margins similar to CSS.
614714

@@ -628,7 +728,7 @@ PinLayout has methods to apply margins.
628728
* `margin(_ value: CGFloat) `
629729
* `margin(_ vertical: CGFloat, _ horizontal: CGFloat)`
630730
* `margin(_ top: CGFloat, _ horizontal: CGFloat, _ bottom: CGFloat)`
631-
* `margin(_ top: CGFloat, _ left: CGFloat, _ bottom: CGFloat, _ right: CGFloat) `
731+
* `margin(_ top: CGFloat, _ right: CGFloat, _ bottom: CGFloat, _ left: CGFloat) `
632732

633733
* `pinEdges()`
634734

@@ -769,7 +869,8 @@ In debug, PinLayout will display warnings when pin rules cannot be applied.
769869
* The newly pinned attributes conflict with other already pinned attributes.
770870
Example:
771871
`view.pin.left(10).right(10).width(200)`
772-
👉 Layout Conflict: `width(200) won't be applied since it conflicts with the following already set properties: left: 0, right: 10.`
872+
👉 Layout Conflict: `width(200) won't be applied since it conflicts with the following already set properties: left: 0, right: 10.`
873+
773874
* The newly pinned attributes have already been set to another value.
774875
Example:
775876
`view.pin.width(100).width(200)`
@@ -787,11 +888,10 @@ Example:
787888
`view.pin.width(-100)`
788889
👉 Layout Warning: `The width (-100) must be greater or equal to 0.`
789890

790-
791-
width and/or height cannot be determined using current PinLaoyout's commands
792-
793-
size(...)'s height won't be aplied...
794-
size(...)'s widht won't be applied....
891+
* `justify(.left|.center|.right)` is used without having set the left and the right coordinates.
892+
Example:
893+
`view.pin.left().width(250).justify(.center)`
894+
👉 PinLayout Warning: justify(center) won't be applied, the left and right coordinates must be set to justify the view.
795895

796896
### Disabling warnings
797897

@@ -955,11 +1055,6 @@ This app is available in the `Example` folder. Note that you must do a `pod inst
9551055
<br>
9561056

9571057

958-
## Coming soon <a name="coming_soon"></a>
959-
* minWidth/maxWidth, minHeight/maxHeight
960-
* ...
961-
962-
9631058
### Contributing, comments, ideas, suggestions, issues, .... <a name="comments"></a>
9641059
For any **comments**, **ideas**, **suggestions**, **issues**, simply open an [issue](https://github.com/mirego/PinLayout/issues).
9651060

Sources/Coordinates.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,10 @@ class Coordinates {
7878
fileprivate static var displayScale: CGFloat = UIScreen.main.scale
7979

8080
static func adjustRectToDisplayScale(_ rect: CGRect) -> CGRect {
81-
return CGRect(x: roundFloatToDisplayScale(rect.origin.x),
82-
y: roundFloatToDisplayScale(rect.origin.y),
83-
width: ceilFloatToDisplayScale(rect.size.width),
84-
height: ceilFloatToDisplayScale(rect.size.height))
81+
return CGRect(x: roundFloatToDisplayScale(rect.origin.x),
82+
y: roundFloatToDisplayScale(rect.origin.y),
83+
width: ceilFloatToDisplayScale(rect.size.width),
84+
height: ceilFloatToDisplayScale(rect.size.height))
8585
}
8686

8787
static func roundFloatToDisplayScale(_ pointValue: CGFloat) -> CGFloat {

0 commit comments

Comments
 (0)