Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for CALayer layout #151

Merged
merged 7 commits into from
Jun 26, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions PinLayout.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@
C82DC20C20CE9F6800B7ACF5 /* Layoutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C82DC20B20CE9F6800B7ACF5 /* Layoutable.swift */; };
C82DC20D20CE9F6800B7ACF5 /* Layoutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C82DC20B20CE9F6800B7ACF5 /* Layoutable.swift */; };
C82DC20E20CE9F6800B7ACF5 /* Layoutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C82DC20B20CE9F6800B7ACF5 /* Layoutable.swift */; };
C83588C120DBC65500D6E8F9 /* CALayerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */; };
C83588C220DBC65600D6E8F9 /* CALayerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */; };
C83588C320DBC65600D6E8F9 /* CALayerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */; };
C8C4928D20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C4928C20DA7DA700048357 /* CALayer+PinLayout.swift */; };
C8C4928E20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C4928C20DA7DA700048357 /* CALayer+PinLayout.swift */; };
C8C4928F20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C4928C20DA7DA700048357 /* CALayer+PinLayout.swift */; };
DF1A5D202084C94700725EF5 /* PinLayoutTestMacOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF1A5D1F2084C94700725EF5 /* PinLayoutTestMacOS.swift */; };
DF1A5D302084CF9700725EF5 /* PinLayoutMacOS.h in Headers */ = {isa = PBXBuildFile; fileRef = DF1A5D2E2084CF9700725EF5 /* PinLayoutMacOS.h */; settings = {ATTRIBUTES = (Public, ); }; };
DF1A5D382084CFC600725EF5 /* Filters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24949A2D1EF69474003643D3 /* Filters.swift */; };
Expand Down Expand Up @@ -216,7 +222,9 @@
C80435D220D0891C00EB1BD7 /* SizeCalculable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SizeCalculable.swift; sourceTree = "<group>"; };
C80435D420D0898000EB1BD7 /* Layoutable+PinLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Layoutable+PinLayout.swift"; sourceTree = "<group>"; };
C82DC20B20CE9F6800B7ACF5 /* Layoutable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Layoutable.swift; sourceTree = "<group>"; };
C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CALayerSpec.swift; sourceTree = "<group>"; };
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>"; };
C8C4928C20DA7DA700048357 /* CALayer+PinLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CALayer+PinLayout.swift"; sourceTree = "<group>"; };
DF1A5D1D2084C94700725EF5 /* PinLayoutTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PinLayoutTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
DF1A5D1F2084C94700725EF5 /* PinLayoutTestMacOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinLayoutTestMacOS.swift; sourceTree = "<group>"; };
DF1A5D2C2084CF9700725EF5 /* PinLayout.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PinLayout.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -409,6 +417,7 @@
children = (
DF702DA820D33D660062045C /* NSView+PinLayout.swift */,
DF702DA920D33D660062045C /* UIView+PinLayout.swift */,
C8C4928C20DA7DA700048357 /* CALayer+PinLayout.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -446,6 +455,7 @@
242E8DC11EED5982005935FB /* RelativePositionMultipleViewsSpec.swift */,
240F88BF1F0C1ED900280FC8 /* WarningSpec.swift */,
DFF222E120BACBA800AC2A84 /* WrapContentSpec.swift */,
C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */,
);
path = Common;
sourceTree = "<group>";
Expand Down Expand Up @@ -830,6 +840,7 @@
buildActionMask = 2147483647;
files = (
DFF222B420B877F900AC2A84 /* PinLayoutObjCImpl.swift in Sources */,
C8C4928F20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */,
DF28022720C2B15B00A1833B /* Types+Description.swift in Sources */,
DFF222B220B877F600AC2A84 /* PinLayoutObjC.swift in Sources */,
DF702D9E20D33CF20062045C /* PinLayout+Relative.swift in Sources */,
Expand Down Expand Up @@ -859,6 +870,7 @@
buildActionMask = 2147483647;
files = (
24D18D241F3E37DD008129EF /* Pin.swift in Sources */,
C8C4928D20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */,
DFF222CF20B99A6600AC2A84 /* Types+Description.swift in Sources */,
DFF222CD20B999BD00AC2A84 /* Types.swift in Sources */,
243C620F1FC3834B0082C327 /* Percent.swift in Sources */,
Expand Down Expand Up @@ -890,6 +902,7 @@
DFF222E320BACBBF00AC2A84 /* WrapContentSpec.swift in Sources */,
240F88BE1F0C066800280FC8 /* JustifyAlignSpec.swift in Sources */,
2469C5001E75D74000073BEE /* AdjustSizeSpec.swift in Sources */,
C83588C120DBC65500D6E8F9 /* CALayerSpec.swift in Sources */,
243B12C81FC3D06F0072A9C3 /* LayoutMethodSpec.swift in Sources */,
2482908C1E78CFFC00667D08 /* RelativePositionSpec.swift in Sources */,
DF1E39B520482B200002D0AA /* PinSafeAreaTests.swift in Sources */,
Expand Down Expand Up @@ -931,6 +944,7 @@
DFABC01F208781A900CB6494 /* Types+Appkit.swift in Sources */,
DFED1552208533DA009EF9A7 /* AspectRatioTests.swift in Sources */,
DFB288AE208540F2001F9588 /* PinEdgeCoordinateSpec.swift in Sources */,
C83588C220DBC65600D6E8F9 /* CALayerSpec.swift in Sources */,
DFB288A420853F32001F9588 /* LayoutMethodSpec.swift in Sources */,
DFB288B3208541D9001F9588 /* WarningSpec.swift in Sources */,
DFB288AD208540B8001F9588 /* PinEdgesSpec.swift in Sources */,
Expand All @@ -942,6 +956,7 @@
buildActionMask = 2147483647;
files = (
DFF222B320B877F800AC2A84 /* PinLayoutObjCImpl.swift in Sources */,
C8C4928E20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */,
DF28022620C2B15A00A1833B /* Types+Description.swift in Sources */,
DFF222B120B877F400AC2A84 /* PinLayoutObjC.swift in Sources */,
DF702D9A20D33CF10062045C /* PinLayout+Relative.swift in Sources */,
Expand Down Expand Up @@ -982,6 +997,7 @@
DFB288B720854252001F9588 /* UIImage+Color.swift in Sources */,
DFF6F9DE2084E15A004F5AED /* UIScrollViewSpec.swift in Sources */,
DFF6F9DD2084E15A004F5AED /* TransformSpec.swift in Sources */,
C83588C320DBC65600D6E8F9 /* CALayerSpec.swift in Sources */,
DFF6F9D32084E15A004F5AED /* MinMaxWidthHeightSpec.swift in Sources */,
DFF6F9CD2084E15A004F5AED /* BasicView.swift in Sources */,
DFF6F9DA2084E15A004F5AED /* RTLSpec.swift in Sources */,
Expand Down
78 changes: 78 additions & 0 deletions Sources/Extensions/CALayer+PinLayout.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// CALayer+PinLayout.swift
// PinLayout
//
// Created by Antoine Lamy on 2018-06-20.
// Copyright © 2018 mcswiftlayyout.mirego.com. All rights reserved.
//

import QuartzCore

extension CALayer: Layoutable {
public typealias View = CALayer

public var superview: CALayer? {
return superlayer
}

public var subviews: [CALayer] {
return sublayers ?? []
}

public var pin: PinLayout<CALayer> {
return PinLayout(view: self, keepTransform: true)
}

public var pinFrame: PinLayout<CALayer> {
return PinLayout(view: self, keepTransform: false)
}

public func getRect(keepTransform: Bool) -> CGRect {
if keepTransform {
/*
To adjust the view's position and size, we don't set the UIView's frame directly, because we want to keep the
view's transform (UIView.transform).
By setting the view's center and bounds we really set the frame of the non-transformed view, and this keep
the view's transform. So view's transforms won't be affected/altered by PinLayout.
*/

let size = bounds.size
// See setRect(...) for details about this calculation.
let origin = CGPoint(x: position.x - (size.width * anchorPoint.x),
y: position.y - (size.height * anchorPoint.y))

return CGRect(origin: origin, size: size)
} else {
return frame
}
}

public func setRect(_ rect: CGRect, keepTransform: Bool) {
let adjustedRect = Coordinates<View>.adjustRectToDisplayScale(rect)

if keepTransform {
/*
To adjust the view's position and size, we don't set the UIView's frame directly, because we want to keep the
view's transform (UIView.transform).
By setting the view's center and bounds we really set the frame of the non-transformed view, and this keep
the view's transform. So view's transforms won't be affected/altered by PinLayout.
*/

// NOTE: The center is offset by the layer.anchorPoint, so we have to take it into account.
position = CGPoint(x: adjustedRect.origin.x + (adjustedRect.width * anchorPoint.x),
y: adjustedRect.origin.y + (adjustedRect.height * anchorPoint.y))
// NOTE: We must set only the bounds's size and keep the origin.
bounds.size = adjustedRect.size
} else {
frame = adjustedRect
}
}

public func isLTR() -> Bool {
switch Pin.layoutDirection {
case .auto: return true
case .ltr: return true
case .rtl: return false
}
}
}
36 changes: 2 additions & 34 deletions Sources/Extensions/UIView+PinLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,43 +38,11 @@ extension UIView: Layoutable, SizeCalculable {
}

public func getRect(keepTransform: Bool) -> CGRect {
if keepTransform {
/*
To adjust the view's position and size, we don't set the UIView's frame directly, because we want to keep the
view's transform (UIView.transform).
By setting the view's center and bounds we really set the frame of the non-transformed view, and this keep
the view's transform. So view's transforms won't be affected/altered by PinLayout.
*/
let size = bounds.size
// See setRect(...) for details about this calculation.
let origin = CGPoint(x: center.x - (size.width * layer.anchorPoint.x),
y: center.y - (size.height * layer.anchorPoint.y))

return CGRect(origin: origin, size: size)
} else {
return frame
}
return layer.getRect(keepTransform: keepTransform)
}

public func setRect(_ rect: CGRect, keepTransform: Bool) {
let adjustedRect = Coordinates<View>.adjustRectToDisplayScale(rect)

if keepTransform {
/*
To adjust the view's position and size, we don't set the UIView's frame directly, because we want to keep the
view's transform (UIView.transform).
By setting the view's center and bounds we really set the frame of the non-transformed view, and this keep
the view's transform. So view's transforms won't be affected/altered by PinLayout.
*/

// NOTE: The center is offset by the layer.anchorPoint, so we have to take it into account.
center = CGPoint(x: adjustedRect.origin.x + (adjustedRect.width * layer.anchorPoint.x),
y: adjustedRect.origin.y + (adjustedRect.height * layer.anchorPoint.y))
// NOTE: We must set only the bounds's size and keep the origin.
bounds.size = adjustedRect.size
} else {
frame = adjustedRect
}
layer.setRect(rect, keepTransform: keepTransform)
}

public func isLTR() -> Bool {
Expand Down
106 changes: 106 additions & 0 deletions Tests/Common/CALayerSpec.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//
// CALayerSpec.swift
// PinLayout-iOS
//
// Created by Antoine Lamy on 2018-06-21.
// Copyright © 2018 mcswiftlayyout.mirego.com. All rights reserved.
//

import Quick
import Nimble
import PinLayout

class CALayerSpec: QuickSpec {
override func spec() {
var viewController: PViewController!
var rootView: BasicView!
var rootLayer: CALayer!
var aLayer: CALayer!
var bLayer: CALayer!

/*
rootLayer
|
- aLayer
bLayer
*/

beforeSuite {
_pinlayoutSetUnitTest(scale: 2)
}

beforeEach {
Pin.lastWarningText = nil
Pin.logMissingLayoutCalls = false

viewController = PViewController()
viewController.view = BasicView()

rootView = BasicView()
rootView.frame = CGRect(x: 0, y: 0, width: 400, height: 400)

rootLayer = CALayer()
rootLayer.frame = CGRect(x: 0, y: 0, width: 400, height: 400)

aLayer = CALayer()
aLayer.bounds.size = CGSize(width: 50, height: 50)
bLayer = CALayer()
bLayer.bounds.size = CGSize(width: 20, height: 20)

#if os(macOS)
rootView.wantsLayer = true
rootView.layer?.addSublayer(rootLayer)
#else
rootView.layer.addSublayer(rootLayer)
#endif

rootLayer.addSublayer(aLayer)
rootLayer.addSublayer(bLayer)

viewController.view.addSubview(rootView)
}

afterEach {
Pin.logMissingLayoutCalls = false
}

//
// CALayer is already heavily tested since UIView delegate it's layout to it's layer.
// Validate only the direct usability.
//
describe("test CALayer interface") {
it("should work with basic pinlayout calls") {
aLayer.pin.top(10).left(10).width(20%).height(80%)
bLayer.pin.right(of: aLayer, aligned: .center)
expect(aLayer.frame).to(equal(CGRect(x: 10, y: 10, width: 80, height: 320)))
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add unit tests that:

  • use pinFrame
  • use edge property
  • use anchor property

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will also do :)


it("should be able to be positioned relatively to edges") {
aLayer.pin.top().right(to: rootLayer.edge.right)
expect(aLayer.frame).to(equal(CGRect(x: 350, y: 0, width: 50, height: 50)))
}

it("should be able to be positioned relatively to anchors") {
aLayer.pin.topLeft(to: rootLayer.anchor.center)
expect(aLayer.frame).to(equal(CGRect(x: 200, y: 200, width: 50, height: 50)))
}

it("should support pinFrame properly when a transform is set") {
rootLayer.transform = CATransform3DIdentity

bLayer.frame = aLayer.frame

aLayer.transform = CATransform3DMakeScale(2, 2, 1)
bLayer.transform = CATransform3DMakeScale(2, 2, 1)

aLayer.pin.top(100).left(100).width(100).height(50)
bLayer.pinFrame.top(100).left(100).width(100).height(50)

expect(aLayer.frame).to(equal(CGRect(x: 50, y: 75, width: 200, height: 100)))
expect(aLayer.bounds).to(equal(CGRect(x: 0, y: 0, width: 100, height: 50)))
expect(bLayer.frame).to(equal(CGRect(x: 100, y: 100, width: 100, height: 50)))
expect(bLayer.bounds).to(equal(CGRect(x: 0, y: 0, width: 50, height: 25)))
}
}
}
}
Loading