Skip to content

Commit

Permalink
Adds support for %-based margins in layout blocks + tests
Browse files Browse the repository at this point in the history
  • Loading branch information
s4cha committed Mar 30, 2020
1 parent 1390ca1 commit 0f5a031
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 3 deletions.
5 changes: 5 additions & 0 deletions Sources/Stevia/Stevia+Operators.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public func ~ (left: UIView, right: CGFloat) -> UIView {
return left.height(right)
}

@discardableResult
public func ~ (left: UIView, right: SteviaPercentage) -> UIView {
return left.height(right)
}

@discardableResult
public func ~ (left: UIView, right: SteviaFlexibleMargin) -> UIView {
return left.height(right)
Expand Down
28 changes: 28 additions & 0 deletions Sources/Stevia/Stevia+Stacks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public extension UIView {
func layout(_ objects: [Any]) -> [UIView] {
var previousMargin: CGFloat?
var previousFlexibleMargin: SteviaFlexibleMargin?
var previousPercentMargin: SteviaPercentage?

for (i, o) in objects.enumerated() {

Expand Down Expand Up @@ -77,6 +78,23 @@ public extension UIView {
}
}
previousFlexibleMargin = nil
} else if let ppm = previousPercentMargin {
if i == 1 {
v.top(ppm) // only if first view
} else {
if let vx = objects[i-2] as? UIView {
// Add layout guide to suport %-based spaces.
let percent = ppm.value / 100
let lg = UILayoutGuide()
addLayoutGuide(lg)
NSLayoutConstraint.activate([
lg.topAnchor.constraint(equalTo: vx.bottomAnchor),
lg.heightAnchor.constraint(equalTo: heightAnchor, multiplier: percent),
v.topAnchor.constraint(equalTo: lg.bottomAnchor)
])
}
}
previousPercentMargin = nil
} else {
tryStackViewVerticallyWithPreviousView(v, index: i, objects: objects)
}
Expand Down Expand Up @@ -106,6 +124,16 @@ public extension UIView {
va.first!.bottom(fm)
}
}
case let pm as SteviaPercentage:
previousPercentMargin = pm // Store margin for next pass
if i != 0 && i == (objects.count - 1) {
//Last Margin, Bottom
if let previousView = objects[i-1] as? UIView {
previousView.bottom(pm)
} else if let va = objects[i-1] as? [UIView] {
va.first!.bottom(pm)
}
}
case _ as String:() //Do nothin' !
case let a as [UIView]:
align(horizontally: a)
Expand Down
41 changes: 38 additions & 3 deletions Tests/SteviaTests/FullLayoutTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class TestView: UIView {
let password = UITextField()
let login = UIButton()

let view1 = UIView()
let view2 = UIView()

convenience init() {
self.init(frame: CGRect.zero)

Expand All @@ -26,13 +29,26 @@ class TestView: UIView {

layout(
100,
|-email-22-| ~ 80,
|-email-22-| ~ 10%,
20,
|password.width(54) ~ 47,
"",
login.centerHorizontally() ~ 99,
7
)

sv(
view1,
view2
)

layout(
10%,
|view1| ~ 20,
33%,
|view2|,
20%
)
}
}

Expand Down Expand Up @@ -71,7 +87,7 @@ class FullLayoutTests: XCTestCase {
XCTAssertEqual(v.email.frame.origin.x, 8, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.email.frame.width, win.frame.width - 8 - 22,
accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.email.frame.height, 80, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.email.frame.height, win.frame.height*0.1, accuracy: 0.5)

// Password
XCTAssertEqual(v.password.frame.origin.y,
Expand Down Expand Up @@ -107,7 +123,7 @@ class FullLayoutTests: XCTestCase {
XCTAssertEqual(v.email.frame.origin.x, 22, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.email.frame.width, win.frame.width - 8 - 22,
accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.email.frame.height, 80, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.email.frame.height, win.frame.height*0.1, accuracy: 0.5)

// Password
XCTAssertEqual(v.password.frame.origin.y,
Expand All @@ -126,4 +142,23 @@ class FullLayoutTests: XCTestCase {
accuracy: CGFloat(magicalIphoneXShift))
XCTAssertEqual(v.login.frame.height, 99, accuracy: CGFloat(Float.ulpOfOne))
}

func testPercentLayout() {
XCTAssertEqual(vc.view.frame.origin.x, 0, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(vc.view.frame.origin.y, 0, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(vc.view.frame.width, win.frame.width,
accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(vc.view.frame.height, win.frame.height,
accuracy: CGFloat(Float.ulpOfOne))

v.layoutIfNeeded()

XCTAssertEqual(v.view1.frame.origin.y, v.frame.height*0.1, accuracy: 0.5)
XCTAssertEqual(v.view1.frame.origin.x, 0, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.view1.frame.width, v.frame.width, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.view2.frame.origin.y, (v.frame.height*0.1) + 20 + (v.frame.height*0.33), accuracy: 0.5)
XCTAssertEqual(v.view2.frame.origin.x, 0, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.view2.frame.origin.y + v.view2.frame.height, (v.frame.height*0.8), accuracy: 0.5)
XCTAssertEqual(v.view2.frame.width, v.frame.width, accuracy: CGFloat(Float.ulpOfOne))
}
}
9 changes: 9 additions & 0 deletions Tests/SteviaTests/LayoutTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,15 @@ class LayoutTests: XCTestCase {
XCTAssertEqual(v.frame.height, 180, accuracy: CGFloat(Float.ulpOfOne))
}

func testHeightPercentage() {
v ~ 25%
ctrler.view.layoutIfNeeded() // This is needed to force auto-layout to kick-in
XCTAssertEqual(v.frame.origin.y, 0, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.frame.origin.x, 0, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.frame.width, 0, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.frame.height, ctrler.view.frame.height*0.25, accuracy: 0.5)
}

func testMultipleHeightsAtOnce() {
let v1 = UIView()
let v2 = UIView()
Expand Down
20 changes: 20 additions & 0 deletions Tests/SteviaTests/SizeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ class SizeTests: XCTestCase {
XCTAssertEqual(v.frame.height, 23, accuracy: CGFloat(Float.ulpOfOne))
}

func testHeightPercentage() {
v.width(100)
v.height(40%)
ctrler.view.layoutIfNeeded()
XCTAssertEqual(v.frame.origin.y, 0, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.frame.origin.x, 0, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.frame.width, 100, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.frame.height, ctrler.view.frame.height*0.4, accuracy:0.5)
}

func testWidthPercentage() {
v.height(100)
v.width(87%)
ctrler.view.layoutIfNeeded()
XCTAssertEqual(v.frame.origin.y, 0, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.frame.origin.x, 0, accuracy: CGFloat(Float.ulpOfOne))
XCTAssertEqual(v.frame.width, ctrler.view.frame.width*0.87, accuracy: 0.5)
XCTAssertEqual(v.frame.height, 100, accuracy: CGFloat(Float.ulpOfOne))
}

func testEqualSizes() {
let width: CGFloat = 24
let height: CGFloat = 267
Expand Down

0 comments on commit 0f5a031

Please sign in to comment.