Skip to content

Commit af4905a

Browse files
authored
Merge pull request #103 from mirego/layout_method
Add layout() method to support Xcode playgrounds.
2 parents 4b1677a + a5a662b commit af4905a

25 files changed

+368
-126
lines changed

Example/PinLayoutSample/UI/Examples/AutoAdjustingSizeView/AutoAdjustingSizeView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class AutoAdjustingSizeView: BaseView {
8282
row1Item2.pin.right(of: row1Item1, aligned: .top).bottomRight().margin(0, 2, 2, 2)
8383

8484
row2.pin.below(of: row1, aligned: .left).size(of: row1).marginTop(10)
85-
row2Item1.pin.top().right().bottom().width(150).width(25%).margin(2)
85+
row2Item1.pin.top().bottom().right().width(150).margin(2)
8686
row2Item2.pin.left(of: row2Item1, aligned: .top).left().bottom().margin(0, 2, 2, 2)
8787

8888
row3.pin.below(of: row2, aligned: .left).size(of: row1).marginTop(10)

Example/PinLayoutSample/UI/Examples/Intro/IntroView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class IntroView: BaseView {
6262
let containerInsets = safeArea.minInsets(UIEdgeInsets(top: 10, left: 10, bottom: 0, right: 10))
6363
contentView.pin.all().margin(containerInsets)
6464

65-
logo.pin.top().left().size(100).aspectRatio().marginTop(10)
65+
logo.pin.top().left().width(100).aspectRatio().marginTop(10)
6666
segmented.pin.after(of: logo, aligned: .top).right().marginLeft(10)
6767
textLabel.pin.below(of: segmented, aligned: .left).width(of: segmented).pinEdges().marginTop(10).sizeToFit(.width)
6868
separatorView.pin.below(of: [logo, textLabel], aligned: .left).right(to: segmented.edge.right).marginTop(10)

Example/PinLayoutSample/UI/Examples/IntroObjectiveC/IntroObjectiveCView.m

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ @implementation IntroObjectiveCView {
3232

3333
- (id)initWithFrame:(CGRect)frame {
3434
if ((self = [super initWithFrame:frame])) {
35+
Pin.logMissingLayoutCalls = true;
36+
3537
topLayoutGuide = 0;
3638
self.backgroundColor = UIColor.whiteColor;
3739

@@ -56,6 +58,10 @@ - (id)initWithFrame:(CGRect)frame {
5658
return self;
5759
}
5860

61+
- (void)dealloc {
62+
Pin.logMissingLayoutCalls = false;
63+
}
64+
5965
- (void) layoutSubviews {
6066
[super layoutSubviews];
6167

Example/PinLayoutSample/UI/Examples/IntroRTL/IntroRTLView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class IntroRTLView: BaseView {
6767
let containerInsets = safeArea.minInsets(UIEdgeInsets(top: 10, left: 10, bottom: 0, right: 10))
6868
contentView.pin.all().margin(containerInsets)
6969

70-
logo.pin.top().start().size(100).aspectRatio().marginTop(10)
70+
logo.pin.top().start().width(100).aspectRatio().marginTop(10)
7171
segmented.pin.after(of: logo, aligned: .top).end().marginStart(10)
7272
textLabel.pin.below(of: segmented, aligned: .start).width(of: segmented).pinEdges().marginTop(10).sizeToFit(.width)
7373
separatorView.pin.below(of: [logo, textLabel], aligned: .start).end(to: segmented.edge.end).marginTop(10)

PinLayout.xcodeproj/project.pbxproj

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
242723731F008BF7006A5C3A /* MinMaxWidthHeightSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 242723711F008B85006A5C3A /* MinMaxWidthHeightSpec.swift */; };
1515
242E8DC31EED5AB2005935FB /* RelativePositionMultipleViewsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 242E8DC11EED5982005935FB /* RelativePositionMultipleViewsSpec.swift */; };
1616
243B12BC1FC393580072A9C3 /* Percent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243C620E1FC3834B0082C327 /* Percent.swift */; };
17+
243B12C81FC3D06F0072A9C3 /* LayoutMethodSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243B12C41FC3CFC10072A9C3 /* LayoutMethodSpec.swift */; };
18+
243B12CB1FC469D00072A9C3 /* ObjectiveCSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 243B12C91FC469550072A9C3 /* ObjectiveCSpec.m */; };
1719
243C62041FC37F680082C327 /* PinLayoutImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C61FC37C1C0054CADD /* PinLayoutImpl.swift */; };
1820
243C62051FC37F6C0082C327 /* PinLayoutImpl+Coordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C71FC37C1C0054CADD /* PinLayoutImpl+Coordinates.swift */; };
1921
243C62061FC37F6C0082C327 /* PinLayoutImpl+Layouting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C41FC37A900054CADD /* PinLayoutImpl+Layouting.swift */; };
@@ -50,8 +52,8 @@
5052
24BBE6351F50FA1D0091D5E9 /* UnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24BBE6341F50FA1D0091D5E9 /* UnitTests.swift */; };
5153
24BBE6381F52EC380091D5E9 /* UnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24BBE6341F50FA1D0091D5E9 /* UnitTests.swift */; };
5254
24D18D171F3B4381008129EF /* RTLSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24D18D151F3B42E0008129EF /* RTLSpec.swift */; };
53-
24D18D241F3E37DD008129EF /* PinLayoutGlobals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24D18D231F3E37DD008129EF /* PinLayoutGlobals.swift */; };
54-
24D18D261F3E5EA5008129EF /* PinLayoutGlobals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24D18D231F3E37DD008129EF /* PinLayoutGlobals.swift */; };
55+
24D18D241F3E37DD008129EF /* Pin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24D18D231F3E37DD008129EF /* Pin.swift */; };
56+
24D18D261F3E5EA5008129EF /* Pin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24D18D231F3E37DD008129EF /* Pin.swift */; };
5557
CA6C7C85508320B18B1FC777 /* Pods_PinLayoutTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 45CE7ABE76F48416DE57DF55 /* Pods_PinLayoutTests.framework */; };
5658
DF7A36BD1E918301000F9856 /* PinEdgesSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF7A36BC1E918301000F9856 /* PinEdgesSpec.swift */; };
5759
DFC97CA71E8A8F2C001545D5 /* PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFC97CA61E8A8F2C001545D5 /* PinLayout.swift */; };
@@ -86,6 +88,8 @@
8688
241A277C1F8E958F00B1AD39 /* PinLayoutObjCImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinLayoutObjCImpl.swift; sourceTree = "<group>"; };
8789
242723711F008B85006A5C3A /* MinMaxWidthHeightSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinMaxWidthHeightSpec.swift; sourceTree = "<group>"; };
8890
242E8DC11EED5982005935FB /* RelativePositionMultipleViewsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelativePositionMultipleViewsSpec.swift; sourceTree = "<group>"; };
91+
243B12C41FC3CFC10072A9C3 /* LayoutMethodSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutMethodSpec.swift; sourceTree = "<group>"; };
92+
243B12C91FC469550072A9C3 /* ObjectiveCSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ObjectiveCSpec.m; sourceTree = "<group>"; };
8993
243C620E1FC3834B0082C327 /* Percent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Percent.swift; path = Impl/Percent.swift; sourceTree = "<group>"; };
9094
244C6E141E776A0C0074FC74 /* MarginsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarginsSpec.swift; sourceTree = "<group>"; };
9195
244DF2F81EF46C500090508B /* PinLayoutTVOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PinLayoutTVOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -116,7 +120,7 @@
116120
249EFE8A1E64FB4C00165E39 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
117121
24BBE6341F50FA1D0091D5E9 /* UnitTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UnitTests.swift; path = Impl/UnitTests.swift; sourceTree = "<group>"; };
118122
24D18D151F3B42E0008129EF /* RTLSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RTLSpec.swift; sourceTree = "<group>"; };
119-
24D18D231F3E37DD008129EF /* PinLayoutGlobals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinLayoutGlobals.swift; sourceTree = "<group>"; };
123+
24D18D231F3E37DD008129EF /* Pin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pin.swift; sourceTree = "<group>"; };
120124
45CE7ABE76F48416DE57DF55 /* Pods_PinLayoutTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PinLayoutTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
121125
561BC8B0591ACB691B563927 /* Pods-PinLayoutTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-PinLayoutTests/Pods-PinLayoutTests.release.xcconfig"; sourceTree = "<group>"; };
122126
6070E55EDEAA3DC58C0B4CDD /* Pods-PinLayoutTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PinLayoutTests/Pods-PinLayoutTests.debug.xcconfig"; sourceTree = "<group>"; };
@@ -205,7 +209,7 @@
205209
children = (
206210
DFC97CA61E8A8F2C001545D5 /* PinLayout.swift */,
207211
24949A2D1EF69474003643D3 /* PinLayout+Filters.swift */,
208-
24D18D231F3E37DD008129EF /* PinLayoutGlobals.swift */,
212+
24D18D231F3E37DD008129EF /* Pin.swift */,
209213
DFA06B031E8B38B300B6D5E7 /* Impl */,
210214
241A277A1F8E958F00B1AD39 /* ObjectiveC */,
211215
2475B6D51FC37D270054CADD /* SupportingFiles */,
@@ -221,8 +225,10 @@
221225
2469C4FF1E75D74000073BEE /* AdjustSizeSpec.swift */,
222226
248E4C721F7A83FA00C0E7F7 /* AspectRatioTests.swift */,
223227
240F88BC1F0C042500280FC8 /* JustifyAlignSpec.swift */,
228+
243B12C41FC3CFC10072A9C3 /* LayoutMethodSpec.swift */,
224229
244C6E141E776A0C0074FC74 /* MarginsSpec.swift */,
225230
242723711F008B85006A5C3A /* MinMaxWidthHeightSpec.swift */,
231+
243B12C91FC469550072A9C3 /* ObjectiveCSpec.m */,
226232
249EFE881E64FB4C00165E39 /* PinLayoutTests.swift */,
227233
DF7A36BC1E918301000F9856 /* PinEdgesSpec.swift */,
228234
2469C4FB1E74855D00073BEE /* PinEdgeCoordinateSpec.swift */,
@@ -359,7 +365,7 @@
359365
};
360366
249EFE791E64FB4C00165E39 = {
361367
CreatedOnToolsVersion = 8.2.1;
362-
LastSwiftMigration = 0820;
368+
LastSwiftMigration = 0910;
363369
ProvisioningStyle = Automatic;
364370
};
365371
249EFE821E64FB4C00165E39 = {
@@ -467,7 +473,7 @@
467473
buildActionMask = 2147483647;
468474
files = (
469475
243C62081FC37F6C0082C327 /* PinLayoutImpl+Warning.swift in Sources */,
470-
24D18D261F3E5EA5008129EF /* PinLayoutGlobals.swift in Sources */,
476+
24D18D261F3E5EA5008129EF /* Pin.swift in Sources */,
471477
243C62041FC37F680082C327 /* PinLayoutImpl.swift in Sources */,
472478
243C62071FC37F6C0082C327 /* PinLayoutImpl+Relative.swift in Sources */,
473479
243B12BC1FC393580072A9C3 /* Percent.swift in Sources */,
@@ -486,7 +492,7 @@
486492
isa = PBXSourcesBuildPhase;
487493
buildActionMask = 2147483647;
488494
files = (
489-
24D18D241F3E37DD008129EF /* PinLayoutGlobals.swift in Sources */,
495+
24D18D241F3E37DD008129EF /* Pin.swift in Sources */,
490496
2475B6CA1FC37C1C0054CADD /* PinLayoutImpl.swift in Sources */,
491497
241A277E1F8E958F00B1AD39 /* PinLayoutObjCImpl.swift in Sources */,
492498
243C620F1FC3834B0082C327 /* Percent.swift in Sources */,
@@ -510,7 +516,9 @@
510516
files = (
511517
240F88BE1F0C066800280FC8 /* JustifyAlignSpec.swift in Sources */,
512518
2469C5001E75D74000073BEE /* AdjustSizeSpec.swift in Sources */,
519+
243B12C81FC3D06F0072A9C3 /* LayoutMethodSpec.swift in Sources */,
513520
2482908C1E78CFFC00667D08 /* RelativePositionSpec.swift in Sources */,
521+
243B12CB1FC469D00072A9C3 /* ObjectiveCSpec.m in Sources */,
514522
248E4C741F7A883800C0E7F7 /* AspectRatioTests.swift in Sources */,
515523
240F88C11F0C1F5000280FC8 /* WarningSpec.swift in Sources */,
516524
242E8DC31EED5AB2005935FB /* RelativePositionMultipleViewsSpec.swift in Sources */,

README.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ Extremely Fast views layouting without auto layout. No magic, pure code, full co
4343
* [Warnings](#warnings)
4444
* [More examples](#more_examples)
4545
* [Examples App](#examples_app)
46-
* [Using PinLayout with Objective-C](#objective_c_interface)
46+
* [PinLayout in Xcode Playgrounds](#playgrounds)
47+
* [PinLayout using Objective-C](#objective_c_interface)
4748
* [Installation](#installation)
4849
* [FAQ](#faq)
4950
* [Comments, ideas, suggestions, issues, ....](#comments)
@@ -81,7 +82,7 @@ This example layout an image, a UISegmentedControl, a label and a line separator
8182
override func layoutSubviews() {
8283
super.layoutSubviews()
8384

84-
logo.pin.top().left().size(100).aspectRatio().margin(10)
85+
logo.pin.top().left().width(100).aspectRatio().margin(10)
8586
segmented.pin.after(of: logo, aligned: .top).right().marginHorizontal(10)
8687
textLabel.pin.below(of: segmented, aligned: .left).right().marginTop(10).marginRight(10).sizeToFit(.width)
8788
separatorView.pin.below(of: [logo, textLabel], aligned: .left).right(to: segmented.edge.right).marginTop(10)
@@ -1179,6 +1180,10 @@ Example:
11791180
`view.pin.left().width(250).justify(.center)`
11801181
👉 PinLayout Warning: justify(center) won't be applied, the left and right coordinates must be set to justify the view.
11811182

1183+
* Layout must be executed from the **Main thread**.
1184+
👉 PinLayout Warning: Layout must be executed from the Main Thread!
1185+
1186+
11821187
### Disabling warnings
11831188

11841189
Warnings can be disabled also in debug mode by setting the boolean Pin.logWarnings to false.
@@ -1335,10 +1340,18 @@ This app is available in the `Example` folder. Note that you must do a `pod inst
13351340

13361341
<br>
13371342

1338-
## Using PinLayout with Objective-C <a name="objective_c_interface"></a>
1343+
## PinLayout in Xcode Playgrounds <a name="playgrounds"></a>
1344+
1345+
PinLayout layouts views immediately after the line containing `.pin` has been fully executed, thanks to ARC (Automatic Reference Counting) this works perfectly on iOS/tvOS/macOS simulators and devices. But in Xcode Playgrounds, ARC doesn't work as expected, object references are kept much longer. This is a well documented issue and have a little impact on the PinLayout behaviour.
1346+
1347+
[See here for more details about using PinLayout in Xcode playgrounds](docs/xcode_playground.md)
1348+
1349+
<br>
1350+
1351+
## PinLayout using Objective-C <a name="objective_c_interface"></a>
13391352
PinLayout also expose an Objective-C interface slightly different than the Swift interface.
13401353

1341-
[See here for more details](docs/objective_c.md).
1354+
[See here for more details](docs/objective_c.md)
13421355

13431356
<br>
13441357

Sources/Impl/PinLayoutImpl+Warning.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,25 +52,35 @@ extension PinLayoutImpl {
5252

5353
internal func warnPropertyAlreadySet(_ propertyName: String, propertyValue: CGFloat, _ context: Context) {
5454
guard Pin.logWarnings else { return }
55-
displayWarning("PinLayout Conflict: \(context()) won't be applied since it value has already been set to \(propertyValue).")
55+
displayWarning("PinLayout Conflict: \(context()) won't be applied since it value has already been set to \(propertyValue.description).")
5656
}
5757

5858
internal func warnPropertyAlreadySet(_ propertyName: String, propertyValue: CGSize, _ context: Context) {
5959
guard Pin.logWarnings else { return }
60-
displayWarning("PinLayout Conflict: \(context()) won't be applied since it value has already been set to CGSize(width: \(propertyValue.width), height: \(propertyValue.height)).")
60+
displayWarning("PinLayout Conflict: \(context()) won't be applied since it value has already been set to CGSize(width: \(propertyValue.width.description), height: \(propertyValue.height.description)).")
6161
}
6262

6363
internal func warnConflict(_ context: Context, _ properties: [String: Any]) {
6464
guard Pin.logWarnings else { return }
6565
var warning = "PinLayout Conflict: \(context()) won't be applied since it conflicts with the following already set properties:"
6666
properties.forEach { (property) in
67-
warning += "\n \(property.key): \(property.value)"
67+
warning += "\n \(property.key): "
68+
69+
if let floatValue = property.value as? CGFloat {
70+
warning += "\(floatValue.description)"
71+
} else {
72+
warning += "\(property.value)"
73+
}
6874
}
6975

7076
displayWarning(warning)
7177
}
7278

7379
internal func displayLayoutWarnings() {
80+
if !Thread.isMainThread {
81+
warn("Layout must be executed from the Main Thread!")
82+
}
83+
7484
if let justify = justify {
7585
func context() -> String { return "justify(.\(justify.description))" }
7686
if !((_left != nil && _right != nil) || (shouldPinEdges && width != nil && (_left != nil || _right != nil || _hCenter != nil))) {
@@ -96,7 +106,7 @@ extension PinLayoutImpl {
96106

97107
internal func displayWarning(_ text: String) {
98108
var displayText = "\n👉 \(text)"
99-
displayText += "\n (Layouted view info: Type: \(viewName(view)), Frame: \(view.frame)"
109+
displayText += "\n(Layouted view info: Type: \(viewName(view)), Frame: \(view.frame)"
100110

101111
var currentView = view
102112
var hierarchy: [String] = []
@@ -111,7 +121,7 @@ extension PinLayoutImpl {
111121
displayText += ", Tag: \(view.tag))\n"
112122

113123
print(displayText)
114-
_pinlayoutUnitTestLastWarning = text
124+
Pin.lastWarningText = text
115125
}
116126

117127
internal func viewDescription(_ view: UIView) -> String {

0 commit comments

Comments
 (0)