diff --git a/PinLayout.podspec b/PinLayout.podspec index c64f1e73..6fed3070 100644 --- a/PinLayout.podspec +++ b/PinLayout.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "PinLayout" - s.version = "1.0.5" + s.version = "1.0.6" s.summary = "Swift manual views layouting without auto layout, no magic, pure code, full control. Concise syntax, readable & chainable." # This description is used to generate tags and improve search results. diff --git a/PinLayout/Helpers/Coordinates.swift b/PinLayout/Helpers/Coordinates.swift index cf1574e7..e55a4e03 100644 --- a/PinLayout/Helpers/Coordinates.swift +++ b/PinLayout/Helpers/Coordinates.swift @@ -72,7 +72,7 @@ class Coordinates { return CGPoint(x: view.frame.minX + view.frame.width, y: view.frame.minY + view.frame.height) } - static var displayScale: CGFloat = UIScreen.main.scale + fileprivate static var displayScale: CGFloat = UIScreen.main.scale static func adjustRectToDisplayScale(_ rect: CGRect) -> CGRect { return CGRect(x: roundFloatToDisplayScale(rect.origin.x), @@ -89,3 +89,7 @@ class Coordinates { return CGFloat(ceilf(Float(pointValue * displayScale))) / displayScale } } + +public func setUnitTest(displayScale: CGFloat) { + Coordinates.displayScale = displayScale +} diff --git a/PinLayout/PinLayoutImpl.swift b/PinLayout/PinLayoutImpl.swift index 320d4baf..cce72d70 100644 --- a/PinLayout/PinLayoutImpl.swift +++ b/PinLayout/PinLayoutImpl.swift @@ -1087,10 +1087,7 @@ extension PinLayoutImpl { let marginToDistribute = newWidth! - sizeThatFits.width // Distribute the width change to Margins - if let _ = _left, let _ = _right { - marginLeft = (marginLeft ?? 0) + (marginToDistribute / 2) - marginRight = (marginRight ?? 0) + (marginToDistribute / 2) - } else if let _ = _left { + if let _ = _left { marginRight = (marginRight ?? 0) + marginToDistribute } else if let _ = _right { marginLeft = (marginLeft ?? 0) + marginToDistribute @@ -1101,10 +1098,7 @@ extension PinLayoutImpl { let marginToDistribute = newHeight! - sizeThatFits.height // Distribute the height change to Margins - if let _ = _top, let _ = _bottom { - marginTop = (marginTop ?? 0) + (marginToDistribute / 2) - marginBottom = (marginBottom ?? 0) + (marginToDistribute / 2) - } else if let _ = _top { + if let _ = _top { marginBottom = (marginBottom ?? 0) + marginToDistribute } else if let _ = _bottom { marginTop = (marginTop ?? 0) + marginToDistribute diff --git a/PinLayoutSample/.ruby-version b/PinLayoutSample/.ruby-version index ecf00d90..45674f16 100644 --- a/PinLayoutSample/.ruby-version +++ b/PinLayoutSample/.ruby-version @@ -1 +1 @@ -2.2.5 \ No newline at end of file +2.3.3 \ No newline at end of file diff --git a/PinLayoutSample/Podfile b/PinLayoutSample/Podfile index 32e15344..f34ebca3 100644 --- a/PinLayoutSample/Podfile +++ b/PinLayoutSample/Podfile @@ -4,15 +4,8 @@ workspace 'PinLayoutSample.xcworkspace' target 'PinLayoutSample' do project 'PinLayoutSample.xcodeproj' - #pod 'YogaKit', '~> 1.1' # Debug only - pod 'Reveal-SDK', :configurations => ['Debug'] - pod 'SwiftLint' + pod 'Reveal-SDK', :configurations => ['Debug'] + pod 'SwiftLint' end - -#target 'PinLayoutTests' do -# project '../PinLayout.xcodeproj' -# pod 'Quick' -# pod 'Nimble' -#end diff --git a/PinLayoutSample/Podfile.lock b/PinLayoutSample/Podfile.lock index 1d4144de..0dd9e682 100644 --- a/PinLayoutSample/Podfile.lock +++ b/PinLayoutSample/Podfile.lock @@ -1,6 +1,6 @@ PODS: - Reveal-SDK (8) - - SwiftLint (0.18.1) + - SwiftLint (0.19.0) DEPENDENCIES: - Reveal-SDK @@ -8,8 +8,8 @@ DEPENDENCIES: SPEC CHECKSUMS: Reveal-SDK: 43be4e662864e937960d0d04d005135e29c4e53b - SwiftLint: b467d08f5b25dc3b3cfed243d8e1b74b91714c67 + SwiftLint: 3537a05b34060e78e7510f04fb7537d738247803 -PODFILE CHECKSUM: 59a9a3ad8ae4f0bb64a6dadf661a354de61b0d83 +PODFILE CHECKSUM: 500d466f69a20c6292b04b938d6dce3e1f8f6e8b -COCOAPODS: 1.1.1 +COCOAPODS: 1.2.1.beta.1 diff --git a/PinLayoutTests/AdjustSizeSpec.swift b/PinLayoutTests/AdjustSizeSpec.swift index cbd7a5a7..c89801b2 100644 --- a/PinLayoutTests/AdjustSizeSpec.swift +++ b/PinLayoutTests/AdjustSizeSpec.swift @@ -51,6 +51,10 @@ class AdjustSizeSpec: QuickSpec { | - bViewChild */ + + beforeSuite { + setUnitTest(displayScale: 2) + } beforeEach { viewController = UIViewController() @@ -200,26 +204,6 @@ class AdjustSizeSpec: QuickSpec { expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 100.0, height: 60.0))) } - it("should adjust the size of aView by calling sizeToFit() method") { - aView.pin.width(100).sizeToFit() - expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 100.0, height: 16.0))) - } - - it("should adjust the size of aView by calling sizeToFit() method") { - aView.pin.width(of: aViewChild).sizeToFit() - expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 50.0, height: 32.0))) - } - - it("should adjust the size of aView by calling sizeToFit() method") { - aView.pin.height(100).sizeToFit() - expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 16.0, height: 100.0))) - } - - it("should adjust the size of aView by calling sizeToFit() method") { - aView.pin.height(of: aViewChild).sizeToFit() - expect(aView.frame).to(beCloseTo(CGRect(x: 140.0, y: 100.0, width: 53.3333333333333, height: 30.0), within: 0.3)) - } - it("should adjust the size of aView by calling sizeToFit() method") { aView.pin.size(of: aViewChild).sizeToFit() expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 50.0, height: 30.0))) @@ -241,69 +225,265 @@ class AdjustSizeSpec: QuickSpec { } } - describe("the result of the sizeThatFits(...) methods") { + describe("the result of the sizeToFit() method when pinning left and right edges") { + it("should adjust the size with sizeToFit()") { + aView.pin.left(20).right(80).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 100.0, width: 300.0, height: 5.5))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.left(20).right(80).marginLeft(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 30.0, y: 100.0, width: 290.0, height: 6))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.left(20).right(80).marginRight(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 100.0, width: 290.0, height: 6.0))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.left(20).right(80).marginLeft(10).marginRight(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 30.0, y: 100.0, width: 280.0, height: 6.0))) + } + + } + + describe("the result of the sizeToFit() method when pinning top and bottom edges") { + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).bottom(80).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 140.0, y: 20.0, width: 5.5, height: 300))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).bottom(80).marginTop(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 140.0, y: 30.0, width: 6, height: 290))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).bottom(80).marginBottom(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 140.0, y: 20.0, width: 6, height: 290))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).bottom(80).marginTop(10).marginBottom(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 140.0, y: 30.0, width: 6, height: 280))) + } + } + + + describe("the result of the sizeToFit() method when pinning right edge + width") { + it("should adjust the size with sizeToFit() and distribute extra width") { + aView.pin.right(100).width(200).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 100.0, y: 100.0, width: 200, height: 8))) + } + + it("should adjust the size with sizeToFit() and distribute extra width") { + aView.pin.right(100).width(200).marginLeft(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 100.0, y: 100.0, width: 200, height: 8))) + } + + it("should adjust the size with sizeToFit() and distribute extra width") { + aView.pin.right(100).width(200).marginRight(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 90.0, y: 100.0, width: 200, height: 8))) + } + + it("should adjust the size with sizeToFit() and distribute extra width") { + aView.pin.right(100).width(200).marginLeft(10).marginRight(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 90.0, y: 100.0, width: 200, height: 8))) + } + } + + describe("the result of the sizeToFit() method when pinning right edge + width + pinEdges()") { + it("should adjust the size with sizeToFit() and distribute extra width") { + aView.pin.right(100).width(200).pinEdges().sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 100.0, y: 100.0, width: 200, height: 8))) + } + + it("should adjust the size with sizeToFit() and distribute extra width") { + aView.pin.right(100).width(200).pinEdges().marginLeft(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 110.0, y: 100.0, width: 190, height: 8.5))) + } + + it("should adjust the size with sizeToFit() and distribute extra width") { + aView.pin.right(100).width(200).pinEdges().marginRight(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 100.0, y: 100.0, width: 190, height: 8.5))) + } + + it("should adjust the size with sizeToFit() and distribute extra width") { + aView.pin.right(100).width(200).pinEdges().marginLeft(10).marginRight(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 110.0, y: 100.0, width: 180, height: 9))) + } + } + + describe("the result of the sizeToFit() method when pinning bottom edge + height") { + it("should adjust the size with sizeToFit() and distribute extra height") { + aView.pin.bottom(100).height(200).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 8, height: 200))) + } + + it("should adjust the size with sizeToFit() and distribute extra height") { + aView.pin.bottom(100).height(200).marginTop(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 8, height: 200))) + } + + it("should adjust the size with sizeToFit() and distribute extra height") { + aView.pin.bottom(100).height(200).marginBottom(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 140.0, y: 90.0, width: 8, height: 200))) + } + + it("should adjust the size with sizeToFit() and distribute extra height") { + aView.pin.bottom(100).height(200).marginTop(10).marginBottom(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 140.0, y: 90.0, width: 8, height: 200))) + } + } + + describe("the result of the sizeToFit() method when pinning bottom edge + height + pinEdges()") { + it("should adjust the size with sizeToFit() and distribute extra height") { + aView.pin.bottom(100).height(200).pinEdges().sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 8, height: 200))) + } + + it("should adjust the size with sizeToFit() and distribute extra height") { + aView.pin.bottom(100).height(200).pinEdges().marginTop(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 140.0, y: 110.0, width: 8.5, height: 190))) + } + + it("should adjust the size with sizeToFit() and distribute extra height") { + aView.pin.bottom(100).height(200).pinEdges().marginBottom(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 8.5, height: 190))) + } + + it("should adjust the size with sizeToFit() and distribute extra height") { + aView.pin.bottom(100).height(200).pinEdges().marginTop(10).marginBottom(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 140.0, y: 110.0, width: 9, height: 180))) + } + } + + describe("the result of the sizeToFit() method when pinning top, left, bottom and right edges") { + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).left(20).bottom(180).right(180).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 20.0, width: 200.0, height: 8))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).left(20).bottom(180).right(180).marginLeft(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 30.0, y: 20.0, width: 190.0, height: 8.5))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).left(20).bottom(180).right(180).marginRight(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 20.0, width: 190.0, height: 8.5))) + } - it("should adjust the size of aView by calling sizeThatFits(size: CGSize) method") { + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).left(20).bottom(180).right(180).marginLeft(10).marginRight(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 30.0, y: 20.0, width: 180.0, height: 9.0))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).left(20).bottom(180).right(180).marginTop(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 30.0, width: 200.0, height: 8))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).left(20).bottom(180).right(180).marginBottom(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 20.0, width: 200.0, height: 8.0))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).left(20).bottom(180).right(180).marginLeft(10).marginBottom(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 30.0, y: 20.0, width: 190.0, height: 8.5))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).left(20).bottom(180).right(180).marginLeft(10).marginTop(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 30.0, y: 30.0, width: 190.0, height: 8.5))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).left(20).bottom(180).right(180).marginLeft(10).marginBottom(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 30.0, y: 20.0, width: 190.0, height: 8.5))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).left(20).bottom(180).right(180).marginLeft(10).marginTop(10).marginBottom(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 30.0, y: 30.0, width: 190.0, height: 8.5))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).left(20).bottom(180).right(180).marginLeft(10).marginRight(10).marginTop(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 30.0, y: 30.0, width: 180.0, height: 9))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).left(20).bottom(180).right(180).marginLeft(10).marginRight(10).marginBottom(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 30.0, y: 20.0, width: 180.0, height: 9.0))) + } + + it("should adjust the size with sizeToFit()") { + aView.pin.top(20).left(20).bottom(180).right(180).marginLeft(10).marginRight(10).marginTop(10).marginBottom(10).sizeToFit() + expect(aView.frame).to(equal(CGRect(x: 30.0, y: 30.0, width: 180.0, height: 9.0))) + } + } + + describe("the result of the sizeToFit(...) methods") { + + it("should adjust the size of aView by calling sizeToFit() method") { aView.pin.width(100).sizeToFit() expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 100.0, height: 16.0))) } - it("should adjust the size of aView by calling sizeThatFits(size) method") { + it("should adjust the size of aView by calling sizeToFit() method") { aView.pin.height(100).sizeToFit() expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 16.0, height: 100.0))) } - it("should adjust the size of aView by calling sizeThatFits") { + it("should adjust the size of aView by calling sizeToFit()") { aView.pin.top(0).height(70).width(100).sizeToFit() expect(aView.frame).to(equal(CGRect(x: 140.0, y: 0.0, width: 100.0, height: 16.0))) } - it("should adjust the size of aView by calling sizeThatFits(width: CGFloat) method") { + it("should adjust the size of aView by calling sizeToFit() method") { aView.pin.width(100).sizeToFit() expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 100.0, height: 16.0))) } - it("should warn that sizeThatFits(width:) won't be applied") { + it("should adjust the size of aView by calling sizeToFit() method") { aView.pin.height(90).width(100).sizeToFit() expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 100.0, height: 16.0))) } - it("should adjust the size of aView by calling sizeThatFits(widthOf: UIView) method") { + it("should adjust the size of aView by calling sizeToFit() method") { aView.pin.width(of: aViewChild).sizeToFit() expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 50.0, height: 32.0))) } - it("should warn that sizeThatFits(widthOf:) won't be applied") { + it("should adjust the size of aView by calling sizeToFit() method") { aView.pin.width(80).width(of: aViewChild).sizeToFit() expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 80.0, height: 20.0))) } - it("should adjust the size of aView by calling sizeThatFits(height: CGFloat) method") { + it("should adjust the size of aView by calling sizeToFit() method") { aView.pin.height(100).sizeToFit() expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 16.0, height: 100.0))) } - it("should warn that sizeThatFits(height:) won't be applied") { - aView.pin.width(90).height(100).sizeToFit() - expect(aView.frame).to(beCloseTo(CGRect(x: 140.0, y: 100.0, width: 90.0, height: 17.6), within: 0.4)) - } - - it("should adjust the size of aView by calling sizeThatFits(heightOf: UIView) method") { + it("should adjust the size of aView by calling sizeToFit() method") { aView.pin.height(of: aViewChild).sizeToFit() expect(aView.frame).to(beCloseTo(CGRect(x: 140.0, y: 100.0, width: 53.3333333333333, height: 30.0), within: 0.4)) } - it("should warn that sizeThatFits(heightOf:) won't be applied") { + it("should adjust the size of aView by calling sizeToFit() method") { aView.pin.width(90).height(of: aViewChild).sizeToFit() expect(aView.frame).to(beCloseTo(CGRect(x: 140.0, y: 100.0, width: 90.0, height: 17.6), within: 0.4)) } - it("should adjust the size of aView by calling sizeThatFits(sizeOf: UIView) method") { + it("should adjust the size of aView by calling sizeToFit() method") { aView.pin.size(of: aViewChild).sizeToFit() expect(aView.frame).to(beCloseTo(CGRect(x: 140.0, y: 100.0, width: 50.0, height: 30.0))) } - it("should warn that sizeThatFits(sizeOf:) won't be applied") { + it("should adjust the size of aView by calling sizeToFit() method") { aView.pin.width(90).size(of: aViewChild).sizeToFit() expect(aView.frame).to(beCloseTo(CGRect(x: 140.0, y: 100.0, width: 90.0, height: 17.6), within: 0.4)) } diff --git a/README.md b/README.md index 17538d80..b6f238f3 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Swift manual views layouting without auto layout, no magic, pure code, full cont * Concise syntax. Layout most views using a single line. * Stateless - * The layout system doesn’t add any stored properties to UIViews. It simply computes the UIView.frame property, one view at a time. + * PinLayout doesn’t add any stored properties to UIViews. It simply computes the UIView.frame property, one view at a time. * Since it is stateless, it can be used with any other layout framework without conflicts. Each view can use the layout system that better suit it (PinLayout, constraints, flexbox, grids, …) A view can be layouted using PinLayout and later with another method/framework. @@ -55,6 +55,8 @@ A view can be layouted using PinLayout and later with another method/framework. * Before applying the new sets of attributes, PinLayout always start with the view’s current frame. So it’s possible to set the view’s size during the initialization (ex: view.pin.width(100).height(200)), and later only position the view (ex: view.pin.top(10).left(20)). This makes PinLayout really animation friendly. +* Not too intrusive. PinLayout only adds three properties to existing iOS classes: `UIView.pin`, `UIView.anchor` and `UIView.edge` + * Minimize as much as possible calculations and constants when layouting views. * Methods match as much as possible other layouting systems, including CSS, flexbox, reactive Flexbox, … @@ -118,6 +120,8 @@ override func layoutSubviews() { } ``` +:pushpin: This example and some other examples are available in the **PinLayoutSample** project. Please note that you must do a `pod install` before running the sample project. +
@@ -692,6 +696,30 @@ Warnings can be disabled in debug mode too by setting the boolean PinLayoutLogCo
+## PinLayout style guide + +* You should always specifies methods in the same order, it makes layout lines easier to understand. Here is our prefered ordering: +`view.pin.[EDGE|ANCHOR|RELATIVE].[WIDTH|HEIGHT|SIZE].[pinEdges()].[MARGINS].[sizeToFit()]` + + This order reflect the logic inside PinLayout. `pinEdges()` is applied before margins and margins are applied before `sizeToFit()`. + + ```javascript + view.pin.top().left(10%).margin(10, 12, 10, 12) + view.pin.left().width(100%).pinEdges().marginHorizontal(12) + view.pin.left().right().margin(0, 12).sizeToFit() + view.pin.width(100).height(100%) + ``` + +* If the layout line is too long, you can split in multiple lines: + + ``` + textLabel.pin.below(of: titleLabel) + .right(of: statusIcon).left(of: accessoryView) + .above(of: button).marginHorizontal(10) + ``` + +
+ ## More examples ### Adjust to container size @@ -746,7 +774,7 @@ Cell D:
## Comments, ideas, suggestions, issues, .... -For any (I really meant it) **comments**, **ideas**, **suggestions**, **issues**, simply open an [issue](https://github.com/mirego/PinLayout/issues). +For any **comments**, **ideas**, **suggestions**, **issues**, simply open an [issue](https://github.com/mirego/PinLayout/issues).