Skip to content

Commit 1b2c341

Browse files
author
Luc Dion
authored
Merge pull request #84 from mirego/aspect_ratio
Implementation of aspectRatio methods
2 parents 2c55b0a + 5a3fe9a commit 1b2c341

15 files changed

+536
-53
lines changed

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,33 @@
77

88
# Change Log
99

10+
## [1.3.2](https://github.com/mirego/PinLayout/releases/tag/1.3.2)
11+
#### Change
12+
* Add **aspectRatio** methods:
13+
* **`aspectRatio(_ ratio: CGFloat)`**:
14+
Set the view aspect ratio. If a single dimension is set (either width or height), the aspect ratio will be used to compute the other dimension.
15+
* AspectRatio is defined as the ratio between the width and the height (width / height).
16+
* An aspect ratio of 2 means the width is twice the size of the height.
17+
* AspectRatio respects the min (minWidth/minHeight) and the max (maxWidth/maxHeight) dimensions of an item.
18+
Set all margins using an UIEdgeInsets.
19+
This method is particularly useful to set all margins using iOS 11 UIView.safeAreaInsets
20+
* **`aspectRatio(of view: UIView)`**:
21+
Set the view aspect ratio using another UIView's aspect ratio.
22+
23+
AspectRatio is applied only if a single dimension (either width or height) can be determined,
24+
in that case the aspect ratio will be used to compute the other dimension.
25+
26+
* AspectRatio is defined as the ratio between the width and the height (width / height).
27+
* AspectRatio respects the min (minWidth/minHeight) and the max (maxWidth/maxHeight)
28+
dimensions of an item.
29+
30+
* **`aspectRatio()`**:
31+
If the layouted view is an UIImageView, this method will set the aspectRatio using
32+
the UIImageView's image dimension.
33+
34+
For other types of views, this method as no impact.
35+
* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#84](https://github.com/mirego/PinLayout/pull/84)
36+
*
1037
## [1.3.1](https://github.com/mirego/PinLayout/releases/tag/1.3.1)
1138
#### Change
1239
* Add new margin method `margin(_ insets: UIEdgeInsets)`

PinLayout.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
1616
#
1717

1818
s.name = "PinLayout"
19-
s.version = "1.3.1"
19+
s.version = "1.3.2"
2020
s.summary = "Fast Swift UIViews layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainable."
2121

2222
# This description is used to generate tags and improve search results.

PinLayout.xcodeproj/project.pbxproj

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
2469C5041E75DB7600073BEE /* RectNimbleMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2469C5031E75DB7600073BEE /* RectNimbleMatcher.swift */; };
2222
246D36481E6C46F50050F202 /* PinPointCoordinatesSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 246D36471E6C46F50050F202 /* PinPointCoordinatesSpec.swift */; };
2323
2482908C1E78CFFC00667D08 /* RelativePositionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2482908B1E78CFFC00667D08 /* RelativePositionSpec.swift */; };
24+
248E4C741F7A883800C0E7F7 /* AspectRatioTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 248E4C721F7A83FA00C0E7F7 /* AspectRatioTests.swift */; };
25+
248E4C771F7A88D200C0E7F7 /* UIImage+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 248E4C751F7A88CF00C0E7F7 /* UIImage+Color.swift */; };
2426
24949A281EF550E2003643D3 /* PinLayoutImpl+Relative.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24949A271EF550E2003643D3 /* PinLayoutImpl+Relative.swift */; };
2527
24949A2A1EF551D6003643D3 /* PinLayoutImpl+Coordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24949A291EF551D6003643D3 /* PinLayoutImpl+Coordinates.swift */; };
2628
24949A2C1EF55216003643D3 /* PinLayoutImpl+Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24949A2B1EF55216003643D3 /* PinLayoutImpl+Warning.swift */; };
@@ -92,6 +94,8 @@
9294
2469C5031E75DB7600073BEE /* RectNimbleMatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RectNimbleMatcher.swift; sourceTree = "<group>"; };
9395
246D36471E6C46F50050F202 /* PinPointCoordinatesSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinPointCoordinatesSpec.swift; sourceTree = "<group>"; };
9496
2482908B1E78CFFC00667D08 /* RelativePositionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelativePositionSpec.swift; sourceTree = "<group>"; };
97+
248E4C721F7A83FA00C0E7F7 /* AspectRatioTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AspectRatioTests.swift; sourceTree = "<group>"; };
98+
248E4C751F7A88CF00C0E7F7 /* UIImage+Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Color.swift"; sourceTree = "<group>"; };
9599
24949A271EF550E2003643D3 /* PinLayoutImpl+Relative.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PinLayoutImpl+Relative.swift"; sourceTree = "<group>"; };
96100
24949A291EF551D6003643D3 /* PinLayoutImpl+Coordinates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PinLayoutImpl+Coordinates.swift"; sourceTree = "<group>"; };
97101
24949A2B1EF55216003643D3 /* PinLayoutImpl+Warning.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PinLayoutImpl+Warning.swift"; sourceTree = "<group>"; };
@@ -179,7 +183,6 @@
179183
DFC97CA61E8A8F2C001545D5 /* PinLayout.swift */,
180184
24949A2D1EF69474003643D3 /* PinLayout+Filters.swift */,
181185
24D18D231F3E37DD008129EF /* PinLayoutGlobals.swift */,
182-
24BBE6361F50FADE0091D5E9 /* Percent.swift */,
183186
DFA06B031E8B38B300B6D5E7 /* Impl */,
184187
DF11A3741E834181008B33E5 /* Supporting Files */,
185188
);
@@ -193,6 +196,7 @@
193196
2469C5011E75D88500073BEE /* BasicView.swift */,
194197
245302061ED05FD000E13F29 /* AccurencyTests.swift */,
195198
2469C4FF1E75D74000073BEE /* AdjustSizeSpec.swift */,
199+
248E4C721F7A83FA00C0E7F7 /* AspectRatioTests.swift */,
196200
240F88BC1F0C042500280FC8 /* JustifyAlignSpec.swift */,
197201
244C6E141E776A0C0074FC74 /* MarginsSpec.swift */,
198202
242723711F008B85006A5C3A /* MinMaxWidthHeightSpec.swift */,
@@ -205,6 +209,7 @@
205209
2482908B1E78CFFC00667D08 /* RelativePositionSpec.swift */,
206210
242E8DC11EED5982005935FB /* RelativePositionMultipleViewsSpec.swift */,
207211
240F88BF1F0C1ED900280FC8 /* WarningSpec.swift */,
212+
248E4C751F7A88CF00C0E7F7 /* UIImage+Color.swift */,
208213
);
209214
path = Tests;
210215
sourceTree = "<group>";
@@ -224,6 +229,7 @@
224229
DFA06B031E8B38B300B6D5E7 /* Impl */ = {
225230
isa = PBXGroup;
226231
children = (
232+
24BBE6361F50FADE0091D5E9 /* Percent.swift */,
227233
24A9782D1EE845BB002BD0F1 /* Coordinates.swift */,
228234
DFC97CA41E8A8EB3001545D5 /* PinLayoutImpl.swift */,
229235
24949A291EF551D6003643D3 /* PinLayoutImpl+Coordinates.swift */,
@@ -500,6 +506,7 @@
500506
240F88BE1F0C066800280FC8 /* JustifyAlignSpec.swift in Sources */,
501507
2469C5001E75D74000073BEE /* AdjustSizeSpec.swift in Sources */,
502508
2482908C1E78CFFC00667D08 /* RelativePositionSpec.swift in Sources */,
509+
248E4C741F7A883800C0E7F7 /* AspectRatioTests.swift in Sources */,
503510
240F88C11F0C1F5000280FC8 /* WarningSpec.swift in Sources */,
504511
242E8DC31EED5AB2005935FB /* RelativePositionMultipleViewsSpec.swift in Sources */,
505512
2469C5041E75DB7600073BEE /* RectNimbleMatcher.swift in Sources */,
@@ -512,6 +519,7 @@
512519
DF7A36BD1E918301000F9856 /* PinEdgesSpec.swift in Sources */,
513520
244C6E151E776A0C0074FC74 /* MarginsSpec.swift in Sources */,
514521
249EFE891E64FB4C00165E39 /* PinLayoutTests.swift in Sources */,
522+
248E4C771F7A88D200C0E7F7 /* UIImage+Color.swift in Sources */,
515523
);
516524
runOnlyForDeploymentPostprocessing = 0;
517525
};

README.md

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Extremely Fast views layouting without auto layout. No magic, pure code, full co
3737
* [Relative positioning](#relative_positioning)
3838
* [Width, height and size](#width_height_size)
3939
* [minWidth, maxWidth, minHeight, maxHeight](#minmax_width_height_size)
40+
* [Aspect Ratio](#aspect_ratio)
4041
* [justify, align](#justify_align)
4142
* [Margins](#margins)
4243
* [Warnings](#warnings)
@@ -789,6 +790,9 @@ The following example layout the UILabel on the right side of the UIImageView wi
789790

790791
PinLayout has methods to set the view’s minimum and maximum width, and minimum and maximum height.
791792

793+
:pushpin: minWidth/maxWidth & minHeight/maxHeight have the highest priority. Higher than sizes (width/height/size, fitSize, aspectRatio) and edges positioning (top/left/bottom/right). Their values are always fullfilled.
794+
795+
792796
**Methods:**
793797

794798
* **`minWidth(_ width: CGFloat)`**
@@ -816,9 +820,6 @@ The value specifies the view's maximum height of the view in pixels or in percen
816820
view.pin.top().height(50%).maxHeight(200)
817821
```
818822

819-
:pushpin: minWidth/maxWidth & minHeight/maxHeight have the highest priority. Higher than sizes (width/height/size) and edges positioning (top/left/bottom/right). Their values are always fullfilled.
820-
821-
822823
###### Example:
823824
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.
824825

@@ -837,6 +838,46 @@ This is an equivalent solutions using the `justify()` method. This method is exp
837838

838839
<br/>
839840

841+
842+
## Aspect Ratio <a name="aspect_ratio"></a>
843+
Set the view aspect ratio.
844+
AspectRatio solves the problem of knowing one dimension of an element and an aspect ratio, this is particularly useful for images.
845+
846+
AspectRatio is applied only if a single dimension (either width or height) can be determined, in that case the aspect ratio will be used to compute the other dimension.
847+
848+
* AspectRatio is defined as the ratio between the width and the height (width / height).
849+
* An aspect ratio of 2 means the width is twice the size of the height.
850+
* AspectRatio respects the min (minWidth/minHeight) and the max (maxWidth/maxHeight)
851+
dimensions of an item.
852+
853+
**Methods:**
854+
855+
* **`aspectRatio(_ ratio: CGFloat)`**:
856+
Set the view aspect ratio using a CGFloat. AspectRatio is defined as the ratio between the width and the height (width / height).
857+
* **`aspectRatio(of view: UIView)`**:
858+
Set the view aspect ratio using another UIView's aspect ratio.
859+
* **`aspectRatio()`**:
860+
If the layouted view is an UIImageView, this method will set the aspectRatio using the UIImageView's image dimension. For other types of views, this method as no impact.
861+
862+
###### Usage examples:
863+
```swift
864+
aView.pin.left().width(100%).aspectRatio(2)
865+
imageView.pin.left().width(200).aspectRatio()
866+
```
867+
868+
###### Example:
869+
This example layout an UIImageView at the top and center it horizontally, it also adjust its width to 50%. The view’s height will be adjusted automatically using the image aspect ratio.
870+
871+
![](docs/pinlayout_example_aspectratio.png)
872+
873+
```swift
874+
imageView.pin.top().hCenter().width(50%).aspectRatio()
875+
```
876+
877+
878+
</br>
879+
880+
840881
## justify() / align() <a name="justify_align"></a>
841882

842883
**Methods:**
@@ -1258,10 +1299,14 @@ This app is available in the `Example` folder. Note that you must do a `pod inst
12581299
let percentageValue: CGFloat = 50
12591300
view.pin.width(percentageValue%)
12601301
```
1302+
* For other questions, you can checks already [answered questions here.](https://github.com/mirego/PinLayout/issues?q=is%3Aissue+is%3Aclosed+label%3Aquestion)
1303+
12611304
<br>
12621305

12631306

1264-
### Contributing, comments, ideas, suggestions, issues, .... <a name="comments"></a>
1307+
### Questions, comments, ideas, suggestions, issues, .... <a name="comments"></a>
1308+
If you have questions, you can checks already [answered questions here.](https://github.com/mirego/PinLayout/issues?q=is%3Aissue+is%3Aclosed+label%3Aquestion)
1309+
12651310
For any **comments**, **ideas**, **suggestions**, **issues**, simply open an [issue](https://github.com/mirego/PinLayout/issues).
12661311

12671312
If you find PinLayout interesting, thanks to **Star** it. You'll be able to retrieve it easily later.

Sources/PinLayout.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,37 @@ public protocol PinLayout {
279279
@discardableResult func size(_ percent: Percent) -> PinLayout
280280
@discardableResult func size(of view: UIView) -> PinLayout
281281

282+
/**
283+
Set the view aspect ratio.
284+
285+
AspectRatio is applied only if a single dimension (either width or height) can be determined,
286+
in that case the aspect ratio will be used to compute the other dimension.
287+
288+
* AspectRatio is defined as the ratio between the width and the height (width / height).
289+
* An aspect ratio of 2 means the width is twice the size of the height.
290+
* AspectRatio respects the min (minWidth/minHeight) and the max (maxWidth/maxHeight)
291+
dimensions of an item.
292+
*/
293+
@discardableResult func aspectRatio(_ ratio: CGFloat) -> PinLayout
294+
/**
295+
Set the view aspect ratio using another UIView's aspect ratio.
296+
297+
AspectRatio is applied only if a single dimension (either width or height) can be determined,
298+
in that case the aspect ratio will be used to compute the other dimension.
299+
300+
* AspectRatio is defined as the ratio between the width and the height (width / height).
301+
* AspectRatio respects the min (minWidth/minHeight) and the max (maxWidth/maxHeight)
302+
dimensions of an item.
303+
*/
304+
@discardableResult func aspectRatio(of view: UIView) -> PinLayout
305+
/**
306+
If the layouted view is an UIImageView, this method will set the aspectRatio using
307+
the UIImageView's image dimension.
308+
309+
For other types of views, this method as no impact.
310+
*/
311+
@discardableResult func aspectRatio() -> PinLayout
312+
282313
@available(*, deprecated, message: "You should now use fitSize() instead.")
283314
@discardableResult func sizeToFit() -> PinLayout
284315
@discardableResult func fitSize() -> PinLayout

Sources/PinLayoutImpl+Coordinates.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ extension PinLayoutImpl {
279279

280280
internal func validateWidth(_ width: CGFloat, context: Context) -> Bool {
281281
if width < 0 {
282-
warn("the width (\(width)) must be greater than or equal to zero.", context);
282+
warnWontBeApplied("the width (\(width)) must be greater than or equal to zero.", context);
283283
return false
284284
} else {
285285
return true
@@ -297,7 +297,7 @@ extension PinLayoutImpl {
297297

298298
internal func validateHeight(_ height: CGFloat, context: Context) -> Bool {
299299
if height < 0 {
300-
warn("the height (\(height)) must be greater than or equal to zero.", context);
300+
warnWontBeApplied("the height (\(height)) must be greater than or equal to zero.", context);
301301
return false
302302
} else {
303303
return true
@@ -338,7 +338,7 @@ extension PinLayoutImpl {
338338
})
339339

340340
guard results.count > 0 else {
341-
warn("no valid references", context)
341+
warnWontBeApplied("no valid references", context)
342342
return nil
343343
}
344344

Sources/PinLayoutImpl+Relative.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ extension PinLayoutImpl {
368368
fileprivate func validateRelativeViews(_ relativeViews: [UIView], context: Context) -> [UIView]? {
369369
guard let _ = layoutSuperview(context) else { return nil }
370370
guard relativeViews.count > 0 else {
371-
warn("At least one view must be visible (i.e. UIView.isHidden != true) ", context)
371+
warnWontBeApplied("At least one view must be visible (i.e. UIView.isHidden != true) ", context)
372372
return nil
373373
}
374374

Sources/PinLayoutImpl+Warning.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ extension PinLayoutImpl {
2828
return "\(method)(to: .\(anchor.type.rawValue), of: \(viewDescription(anchor.view)))"
2929
}
3030

31-
internal func warn(_ text: String, _ context: Context) {
31+
internal func warnWontBeApplied(_ text: String, _ context: Context) {
3232
guard Pin.logWarnings else { return }
3333
warn("\(context()) won't be applied, \(text)")
3434
}
35-
35+
3636
internal func warn(_ text: String) {
3737
guard Pin.logWarnings else { return }
3838
displayWarning("PinLayout Warning: \(text)")
@@ -48,7 +48,7 @@ extension PinLayoutImpl {
4848
displayWarning("PinLayout Conflict: \(context()) won't be applied since it value has already been set to CGSize(width: \(propertyValue.width), height: \(propertyValue.height)).")
4949
}
5050

51-
internal func warnConflict(_ context: Context, _ properties: [String: CGFloat]) {
51+
internal func warnConflict(_ context: Context, _ properties: [String: Any]) {
5252
guard Pin.logWarnings else { return }
5353
var warning = "PinLayout Conflict: \(context()) won't be applied since it conflicts with the following already set properties:"
5454
properties.forEach { (property) in
@@ -62,22 +62,22 @@ extension PinLayoutImpl {
6262
if let justify = justify {
6363
func context() -> String { return "justify(.\(justify))" }
6464
if !((_left != nil && _right != nil) || (shouldPinEdges && width != nil && (_left != nil || _right != nil || _hCenter != nil))) {
65-
warn("the left and right coordinates must be set to justify the view horizontally.", context)
65+
warnWontBeApplied("the left and right coordinates must be set to justify the view horizontally.", context)
6666
}
6767

6868
if _hCenter != nil {
69-
warn("justification is not applied when hCenter has been set. By default the view will be centered around the hCenter.", context)
69+
warnWontBeApplied("justification is not applied when hCenter has been set. By default the view will be centered around the hCenter.", context)
7070
}
7171
}
7272

7373
if let align = align {
7474
func context() -> String { return "align(.\(align))" }
7575
if !((_top != nil && _bottom != nil) || (shouldPinEdges && height != nil && (_top != nil || _bottom != nil || _vCenter != nil))) {
76-
warn("the top and bottom coordinates must be set to align the view vertically.", context)
76+
warnWontBeApplied("the top and bottom coordinates must be set to align the view vertically.", context)
7777
}
7878

7979
if _vCenter != nil {
80-
warn("alignment is not applied when vCenter has been set. By default the view will be centered around the specified vCenter.", context)
80+
warnWontBeApplied("alignment is not applied when vCenter has been set. By default the view will be centered around the specified vCenter.", context)
8181
}
8282
}
8383
}

0 commit comments

Comments
 (0)