Skip to content

Commit 191a818

Browse files
authored
Merge pull request #151 from antoinelamy/feature/CALayer
Add support for CALayer layout
2 parents d60cb4c + dc4e934 commit 191a818

File tree

6 files changed

+262
-128
lines changed

6 files changed

+262
-128
lines changed

PinLayout.xcodeproj/project.pbxproj

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@
4949
C82DC20C20CE9F6800B7ACF5 /* Layoutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C82DC20B20CE9F6800B7ACF5 /* Layoutable.swift */; };
5050
C82DC20D20CE9F6800B7ACF5 /* Layoutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C82DC20B20CE9F6800B7ACF5 /* Layoutable.swift */; };
5151
C82DC20E20CE9F6800B7ACF5 /* Layoutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C82DC20B20CE9F6800B7ACF5 /* Layoutable.swift */; };
52+
C83588C120DBC65500D6E8F9 /* CALayerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */; };
53+
C83588C220DBC65600D6E8F9 /* CALayerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */; };
54+
C83588C320DBC65600D6E8F9 /* CALayerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */; };
55+
C8C4928D20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C4928C20DA7DA700048357 /* CALayer+PinLayout.swift */; };
56+
C8C4928E20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C4928C20DA7DA700048357 /* CALayer+PinLayout.swift */; };
57+
C8C4928F20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C4928C20DA7DA700048357 /* CALayer+PinLayout.swift */; };
5258
DF1A5D202084C94700725EF5 /* PinLayoutTestMacOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF1A5D1F2084C94700725EF5 /* PinLayoutTestMacOS.swift */; };
5359
DF1A5D302084CF9700725EF5 /* PinLayoutMacOS.h in Headers */ = {isa = PBXBuildFile; fileRef = DF1A5D2E2084CF9700725EF5 /* PinLayoutMacOS.h */; settings = {ATTRIBUTES = (Public, ); }; };
5460
DF1A5D382084CFC600725EF5 /* Filters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24949A2D1EF69474003643D3 /* Filters.swift */; };
@@ -216,7 +222,10 @@
216222
C80435D220D0891C00EB1BD7 /* SizeCalculable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SizeCalculable.swift; sourceTree = "<group>"; };
217223
C80435D420D0898000EB1BD7 /* Layoutable+PinLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Layoutable+PinLayout.swift"; sourceTree = "<group>"; };
218224
C82DC20B20CE9F6800B7ACF5 /* Layoutable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Layoutable.swift; sourceTree = "<group>"; };
225+
C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CALayerSpec.swift; sourceTree = "<group>"; };
226+
C83600A520E2949200A3D891 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; usesTabs = 1; };
219227
C8BDEE8FC7F6D6F36D69AE89 /* Pods-PinLayoutTests-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutTests-iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PinLayoutTests-iOS/Pods-PinLayoutTests-iOS.debug.xcconfig"; sourceTree = "<group>"; };
228+
C8C4928C20DA7DA700048357 /* CALayer+PinLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CALayer+PinLayout.swift"; sourceTree = "<group>"; };
220229
DF1A5D1D2084C94700725EF5 /* PinLayoutTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PinLayoutTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
221230
DF1A5D1F2084C94700725EF5 /* PinLayoutTestMacOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinLayoutTestMacOS.swift; sourceTree = "<group>"; };
222231
DF1A5D2C2084CF9700725EF5 /* PinLayout.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PinLayout.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -327,6 +336,7 @@
327336
249EFE701E64FB4C00165E39 = {
328337
isa = PBXGroup;
329338
children = (
339+
C83600A520E2949200A3D891 /* README.md */,
330340
249EFE7C1E64FB4C00165E39 /* Sources */,
331341
249EFE871E64FB4C00165E39 /* Tests */,
332342
249EFE7B1E64FB4C00165E39 /* Products */,
@@ -409,6 +419,7 @@
409419
children = (
410420
DF702DA820D33D660062045C /* NSView+PinLayout.swift */,
411421
DF702DA920D33D660062045C /* UIView+PinLayout.swift */,
422+
C8C4928C20DA7DA700048357 /* CALayer+PinLayout.swift */,
412423
);
413424
path = Extensions;
414425
sourceTree = "<group>";
@@ -446,6 +457,7 @@
446457
242E8DC11EED5982005935FB /* RelativePositionMultipleViewsSpec.swift */,
447458
240F88BF1F0C1ED900280FC8 /* WarningSpec.swift */,
448459
DFF222E120BACBA800AC2A84 /* WrapContentSpec.swift */,
460+
C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */,
449461
);
450462
path = Common;
451463
sourceTree = "<group>";
@@ -830,6 +842,7 @@
830842
buildActionMask = 2147483647;
831843
files = (
832844
DFF222B420B877F900AC2A84 /* PinLayoutObjCImpl.swift in Sources */,
845+
C8C4928F20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */,
833846
DF28022720C2B15B00A1833B /* Types+Description.swift in Sources */,
834847
DFF222B220B877F600AC2A84 /* PinLayoutObjC.swift in Sources */,
835848
DF702D9E20D33CF20062045C /* PinLayout+Relative.swift in Sources */,
@@ -859,6 +872,7 @@
859872
buildActionMask = 2147483647;
860873
files = (
861874
24D18D241F3E37DD008129EF /* Pin.swift in Sources */,
875+
C8C4928D20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */,
862876
DFF222CF20B99A6600AC2A84 /* Types+Description.swift in Sources */,
863877
DFF222CD20B999BD00AC2A84 /* Types.swift in Sources */,
864878
243C620F1FC3834B0082C327 /* Percent.swift in Sources */,
@@ -890,6 +904,7 @@
890904
DFF222E320BACBBF00AC2A84 /* WrapContentSpec.swift in Sources */,
891905
240F88BE1F0C066800280FC8 /* JustifyAlignSpec.swift in Sources */,
892906
2469C5001E75D74000073BEE /* AdjustSizeSpec.swift in Sources */,
907+
C83588C120DBC65500D6E8F9 /* CALayerSpec.swift in Sources */,
893908
243B12C81FC3D06F0072A9C3 /* LayoutMethodSpec.swift in Sources */,
894909
2482908C1E78CFFC00667D08 /* RelativePositionSpec.swift in Sources */,
895910
DF1E39B520482B200002D0AA /* PinSafeAreaTests.swift in Sources */,
@@ -931,6 +946,7 @@
931946
DFABC01F208781A900CB6494 /* Types+Appkit.swift in Sources */,
932947
DFED1552208533DA009EF9A7 /* AspectRatioTests.swift in Sources */,
933948
DFB288AE208540F2001F9588 /* PinEdgeCoordinateSpec.swift in Sources */,
949+
C83588C220DBC65600D6E8F9 /* CALayerSpec.swift in Sources */,
934950
DFB288A420853F32001F9588 /* LayoutMethodSpec.swift in Sources */,
935951
DFB288B3208541D9001F9588 /* WarningSpec.swift in Sources */,
936952
DFB288AD208540B8001F9588 /* PinEdgesSpec.swift in Sources */,
@@ -942,6 +958,7 @@
942958
buildActionMask = 2147483647;
943959
files = (
944960
DFF222B320B877F800AC2A84 /* PinLayoutObjCImpl.swift in Sources */,
961+
C8C4928E20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */,
945962
DF28022620C2B15A00A1833B /* Types+Description.swift in Sources */,
946963
DFF222B120B877F400AC2A84 /* PinLayoutObjC.swift in Sources */,
947964
DF702D9A20D33CF10062045C /* PinLayout+Relative.swift in Sources */,
@@ -982,6 +999,7 @@
982999
DFB288B720854252001F9588 /* UIImage+Color.swift in Sources */,
9831000
DFF6F9DE2084E15A004F5AED /* UIScrollViewSpec.swift in Sources */,
9841001
DFF6F9DD2084E15A004F5AED /* TransformSpec.swift in Sources */,
1002+
C83588C320DBC65600D6E8F9 /* CALayerSpec.swift in Sources */,
9851003
DFF6F9D32084E15A004F5AED /* MinMaxWidthHeightSpec.swift in Sources */,
9861004
DFF6F9CD2084E15A004F5AED /* BasicView.swift in Sources */,
9871005
DFF6F9DA2084E15A004F5AED /* RTLSpec.swift in Sources */,

README.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ Extremely Fast views layouting without auto layout. No magic, pure code, full co
3737
* Swift 3.2+ / Swift 4.0 / Objective-C
3838

3939
### Recent changes/features
40+
* :star: PinLayout can now layout CALayer. See [CALayer Support](#calayer_support) for more information.
41+
4042
* :star: PinLayout has moved to the **[layoutBox](https://github.com/layoutBox)** organization. See other **[layoutBox](https://github.com/layoutBox)** projects.
4143

4244
* :star: Add `wrapContent()` methods that adjust view's width and height to wrap all its subviews. See [wrapContent](#wrapContent) for more information.
@@ -72,6 +74,7 @@ Extremely Fast views layouting without auto layout. No magic, pure code, full co
7274
* [More examples](#more_examples)
7375
* [Examples App](#examples_app)
7476
* [macOS Support](#macos_support)
77+
* [CALayer Support](#calayer_support)
7578
* [PinLayout in Xcode Playgrounds](#playgrounds)
7679
* [PinLayout using Objective-C](#objective_c_interface)
7780
* [Installation](#installation)
@@ -1543,8 +1546,10 @@ PinLayout **support of macOS is not complete**, see here the particularities of
15431546

15441547
* These methods are currently not supported on macOS, but they will be implemented soon:
15451548

1546-
* [`sizeToFit(:FitType)`](#sizeToFit) (Coming soon)
1549+
* [`sizeToFit(:FitType)`](#sizeToFit) on any view that is not a subclass of NSControl
15471550
* [`aspectRatio()`](#aspect_ratio) with no parameters (Coming soon)
1551+
1552+
* Support for [`sizeToFit(:FitType)`](#sizeToFit) can be added to your custom NSView subclasses. Just make those views conform to the `SizeCalculable` protocol and implement the two required functions.
15481553

15491554

15501555
* [`UIView.pin.safeArea`](#safeAreaInsets) property is not available, AppKit doesn't have an UIView.safeAreaInsets equivalent.
@@ -1553,6 +1558,35 @@ All other PinLayout's methods and properties are available on macOS!
15531558

15541559
<br>
15551560

1561+
1562+
<a name="calayer_support"></a>
1563+
## CALayer Support
1564+
1565+
PinLayout can also layouts **CALayer**'s. All PinLayout's properties and methods are available, with the following exceptions:
1566+
1567+
* These methods are currently not supported for CALayers
1568+
1569+
* [`sizeToFit(:FitType)`](#sizeToFit) is not supported.
1570+
* [`aspectRatio()`](#aspect_ratio) with no parameters.
1571+
* [`CALayer.pin.safeArea`](#safeAreaInsets)
1572+
1573+
* Support for [`sizeToFit(:FitType)`](#sizeToFit) can be added to your custom CALayer subclasses. Just make those layers conform to the `SizeCalculable` protocol and implement the two required functions.
1574+
1575+
###### Usage Examples:
1576+
1577+
```swift
1578+
aLayer = CALayer()
1579+
bLayer = CALayer()
1580+
view.layer.addSublayer(aLayer)
1581+
view.layer.addSublayer(bLayer)
1582+
...
1583+
1584+
aLayer.pin.top(10).left(10).width(20%).height(80%)
1585+
bLayer.pin.below(of: aLayer, aligned: .left).size(of: aLayer)
1586+
```
1587+
1588+
<br>
1589+
15561590
<a name="playgrounds"></a>
15571591
## PinLayout in Xcode Playgrounds
15581592

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//
2+
// CALayer+PinLayout.swift
3+
// PinLayout
4+
//
5+
// Created by Antoine Lamy on 2018-06-20.
6+
// Copyright © 2018 mcswiftlayyout.mirego.com. All rights reserved.
7+
//
8+
9+
import QuartzCore
10+
11+
extension CALayer: Layoutable {
12+
public typealias View = CALayer
13+
14+
public var superview: CALayer? {
15+
return superlayer
16+
}
17+
18+
public var subviews: [CALayer] {
19+
return sublayers ?? []
20+
}
21+
22+
public var pin: PinLayout<CALayer> {
23+
return PinLayout(view: self, keepTransform: true)
24+
}
25+
26+
public var pinFrame: PinLayout<CALayer> {
27+
return PinLayout(view: self, keepTransform: false)
28+
}
29+
30+
public func getRect(keepTransform: Bool) -> CGRect {
31+
if keepTransform {
32+
/*
33+
To adjust the view's position and size, we don't set the UIView's frame directly, because we want to keep the
34+
view's transform (UIView.transform).
35+
By setting the view's center and bounds we really set the frame of the non-transformed view, and this keep
36+
the view's transform. So view's transforms won't be affected/altered by PinLayout.
37+
*/
38+
39+
let size = bounds.size
40+
// See setRect(...) for details about this calculation.
41+
let origin = CGPoint(x: position.x - (size.width * anchorPoint.x),
42+
y: position.y - (size.height * anchorPoint.y))
43+
44+
return CGRect(origin: origin, size: size)
45+
} else {
46+
return frame
47+
}
48+
}
49+
50+
public func setRect(_ rect: CGRect, keepTransform: Bool) {
51+
let adjustedRect = Coordinates<View>.adjustRectToDisplayScale(rect)
52+
53+
if keepTransform {
54+
/*
55+
To adjust the view's position and size, we don't set the UIView's frame directly, because we want to keep the
56+
view's transform (UIView.transform).
57+
By setting the view's center and bounds we really set the frame of the non-transformed view, and this keep
58+
the view's transform. So view's transforms won't be affected/altered by PinLayout.
59+
*/
60+
61+
// NOTE: The center is offset by the layer.anchorPoint, so we have to take it into account.
62+
position = CGPoint(x: adjustedRect.origin.x + (adjustedRect.width * anchorPoint.x),
63+
y: adjustedRect.origin.y + (adjustedRect.height * anchorPoint.y))
64+
// NOTE: We must set only the bounds's size and keep the origin.
65+
bounds.size = adjustedRect.size
66+
} else {
67+
frame = adjustedRect
68+
}
69+
}
70+
71+
public func isLTR() -> Bool {
72+
switch Pin.layoutDirection {
73+
case .auto: return true
74+
case .ltr: return true
75+
case .rtl: return false
76+
}
77+
}
78+
}

Sources/Extensions/UIView+PinLayout.swift

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -38,43 +38,11 @@ extension UIView: Layoutable, SizeCalculable {
3838
}
3939

4040
public func getRect(keepTransform: Bool) -> CGRect {
41-
if keepTransform {
42-
/*
43-
To adjust the view's position and size, we don't set the UIView's frame directly, because we want to keep the
44-
view's transform (UIView.transform).
45-
By setting the view's center and bounds we really set the frame of the non-transformed view, and this keep
46-
the view's transform. So view's transforms won't be affected/altered by PinLayout.
47-
*/
48-
let size = bounds.size
49-
// See setRect(...) for details about this calculation.
50-
let origin = CGPoint(x: center.x - (size.width * layer.anchorPoint.x),
51-
y: center.y - (size.height * layer.anchorPoint.y))
52-
53-
return CGRect(origin: origin, size: size)
54-
} else {
55-
return frame
56-
}
41+
return layer.getRect(keepTransform: keepTransform)
5742
}
5843

5944
public func setRect(_ rect: CGRect, keepTransform: Bool) {
60-
let adjustedRect = Coordinates<View>.adjustRectToDisplayScale(rect)
61-
62-
if keepTransform {
63-
/*
64-
To adjust the view's position and size, we don't set the UIView's frame directly, because we want to keep the
65-
view's transform (UIView.transform).
66-
By setting the view's center and bounds we really set the frame of the non-transformed view, and this keep
67-
the view's transform. So view's transforms won't be affected/altered by PinLayout.
68-
*/
69-
70-
// NOTE: The center is offset by the layer.anchorPoint, so we have to take it into account.
71-
center = CGPoint(x: adjustedRect.origin.x + (adjustedRect.width * layer.anchorPoint.x),
72-
y: adjustedRect.origin.y + (adjustedRect.height * layer.anchorPoint.y))
73-
// NOTE: We must set only the bounds's size and keep the origin.
74-
bounds.size = adjustedRect.size
75-
} else {
76-
frame = adjustedRect
77-
}
45+
layer.setRect(rect, keepTransform: keepTransform)
7846
}
7947

8048
public func isLTR() -> Bool {

Tests/Common/CALayerSpec.swift

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//
2+
// CALayerSpec.swift
3+
// PinLayout-iOS
4+
//
5+
// Created by Antoine Lamy on 2018-06-21.
6+
// Copyright © 2018 mcswiftlayyout.mirego.com. All rights reserved.
7+
//
8+
9+
import Quick
10+
import Nimble
11+
import PinLayout
12+
13+
class CALayerSpec: QuickSpec {
14+
override func spec() {
15+
var viewController: PViewController!
16+
var rootView: BasicView!
17+
var rootLayer: CALayer!
18+
var aLayer: CALayer!
19+
var bLayer: CALayer!
20+
21+
/*
22+
rootLayer
23+
|
24+
- aLayer
25+
bLayer
26+
*/
27+
28+
beforeSuite {
29+
_pinlayoutSetUnitTest(scale: 2)
30+
}
31+
32+
beforeEach {
33+
Pin.lastWarningText = nil
34+
Pin.logMissingLayoutCalls = false
35+
36+
viewController = PViewController()
37+
viewController.view = BasicView()
38+
39+
rootView = BasicView()
40+
rootView.frame = CGRect(x: 0, y: 0, width: 400, height: 400)
41+
42+
rootLayer = CALayer()
43+
rootLayer.frame = CGRect(x: 0, y: 0, width: 400, height: 400)
44+
45+
aLayer = CALayer()
46+
aLayer.bounds.size = CGSize(width: 50, height: 50)
47+
bLayer = CALayer()
48+
bLayer.bounds.size = CGSize(width: 20, height: 20)
49+
50+
#if os(macOS)
51+
rootView.wantsLayer = true
52+
rootView.layer?.addSublayer(rootLayer)
53+
#else
54+
rootView.layer.addSublayer(rootLayer)
55+
#endif
56+
57+
rootLayer.addSublayer(aLayer)
58+
rootLayer.addSublayer(bLayer)
59+
60+
viewController.view.addSubview(rootView)
61+
}
62+
63+
afterEach {
64+
Pin.logMissingLayoutCalls = false
65+
}
66+
67+
//
68+
// CALayer is already heavily tested since UIView delegate it's layout to it's layer.
69+
// Validate only the direct usability.
70+
//
71+
describe("test CALayer interface") {
72+
it("should work with basic pinlayout calls") {
73+
aLayer.pin.top(10).left(10).width(20%).height(80%)
74+
bLayer.pin.right(of: aLayer, aligned: .center)
75+
expect(aLayer.frame).to(equal(CGRect(x: 10, y: 10, width: 80, height: 320)))
76+
}
77+
78+
it("should be able to be positioned relatively to edges") {
79+
aLayer.pin.top().right(to: rootLayer.edge.right)
80+
expect(aLayer.frame).to(equal(CGRect(x: 350, y: 0, width: 50, height: 50)))
81+
}
82+
83+
it("should be able to be positioned relatively to anchors") {
84+
aLayer.pin.topLeft(to: rootLayer.anchor.center)
85+
expect(aLayer.frame).to(equal(CGRect(x: 200, y: 200, width: 50, height: 50)))
86+
}
87+
88+
it("should support pinFrame properly when a transform is set") {
89+
rootLayer.transform = CATransform3DIdentity
90+
91+
bLayer.frame = aLayer.frame
92+
93+
aLayer.transform = CATransform3DMakeScale(2, 2, 1)
94+
bLayer.transform = CATransform3DMakeScale(2, 2, 1)
95+
96+
aLayer.pin.top(100).left(100).width(100).height(50)
97+
bLayer.pinFrame.top(100).left(100).width(100).height(50)
98+
99+
expect(aLayer.frame).to(equal(CGRect(x: 50, y: 75, width: 200, height: 100)))
100+
expect(aLayer.bounds).to(equal(CGRect(x: 0, y: 0, width: 100, height: 50)))
101+
expect(bLayer.frame).to(equal(CGRect(x: 100, y: 100, width: 100, height: 50)))
102+
expect(bLayer.bounds).to(equal(CGRect(x: 0, y: 0, width: 50, height: 25)))
103+
}
104+
}
105+
}
106+
}

0 commit comments

Comments
 (0)