From d54d1fc253d5e71ae5d3f73a495c81a2744817a3 Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Tue, 30 Jan 2018 15:09:11 +0600 Subject: [PATCH 1/9] Testing the `center` functions in container --- .../FramezillaExample/ViewController.swift | 74 ++++++++++--------- Sources/Maker.swift | 8 ++ Tests/ContainerInstallerTests.swift | 45 +++++++++++ 3 files changed, 91 insertions(+), 36 deletions(-) diff --git a/Example/FramezillaExample/ViewController.swift b/Example/FramezillaExample/ViewController.swift index 2a5ba16..e49496a 100644 --- a/Example/FramezillaExample/ViewController.swift +++ b/Example/FramezillaExample/ViewController.swift @@ -30,53 +30,55 @@ class ViewController: UIViewController { override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() -// let container = [content1, content2, content3, content4].container(in: view) { + let container = [content1, content2, content3, content4].container(in: view) { + content1.configureFrame { maker in + maker.centerX() + maker.top() + maker.size(width: 50, height: 50) + } + + content2.configureFrame { maker in + maker.top(to: content1.nui_bottom, inset: 5) + maker.left() + maker.size(width: 80, height: 80) + } + + content3.configureFrame { maker in + maker.top(to: content1.nui_bottom, inset: 15) + maker.left(to: content2.nui_right, inset: 5) + maker.size(width: 80, height: 80) + } + + content4.configureFrame { maker in + maker.top(to: content3.nui_bottom, inset: 5) + maker.right() + maker.size(width: 20, height: 20) + } + } + + + +// let container = [content1, content2, content3].container(in: view) { // content1.configureFrame { maker in -// maker.centerX() -// maker.top() +// maker.right() +// maker.centerY() // maker.size(width: 50, height: 50) // } // // content2.configureFrame { maker in -// maker.top(to: content1.nui_bottom, inset: 5) -// maker.left() -// maker.size(width: 80, height: 80) +// maker.right(to: content1.nui_left, inset: 5) +// maker.centerY() +// maker.size(width: 30, height: 140) // } // // content3.configureFrame { maker in -// maker.top(to: content1.nui_bottom, inset: 15) -// maker.left(to: content2.nui_right, inset: 5) +// maker.right(to: content2.nui_left, inset: 15) +// maker.centerY() // maker.size(width: 80, height: 80) // } -// -// content4.configureFrame { maker in -// maker.top(to: content3.nui_bottom, inset: 5) -// maker.right() -// maker.size(width: 20, height: 20) -// } // } - - let container = [content1, content2, content3].container(in: view) { - content1.configureFrame { maker in - maker.right() - maker.centerY() - maker.size(width: 50, height: 50) - } - - content2.configureFrame { maker in - maker.right(to: content1.nui_left, inset: 5) - maker.centerY() - maker.size(width: 30, height: 140) - } - - content3.configureFrame { maker in - maker.right(to: content2.nui_left, inset: 15) - maker.centerY() - maker.size(width: 80, height: 80) - } - } // let container = [content1, content2, content3].container(in: view) { @@ -101,8 +103,8 @@ class ViewController: UIViewController { container.backgroundColor = .yellow container.configureFrame { maker in - maker.centerX() - maker.top() + maker.center() +// maker.top() } } } diff --git a/Sources/Maker.swift b/Sources/Maker.swift index 6cb0664..b3bf472 100644 --- a/Sources/Maker.swift +++ b/Sources/Maker.swift @@ -384,6 +384,14 @@ public final class Maker { var minY: CGFloat = 0 for subview in view.subviews { + if subview.frame.origin.x < 0 { + subview.frame.origin.x = 0 + } + + if subview.frame.origin.y < 0 { + subview.frame.origin.y = 0 + } + if subview.frame.origin.x < minX { minX = subview.frame.origin.x } diff --git a/Tests/ContainerInstallerTests.swift b/Tests/ContainerInstallerTests.swift index cbbbcc0..5e3315b 100644 --- a/Tests/ContainerInstallerTests.swift +++ b/Tests/ContainerInstallerTests.swift @@ -76,4 +76,49 @@ class ContainerInstallerTests: BaseTest { XCTAssertEqual(content2.frame, CGRect(x: 95, y: 0, width: 30, height: 140)) XCTAssertEqual(content3.frame, CGRect(x: 0, y: 30, width: 80, height: 80)) } + + func testContainerConfigurationWithCenterYRelation() { + let content1 = UIView() + let content2 = UIView() + + let container = [content1, content2].container(in: mainView) { + content1.configureFrame { maker in + maker.top(inset: 10) + maker.centerX() + maker.size(width: 180, height: 50) + } + + content2.configureFrame { maker in + maker.top(to: content1.nui_bottom, inset: 10) + maker.left() + maker.size(width: 250, height: 200) + } + } + + XCTAssertEqual(container.frame, CGRect(x: 0, y: 0, width: 250, height: 270)) + XCTAssertEqual(content1.frame, CGRect(x: 35, y: 10, width: 180, height: 50)) + XCTAssertEqual(content2.frame, CGRect(x: 0, y: 70, width: 250, height: 200)) + } + + func testContainerConfigurationWithCenterXRelation() { + let content1 = UIView() + let content2 = UIView() + + let container = [content1, content2].container(in: mainView) { + content2.configureFrame { maker in + maker.top().left(inset: 10) + maker.size(width: 200, height: 250) + } + + content1.configureFrame { maker in + maker.left(to: content2.nui_right, inset: 10) + maker.centerY() + maker.size(width: 50, height: 180) + } + } + + XCTAssertEqual(container.frame, CGRect(x: 0, y: 0, width: 270, height: 250)) + XCTAssertEqual(content1.frame, CGRect(x: 220, y: 35, width: 50, height: 180)) + XCTAssertEqual(content2.frame, CGRect(x: 10, y: 0, width: 200, height: 250)) + } } From 6464c415d3887baeedead0ce64565cebc1a226eb Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Wed, 31 Jan 2018 09:15:52 +0600 Subject: [PATCH 2/9] Fix center relations in container --- .../FramezillaExample/ViewController.swift | 75 ++++++++++--------- Tests/ContainerInstallerTests.swift | 24 +++--- 2 files changed, 51 insertions(+), 48 deletions(-) diff --git a/Example/FramezillaExample/ViewController.swift b/Example/FramezillaExample/ViewController.swift index e49496a..ec1d06f 100644 --- a/Example/FramezillaExample/ViewController.swift +++ b/Example/FramezillaExample/ViewController.swift @@ -30,55 +30,57 @@ class ViewController: UIViewController { override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() - let container = [content1, content2, content3, content4].container(in: view) { - content1.configureFrame { maker in - maker.centerX() - maker.top() - maker.size(width: 50, height: 50) - } - - content2.configureFrame { maker in - maker.top(to: content1.nui_bottom, inset: 5) - maker.left() - maker.size(width: 80, height: 80) - } - - content3.configureFrame { maker in - maker.top(to: content1.nui_bottom, inset: 15) - maker.left(to: content2.nui_right, inset: 5) - maker.size(width: 80, height: 80) - } - - content4.configureFrame { maker in - maker.top(to: content3.nui_bottom, inset: 5) - maker.right() - maker.size(width: 20, height: 20) - } - } - - - -// let container = [content1, content2, content3].container(in: view) { +// let container = [content1, content2, content3, content4].container(in: view) { // content1.configureFrame { maker in -// maker.right() -// maker.centerY() +// maker.centerX() +// maker.top() // maker.size(width: 50, height: 50) // } // // content2.configureFrame { maker in -// maker.right(to: content1.nui_left, inset: 5) -// maker.centerY() -// maker.size(width: 30, height: 140) +// maker.top(to: content1.nui_bottom, inset: 5) +// maker.left() +// maker.size(width: 80, height: 80) // } // // content3.configureFrame { maker in -// maker.right(to: content2.nui_left, inset: 15) -// maker.centerY() +// maker.top(to: content1.nui_bottom, inset: 15) +// maker.left(to: content2.nui_right, inset: 5) // maker.size(width: 80, height: 80) // } +// +// content4.configureFrame { maker in +// maker.top(to: content3.nui_bottom, inset: 5) +// maker.right() +// maker.size(width: 20, height: 20) +// } // } +// +// print("") + + + + let container = [content1, content2, content3].container(in: view) { + content1.configureFrame { maker in + maker.left() + maker.centerY() + maker.size(width: 50, height: 50) + } + + content2.configureFrame { maker in + maker.left(to: content1.nui_right, inset: 5) + maker.centerY() + maker.size(width: 30, height: 140) + } + content3.configureFrame { maker in + maker.left(to: content2.nui_right, inset: 15) + maker.centerY() + maker.size(width: 80, height: 80) + } + } + print("") // let container = [content1, content2, content3].container(in: view) { @@ -104,7 +106,6 @@ class ViewController: UIViewController { container.backgroundColor = .yellow container.configureFrame { maker in maker.center() -// maker.top() } } } diff --git a/Tests/ContainerInstallerTests.swift b/Tests/ContainerInstallerTests.swift index 5e3315b..a1f8daf 100644 --- a/Tests/ContainerInstallerTests.swift +++ b/Tests/ContainerInstallerTests.swift @@ -15,7 +15,7 @@ class ContainerInstallerTests: BaseTest { let content3 = UIView() let content4 = UIView() - _ = [content1, content2, content3, content4].container(in: mainView) { + let container = [content1, content2, content3, content4].container(in: mainView) { content1.configureFrame { maker in maker.centerX() maker.top() @@ -40,11 +40,12 @@ class ContainerInstallerTests: BaseTest { maker.size(width: 20, height: 20) } } - - XCTAssertEqual(content1.frame, CGRect(x: 70, y: 0, width: 50, height: 50)) + + XCTAssertEqual(container.frame, CGRect(x: 0, y: 0, width: 165, height: 170)) + XCTAssertEqual(content1.frame, CGRect(x: 57.5, y: 0, width: 50, height: 50)) XCTAssertEqual(content2.frame, CGRect(x: 0, y: 55, width: 80, height: 80)) XCTAssertEqual(content3.frame, CGRect(x: 85, y: 65, width: 80, height: 80)) - XCTAssertEqual(content4.frame, CGRect(x: 170, y: 150, width: 20, height: 20)) + XCTAssertEqual(content4.frame, CGRect(x: 145, y: 150, width: 20, height: 20)) } func testContainerConfigurationFromRightToLeft() { @@ -52,29 +53,30 @@ class ContainerInstallerTests: BaseTest { let content2 = UIView() let content3 = UIView() - _ = [content1, content2, content3].container(in: mainView) { + let container = [content1, content2, content3].container(in: mainView) { content1.configureFrame { maker in - maker.right() + maker.left() maker.centerY() maker.size(width: 50, height: 50) } content2.configureFrame { maker in - maker.right(to: content1.nui_left, inset: 5) + maker.left(to: content1.nui_right, inset: 5) maker.centerY() maker.size(width: 30, height: 140) } content3.configureFrame { maker in - maker.right(to: content2.nui_left, inset: 15) + maker.left(to: content2.nui_right, inset: 15) maker.centerY() maker.size(width: 80, height: 80) } } - XCTAssertEqual(content1.frame, CGRect(x: 130, y: 45, width: 50, height: 50)) - XCTAssertEqual(content2.frame, CGRect(x: 95, y: 0, width: 30, height: 140)) - XCTAssertEqual(content3.frame, CGRect(x: 0, y: 30, width: 80, height: 80)) + XCTAssertEqual(container.frame, CGRect(x: 0, y: 0, width: 180, height: 140)) + XCTAssertEqual(content1.frame, CGRect(x: 0, y: 45, width: 50, height: 50)) + XCTAssertEqual(content2.frame, CGRect(x: 55, y: 0, width: 30, height: 140)) + XCTAssertEqual(content3.frame, CGRect(x: 100, y: 30, width: 80, height: 80)) } func testContainerConfigurationWithCenterYRelation() { From 2552c242cdd506457f4a2c696f9506302d070ad1 Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Wed, 31 Jan 2018 13:06:49 +0600 Subject: [PATCH 3/9] Add a configuration block to the container method --- .../FramezillaExample/ViewController.swift | 8 ++++--- Framezilla.xcodeproj/project.pbxproj | 8 +++---- Sources/UIView+Installer.swift | 3 ++- ...tallerTests.swift => ContainerTests.swift} | 24 +++++++++++++++++-- 4 files changed, 33 insertions(+), 10 deletions(-) rename Tests/{ContainerInstallerTests.swift => ContainerTests.swift} (87%) diff --git a/Example/FramezillaExample/ViewController.swift b/Example/FramezillaExample/ViewController.swift index ec1d06f..9548e1f 100644 --- a/Example/FramezillaExample/ViewController.swift +++ b/Example/FramezillaExample/ViewController.swift @@ -60,7 +60,9 @@ class ViewController: UIViewController { - let container = [content1, content2, content3].container(in: view) { + let container = [content1, content2, content3].container(in: view, configuration: { container in + container.backgroundColor = .yellow + }, installerBlock: { content1.configureFrame { maker in maker.left() maker.centerY() @@ -78,7 +80,7 @@ class ViewController: UIViewController { maker.centerY() maker.size(width: 80, height: 80) } - } + }) print("") @@ -103,7 +105,7 @@ class ViewController: UIViewController { // } // } - container.backgroundColor = .yellow + container.configureFrame { maker in maker.center() } diff --git a/Framezilla.xcodeproj/project.pbxproj b/Framezilla.xcodeproj/project.pbxproj index e25659b..70690be 100644 --- a/Framezilla.xcodeproj/project.pbxproj +++ b/Framezilla.xcodeproj/project.pbxproj @@ -14,7 +14,7 @@ 11FB41471D844F8F00700A40 /* MakerHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11FB41421D844F8F00700A40 /* MakerHelper.swift */; }; 11FB41481D844F8F00700A40 /* UIView+Installer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11FB41431D844F8F00700A40 /* UIView+Installer.swift */; }; 11FB41491D844F8F00700A40 /* UIView+Relations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11FB41441D844F8F00700A40 /* UIView+Relations.swift */; }; - 841192DD1FF205DC00AB255D /* ContainerInstallerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841192DC1FF205DC00AB255D /* ContainerInstallerTests.swift */; }; + 841192DD1FF205DC00AB255D /* ContainerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841192DC1FF205DC00AB255D /* ContainerTests.swift */; }; 842BB7FC1F0638E9000D1CFF /* Parameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842BB7FB1F0638E9000D1CFF /* Parameters.swift */; }; 8442F93E1EC75A8500B72551 /* BaseTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8442F9351EC75A8500B72551 /* BaseTest.swift */; }; 8442F9401EC75A8500B72551 /* MakerBothSideRelationsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8442F9381EC75A8500B72551 /* MakerBothSideRelationsTests.swift */; }; @@ -52,7 +52,7 @@ 11FB414A1D844FFA00700A40 /* Framezilla.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Framezilla.podspec; sourceTree = ""; }; 11FB414B1D844FFA00700A40 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 11FB414C1D844FFA00700A40 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 841192DC1FF205DC00AB255D /* ContainerInstallerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerInstallerTests.swift; sourceTree = ""; }; + 841192DC1FF205DC00AB255D /* ContainerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerTests.swift; sourceTree = ""; }; 842BB7FB1F0638E9000D1CFF /* Parameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parameters.swift; sourceTree = ""; }; 8442F9351EC75A8500B72551 /* BaseTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTest.swift; sourceTree = ""; }; 8442F9361EC75A8500B72551 /* FramezillaTests-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FramezillaTests-Bridging-Header.h"; sourceTree = ""; }; @@ -140,7 +140,7 @@ 84EDCC231F9B3AB10091FAB9 /* MakerSafeAreaTests.swift */, 8442F93D1EC75A8500B72551 /* StateTests.swift */, 845108061EE2F5BC006DC1C8 /* ScrollViewTests.swift */, - 841192DC1FF205DC00AB255D /* ContainerInstallerTests.swift */, + 841192DC1FF205DC00AB255D /* ContainerTests.swift */, ); path = Tests; sourceTree = ""; @@ -276,7 +276,7 @@ files = ( 8442F9441EC75A8500B72551 /* MakerWidthHeightTests.swift in Sources */, 8442F9451EC75A8500B72551 /* StateTests.swift in Sources */, - 841192DD1FF205DC00AB255D /* ContainerInstallerTests.swift in Sources */, + 841192DD1FF205DC00AB255D /* ContainerTests.swift in Sources */, 8442F9411EC75A8500B72551 /* MakerCenterTests.swift in Sources */, 8442F9421EC75A8500B72551 /* MakerStackTests.swift in Sources */, 8442F9401EC75A8500B72551 /* MakerBothSideRelationsTests.swift in Sources */, diff --git a/Sources/UIView+Installer.swift b/Sources/UIView+Installer.swift index 7e0c9c1..6fb337a 100644 --- a/Sources/UIView+Installer.swift +++ b/Sources/UIView+Installer.swift @@ -130,13 +130,14 @@ public extension Collection where Iterator.Element: UIView, Self.Index == Int, S /// /// - returns: Container view. - public func container(in view: UIView, installerBlock: () -> Void) -> UIView { + public func container(in view: UIView, configuration: ((UIView) -> Void)? = nil, installerBlock: () -> Void) -> UIView { let container: UIView if let superView = self.first?.superview { container = superView } else { container = UIView() + configuration?(container) } for view in self { container.addSubview(view) diff --git a/Tests/ContainerInstallerTests.swift b/Tests/ContainerTests.swift similarity index 87% rename from Tests/ContainerInstallerTests.swift rename to Tests/ContainerTests.swift index a1f8daf..a034fa6 100644 --- a/Tests/ContainerInstallerTests.swift +++ b/Tests/ContainerTests.swift @@ -1,5 +1,5 @@ // -// ContainerInstallerTests.swift +// ContainerTests.swift // FramezillaTests // // Created by Nikita Ermolenko on 26/12/2017. @@ -7,7 +7,27 @@ import XCTest -class ContainerInstallerTests: BaseTest { +class ContainerTests: BaseTest { + + func testContainerConfiguration() { + let content1 = UIView() + var count = 0 + + @discardableResult + func configuration() -> UIView { + return [content1].container(in: mainView, configuration: { container in + count += 1 + container.backgroundColor = .red + }, installerBlock: {}) + } + + configuration() + configuration() + let container = configuration() + + XCTAssertEqual(container.backgroundColor, UIColor.red) + XCTAssertEqual(count, 1) + } func testContainerConfigurationFromTopToBottom() { let content1 = UIView() From 2f87a302c8d80f43aa9b589f2532d105e01cf86f Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Wed, 31 Jan 2018 14:27:15 +0600 Subject: [PATCH 4/9] Add the width and height parameters to the container method + additional method for an existing container --- .../FramezillaExample/ViewController.swift | 122 ++++++++++++------ Sources/UIView+Installer.swift | 59 ++++++--- Tests/ContainerTests.swift | 96 +++++++++++--- 3 files changed, 202 insertions(+), 75 deletions(-) diff --git a/Example/FramezillaExample/ViewController.swift b/Example/FramezillaExample/ViewController.swift index 9548e1f..cdee2b3 100644 --- a/Example/FramezillaExample/ViewController.swift +++ b/Example/FramezillaExample/ViewController.swift @@ -11,101 +11,147 @@ import Framezilla class ViewController: UIViewController { + let container = UIView() + let content1 = UIView() let content2 = UIView() let content3 = UIView() let content4 = UIView() + let label1 = UILabel() + let label2 = UILabel() + let label3 = UILabel() + override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white + container.backgroundColor = .yellow + content1.backgroundColor = .red content2.backgroundColor = .black content3.backgroundColor = .green content4.backgroundColor = .gray + + label1.backgroundColor = .red + label2.backgroundColor = .green + label3.backgroundColor = .gray + + label1.numberOfLines = 0 + label2.numberOfLines = 0 + label3.numberOfLines = 0 + + label1.text = "Helloe Helloe Helloe Helloe Helloe Helloe Helloe Helloe Helloe Helloe Helloe" + label2.text = "Helloe Helloe Helloe Helloe Helloe" + label3.text = "Helloe" + + view.addSubview(container) } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() -// let container = [content1, content2, content3, content4].container(in: view) { +// [content1, label1, label2, label3].configure(container: container, width: 200) { +// content1.configureFrame { maker in +// maker.top(inset: 10) +// maker.size(width: 100, height: 60) +// maker.centerX() +// } +// +// label1.configureFrame { maker in +// maker.left().right().top(to: content1.nui_bottom, inset: 10) +// maker.heightToFit() +// } +// +// label2.configureFrame { maker in +// maker.left().right().top(to: label1.nui_bottom, inset: 10) +// maker.heightToFit() +// } +// +// label3.configureFrame { maker in +// maker.left().right().top(to: label2.nui_bottom, inset: 20) +// maker.heightToFit() +// } +// } + + +// [content1, content2, content3, content4].configure(container: container, width: 200) { // content1.configureFrame { maker in +// maker.top(inset: 10) +// maker.size(width: 100, height: 60) // maker.centerX() -// maker.top() -// maker.size(width: 50, height: 50) // } // // content2.configureFrame { maker in -// maker.top(to: content1.nui_bottom, inset: 5) -// maker.left() -// maker.size(width: 80, height: 80) +// maker.left().right().top(to: content1.nui_bottom, inset: 10) +// maker.height(50) // } // // content3.configureFrame { maker in -// maker.top(to: content1.nui_bottom, inset: 15) -// maker.left(to: content2.nui_right, inset: 5) -// maker.size(width: 80, height: 80) +// maker.left().right().top(to: content2.nui_bottom, inset: 10) +// maker.height(70) // } // // content4.configureFrame { maker in -// maker.top(to: content3.nui_bottom, inset: 5) -// maker.right() -// maker.size(width: 20, height: 20) +// maker.top(to: content3.nui_bottom, inset: 20) +// maker.size(width: 30, height: 30) +// maker.centerX() // } // } -// -// print("") - - let container = [content1, content2, content3].container(in: view, configuration: { container in - container.backgroundColor = .yellow - }, installerBlock: { + let container = [content1, content2, content3, content4].container(in: view, height: 200) { content1.configureFrame { maker in - maker.left() + maker.left(inset: 10) + maker.size(width: 60, height: 100) maker.centerY() - maker.size(width: 50, height: 50) } content2.configureFrame { maker in - maker.left(to: content1.nui_right, inset: 5) - maker.centerY() - maker.size(width: 30, height: 140) + maker.top().bottom().left(to: content1.nui_right, inset: 10) + maker.width(50) } content3.configureFrame { maker in - maker.left(to: content2.nui_right, inset: 15) + maker.top().bottom().left(to: content2.nui_right, inset: 10) + maker.width(70) + } + + content4.configureFrame { maker in + maker.left(to: content3.nui_right, inset: 20) + maker.size(width: 30, height: 30) maker.centerY() - maker.size(width: 80, height: 80) } - }) + } print("") -// let container = [content1, content2, content3].container(in: view) { +// let container = [content1, label1, label2, label3].container(in: view, width: 100) { // content1.configureFrame { maker in -// maker.bottom() +// maker.top(inset: 10) +// maker.size(width: 100, height: 60) // maker.centerX() -// maker.size(width: 50, height: 50) // } // -// content2.configureFrame { maker in -// maker.bottom(to: content1.nui_top, inset: 5) -// maker.centerX() -// maker.size(width: 30, height: 140) +// label1.configureFrame { maker in +// maker.left().right().top(to: content1.nui_bottom, inset: 10) +// maker.heightToFit() // } // -// content3.configureFrame { maker in -// maker.left() -// maker.bottom(to: content2.nui_top, inset: 15) -// maker.size(width: 10, height: 10) +// label2.configureFrame { maker in +// maker.left().right().top(to: label1.nui_bottom, inset: 10) +// maker.heightToFit() +// } +// +// label3.configureFrame { maker in +// maker.left().right().top(to: label2.nui_bottom, inset: 20) +// maker.heightToFit() // } // } - + container.backgroundColor = .yellow container.configureFrame { maker in maker.center() } diff --git a/Sources/UIView+Installer.swift b/Sources/UIView+Installer.swift index 6fb337a..19e4183 100644 --- a/Sources/UIView+Installer.swift +++ b/Sources/UIView+Installer.swift @@ -117,38 +117,65 @@ public extension Sequence where Iterator.Element: UIView { public extension Collection where Iterator.Element: UIView, Self.Index == Int, Self.IndexDistance == Int { - /// Creates сontainer view and configures all subview within this container. + /// Configures all subview within a passed container. /// - /// Use this method when you want to set `width` and `height` by wrapping all subviews. + /// Use this method when you want to calculate width and height by wrapping all subviews. Or use static parameters. /// - /// - note: It atomatically adds all subviews to the container. Don't add subviews manually. - /// - note: Also important to understand, that it's not correct to call 'left' and 'right' relations together by subview, because - /// `container` sets width relatively width of subview and here is some ambiguous. + /// - note: It automatically adds all subviews to the container. Don't add subviews manually. + /// - note: If you don't use a static width for instance, important to understand, that it's not correct to call 'left' and 'right' relations together by subviews, + /// because `container` sets width relatively width of subview and here is some ambiguous. /// /// - parameter view: The view where a container will be added. + /// - parameter width: The width of a container. If you specify a width only a dynamic height will be calculated. + /// - parameter height: The height of a container. If you specify a height only a dynamic width will be calculated. + /// - parameter installerBlock: The installer block within which you should configure frames for all subviews. + + public func configure(container: UIView, width: Number? = nil, height: Number? = nil, installerBlock: () -> Void) { + if let width = width?.value { + container.frame.size.width = width + } + + if let height = height?.value { + container.frame.size.height = height + } + + for subview in self { + container.addSubview(subview) + } + + installerBlock() + container.configureFrame { maker in + maker.container() + } + installerBlock() + } + + /// Creates a сontainer view and configures all subview within this container. + /// + /// Use this method when you want to calculate `width` and `height` by wrapping all subviews. Or use static parameters. + /// + /// - note: It automatically adds all subviews to the container. Don't add subviews manually. A generated container is automatically added to a `view` as well. + /// - note: If you don't use a static width for instance, important to understand, that it's not correct to call 'left' and 'right' relations together by subviews, + /// because `container` sets width relatively width of subview and here is some ambiguous. + /// + /// - parameter view: The view where a container will be added. + /// - parameter width: The width of a container. If you specify a width only a dynamic height will be calculated. + /// - parameter height: The height of a container. If you specify a height only a dynamic width will be calculated. /// - parameter installerBlock: The installer block within which you should configure frames for all subviews. /// /// - returns: Container view. - public func container(in view: UIView, configuration: ((UIView) -> Void)? = nil, installerBlock: () -> Void) -> UIView { + public func container(in view: UIView, width: Number? = nil, height: Number? = nil, installerBlock: () -> Void) -> UIView { let container: UIView if let superView = self.first?.superview { container = superView } else { container = UIView() - configuration?(container) - } - for view in self { - container.addSubview(view) } + view.addSubview(container) - - installerBlock() - container.configureFrame { maker in - maker.container() - } - installerBlock() + configure(container: container, width: width, height: height, installerBlock: installerBlock) return container } } diff --git a/Tests/ContainerTests.swift b/Tests/ContainerTests.swift index a034fa6..037fd76 100644 --- a/Tests/ContainerTests.swift +++ b/Tests/ContainerTests.swift @@ -9,26 +9,6 @@ import XCTest class ContainerTests: BaseTest { - func testContainerConfiguration() { - let content1 = UIView() - var count = 0 - - @discardableResult - func configuration() -> UIView { - return [content1].container(in: mainView, configuration: { container in - count += 1 - container.backgroundColor = .red - }, installerBlock: {}) - } - - configuration() - configuration() - let container = configuration() - - XCTAssertEqual(container.backgroundColor, UIColor.red) - XCTAssertEqual(count, 1) - } - func testContainerConfigurationFromTopToBottom() { let content1 = UIView() let content2 = UIView() @@ -68,7 +48,7 @@ class ContainerTests: BaseTest { XCTAssertEqual(content4.frame, CGRect(x: 145, y: 150, width: 20, height: 20)) } - func testContainerConfigurationFromRightToLeft() { + func testContainerConfigurationFromLeftToRight() { let content1 = UIView() let content2 = UIView() let content3 = UIView() @@ -143,4 +123,78 @@ class ContainerTests: BaseTest { XCTAssertEqual(content1.frame, CGRect(x: 220, y: 35, width: 50, height: 180)) XCTAssertEqual(content2.frame, CGRect(x: 10, y: 0, width: 200, height: 250)) } + + func testContainerConfigurationWithStaticWidth() { + let content1 = UIView() + let content2 = UIView() + let content3 = UIView() + let content4 = UIView() + + let container = [content1, content2, content3, content4].container(in: mainView, width: 200) { + content1.configureFrame { maker in + maker.top(inset: 10) + maker.size(width: 100, height: 60) + maker.centerX() + } + + content2.configureFrame { maker in + maker.left().right().top(to: content1.nui_bottom, inset: 10) + maker.height(50) + } + + content3.configureFrame { maker in + maker.left().right().top(to: content2.nui_bottom, inset: 10) + maker.height(70) + } + + content4.configureFrame { maker in + maker.top(to: content3.nui_bottom, inset: 20) + maker.size(width: 30, height: 30) + maker.centerX() + } + } + + XCTAssertEqual(container.frame, CGRect(x: 0, y: 0, width: 200, height: 260)) + XCTAssertEqual(content1.frame, CGRect(x: 50, y: 10, width: 100, height: 60)) + XCTAssertEqual(content2.frame, CGRect(x: 0, y: 80, width: 200, height: 50)) + XCTAssertEqual(content3.frame, CGRect(x: 0, y: 140, width: 200, height: 70)) + XCTAssertEqual(content4.frame, CGRect(x: 85, y: 230, width: 30, height: 30)) + } + + func testContainerConfigurationWithStaticHeight() { + let content1 = UIView() + let content2 = UIView() + let content3 = UIView() + let content4 = UIView() + + let container = [content1, content2, content3, content4].container(in: mainView, height: 200) { + content1.configureFrame { maker in + maker.left(inset: 10) + maker.size(width: 60, height: 100) + maker.centerY() + } + + content2.configureFrame { maker in + maker.top().bottom().left(to: content1.nui_right, inset: 10) + maker.width(50) + } + + content3.configureFrame { maker in + maker.top().bottom().left(to: content2.nui_right, inset: 10) + maker.width(70) + } + + content4.configureFrame { maker in + maker.left(to: content3.nui_right, inset: 20) + maker.size(width: 30, height: 30) + maker.centerY() + } + } + + XCTAssertEqual(container.frame, CGRect(x: 0, y: 0, width: 260, height: 200)) + XCTAssertEqual(content1.frame, CGRect(x: 10, y: 50, width: 60, height: 100)) + XCTAssertEqual(content2.frame, CGRect(x: 80, y: 0, width: 50, height: 200)) + XCTAssertEqual(content3.frame, CGRect(x: 140, y: 0, width: 70, height: 200)) + XCTAssertEqual(content4.frame, CGRect(x: 230, y: 85, width: 30, height: 30)) + } } From bbed46b86cc60aef044edd736622f83ecd3b9c6a Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Wed, 31 Jan 2018 15:34:10 +0600 Subject: [PATCH 5/9] Test padding logic --- .../FramezillaExample/ViewController.swift | 23 ++++---- Sources/UIView+Installer.swift | 54 +++++++++++++++++-- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/Example/FramezillaExample/ViewController.swift b/Example/FramezillaExample/ViewController.swift index cdee2b3..f9f82a6 100644 --- a/Example/FramezillaExample/ViewController.swift +++ b/Example/FramezillaExample/ViewController.swift @@ -101,27 +101,28 @@ class ViewController: UIViewController { // } - let container = [content1, content2, content3, content4].container(in: view, height: 200) { + let container = [content1, content2, content3, content4].container(in: view, insets: UIEdgeInsets(top: 5, left: 20, bottom: 8, right: 4)) { content1.configureFrame { maker in - maker.left(inset: 10) - maker.size(width: 60, height: 100) - maker.centerY() + maker.top() + maker.size(width: 150, height: 50) + maker.centerX() } content2.configureFrame { maker in - maker.top().bottom().left(to: content1.nui_right, inset: 10) - maker.width(50) + maker.top(to: content1.nui_bottom, inset: 10) + maker.size(width: 50, height: 50) + maker.centerX() } content3.configureFrame { maker in - maker.top().bottom().left(to: content2.nui_right, inset: 10) - maker.width(70) + maker.left().top(to: content2.nui_bottom, inset: 5) + maker.size(width: 20, height: 20) } content4.configureFrame { maker in - maker.left(to: content3.nui_right, inset: 20) - maker.size(width: 30, height: 30) - maker.centerY() + maker.top(to: content3.nui_bottom, inset: 40) + maker.size(width: 60, height: 60) + maker.right() } } diff --git a/Sources/UIView+Installer.swift b/Sources/UIView+Installer.swift index 19e4183..2c7494a 100644 --- a/Sources/UIView+Installer.swift +++ b/Sources/UIView+Installer.swift @@ -123,14 +123,21 @@ public extension Collection where Iterator.Element: UIView, Self.Index == Int, S /// /// - note: It automatically adds all subviews to the container. Don't add subviews manually. /// - note: If you don't use a static width for instance, important to understand, that it's not correct to call 'left' and 'right' relations together by subviews, - /// because `container` sets width relatively width of subview and here is some ambiguous. + /// because `container` sets width relatively width of subviews and here is some ambiguous. /// /// - parameter view: The view where a container will be added. /// - parameter width: The width of a container. If you specify a width only a dynamic height will be calculated. /// - parameter height: The height of a container. If you specify a height only a dynamic width will be calculated. + /// - parameter padding: The padding of a container. /// - parameter installerBlock: The installer block within which you should configure frames for all subviews. - public func configure(container: UIView, width: Number? = nil, height: Number? = nil, installerBlock: () -> Void) { + public func configure(container: UIView, + width: Number? = nil, + height: Number? = nil, + padding: UIEdgeInsets = .zero, + installerBlock: () -> Void) { + container.frame = .zero + if let width = width?.value { container.frame.size.width = width } @@ -148,6 +155,38 @@ public extension Collection where Iterator.Element: UIView, Self.Index == Int, S maker.container() } installerBlock() + + if padding != .zero { + let containerCenterX = container.frame.width / 2.0 + let containerCenterY = container.frame.height / 2.0 + + let subviewsWithCenterXRelation = self.filter { subview in + subview.center.x == containerCenterX + } + + let subviewsWithCenterYRelation = self.filter { subview in + subview.center.x == containerCenterY + } + + container.frame.size.width += padding.left + padding.right + container.frame.size.height += padding.top + padding.bottom + + for subview in self { + subview.frame.origin.x += padding.left + subview.frame.origin.y += padding.top + } + + let centerX = container.frame.width / 2.0 + let centerY = container.frame.height / 2.0 + + for subview in subviewsWithCenterXRelation { + subview.center.x = centerX + } + + for subview in subviewsWithCenterYRelation { + subview.center.y = centerY + } + } } /// Creates a сontainer view and configures all subview within this container. @@ -156,16 +195,21 @@ public extension Collection where Iterator.Element: UIView, Self.Index == Int, S /// /// - note: It automatically adds all subviews to the container. Don't add subviews manually. A generated container is automatically added to a `view` as well. /// - note: If you don't use a static width for instance, important to understand, that it's not correct to call 'left' and 'right' relations together by subviews, - /// because `container` sets width relatively width of subview and here is some ambiguous. + /// because `container` sets width relatively width of subviews and here is some ambiguous. /// /// - parameter view: The view where a container will be added. /// - parameter width: The width of a container. If you specify a width only a dynamic height will be calculated. /// - parameter height: The height of a container. If you specify a height only a dynamic width will be calculated. + /// - parameter padding: The padding of a container. /// - parameter installerBlock: The installer block within which you should configure frames for all subviews. /// /// - returns: Container view. - public func container(in view: UIView, width: Number? = nil, height: Number? = nil, installerBlock: () -> Void) -> UIView { + public func container(in view: UIView, + width: Number? = nil, + height: Number? = nil, + padding: UIEdgeInsets = .zero, + installerBlock: () -> Void) -> UIView { let container: UIView if let superView = self.first?.superview { container = superView @@ -175,7 +219,7 @@ public extension Collection where Iterator.Element: UIView, Self.Index == Int, S } view.addSubview(container) - configure(container: container, width: width, height: height, installerBlock: installerBlock) + configure(container: container, width: width, height: height, padding: padding, installerBlock: installerBlock) return container } } From f574b3da568a16c8ae1ac5842c46a49bcf682c23 Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Fri, 2 Feb 2018 10:59:12 +0600 Subject: [PATCH 6/9] Add additional container relations - left / right and top / bottom --- .../FramezillaExample/ViewController.swift | 2 +- Sources/Maker.swift | 2 +- Sources/UIView+Installer.swift | 70 +++++++++++----- Tests/ContainerTests.swift | 79 ++++++++++++++++++- 4 files changed, 128 insertions(+), 25 deletions(-) diff --git a/Example/FramezillaExample/ViewController.swift b/Example/FramezillaExample/ViewController.swift index f9f82a6..4b4d1c4 100644 --- a/Example/FramezillaExample/ViewController.swift +++ b/Example/FramezillaExample/ViewController.swift @@ -101,7 +101,7 @@ class ViewController: UIViewController { // } - let container = [content1, content2, content3, content4].container(in: view, insets: UIEdgeInsets(top: 5, left: 20, bottom: 8, right: 4)) { + let container = [content1, content2, content3, content4].container(in: view, r) { content1.configureFrame { maker in maker.top() maker.size(width: 150, height: 50) diff --git a/Sources/Maker.swift b/Sources/Maker.swift index b3bf472..e58ea84 100644 --- a/Sources/Maker.swift +++ b/Sources/Maker.swift @@ -670,7 +670,7 @@ public final class Maker { /// Use this method when you want to join right side of current view with some horizontal side of another view. /// /// - note: You can not use this method with other relations except for `nui_left`, `nui_centerX` and `nui_right`. - // + /// /// - parameter relationView: The view on which you set right relation. /// - parameter inset: The inset for additional space between views. Default value: 0. /// diff --git a/Sources/UIView+Installer.swift b/Sources/UIView+Installer.swift index 2c7494a..771cae4 100644 --- a/Sources/UIView+Installer.swift +++ b/Sources/UIView+Installer.swift @@ -115,6 +115,13 @@ public extension Sequence where Iterator.Element: UIView { } } +public enum ContainerRelation { + case width(Number) + case height(Number) + case horizontal(left: Number, right: Number) + case vertical(top: Number, bottom: Number) +} + public extension Collection where Iterator.Element: UIView, Self.Index == Int, Self.IndexDistance == Int { /// Configures all subview within a passed container. @@ -125,25 +132,23 @@ public extension Collection where Iterator.Element: UIView, Self.Index == Int, S /// - note: If you don't use a static width for instance, important to understand, that it's not correct to call 'left' and 'right' relations together by subviews, /// because `container` sets width relatively width of subviews and here is some ambiguous. /// - /// - parameter view: The view where a container will be added. - /// - parameter width: The width of a container. If you specify a width only a dynamic height will be calculated. - /// - parameter height: The height of a container. If you specify a height only a dynamic width will be calculated. - /// - parameter padding: The padding of a container. - /// - parameter installerBlock: The installer block within which you should configure frames for all subviews. + /// - parameter view: The view where a container will be added. + /// - parameter relation: The relation of `ContainerRelation` type. + /// - `width`: The width of a container. If you specify a width only a dynamic height will be calculated. + /// - `height`: The height of a container. If you specify a height only a dynamic width will be calculated. + /// - `horizontal(left, right)`: The left and right insets of a container. If you specify these parameters only a dynamic height will be calculated. + /// - `vertical(top, bottom)`: The top and bototm insets of a container. If you specify these parameters only a dynamic width will be calculated. + /// - parameter padding: The padding of a container. + /// - parameter installerBlock: The installer block within which you should configure frames for all subviews. public func configure(container: UIView, - width: Number? = nil, - height: Number? = nil, + relation: ContainerRelation? = nil, padding: UIEdgeInsets = .zero, installerBlock: () -> Void) { container.frame = .zero - if let width = width?.value { - container.frame.size.width = width - } - - if let height = height?.value { - container.frame.size.height = height + if let relation = relation { + configure(container: container, for: relation) } for subview in self { @@ -189,6 +194,27 @@ public extension Collection where Iterator.Element: UIView, Self.Index == Int, S } } + private func configure(container: UIView, for relation: ContainerRelation) { + switch relation { + case let .width(width): container.frame.size.width = width.value + case let .height(height): container.frame.size.height = height.value + case let .horizontal(lInset, rInset): + container.configureFrame { maker in + maker.left(inset: lInset).right(inset: rInset) + } + let width = container.frame.width + container.frame = .zero + container.frame.size.width = width + case let .vertical(tInset, bInset): + container.configureFrame { maker in + maker.top(inset: tInset).bottom(inset: bInset) + } + let height = container.frame.height + container.frame = .zero + container.frame.size.height = height + } + } + /// Creates a сontainer view and configures all subview within this container. /// /// Use this method when you want to calculate `width` and `height` by wrapping all subviews. Or use static parameters. @@ -197,17 +223,19 @@ public extension Collection where Iterator.Element: UIView, Self.Index == Int, S /// - note: If you don't use a static width for instance, important to understand, that it's not correct to call 'left' and 'right' relations together by subviews, /// because `container` sets width relatively width of subviews and here is some ambiguous. /// - /// - parameter view: The view where a container will be added. - /// - parameter width: The width of a container. If you specify a width only a dynamic height will be calculated. - /// - parameter height: The height of a container. If you specify a height only a dynamic width will be calculated. - /// - parameter padding: The padding of a container. - /// - parameter installerBlock: The installer block within which you should configure frames for all subviews. + /// - parameter view: The view where a container will be added. + /// - parameter relation: The relation of `ContainerRelation` type. + /// - `width`: The width of a container. If you specify a width only a dynamic height will be calculated. + /// - `height`: The height of a container. If you specify a height only a dynamic width will be calculated. + /// - `horizontal(left, right)`: The left and right insets of a container. If you specify these parameters only a dynamic height will be calculated. + /// - `vertical(top, bottom)`: The top and bototm insets of a container. If you specify these parameters only a dynamic width will be calculated. + /// - parameter padding: The padding of a container. + /// - parameter installerBlock: The installer block within which you should configure frames for all subviews. /// /// - returns: Container view. public func container(in view: UIView, - width: Number? = nil, - height: Number? = nil, + relation: ContainerRelation? = nil, padding: UIEdgeInsets = .zero, installerBlock: () -> Void) -> UIView { let container: UIView @@ -219,7 +247,7 @@ public extension Collection where Iterator.Element: UIView, Self.Index == Int, S } view.addSubview(container) - configure(container: container, width: width, height: height, padding: padding, installerBlock: installerBlock) + configure(container: container, relation: relation, padding: padding, installerBlock: installerBlock) return container } } diff --git a/Tests/ContainerTests.swift b/Tests/ContainerTests.swift index 037fd76..0916a2d 100644 --- a/Tests/ContainerTests.swift +++ b/Tests/ContainerTests.swift @@ -6,6 +6,7 @@ // import XCTest +import Framezilla class ContainerTests: BaseTest { @@ -130,7 +131,7 @@ class ContainerTests: BaseTest { let content3 = UIView() let content4 = UIView() - let container = [content1, content2, content3, content4].container(in: mainView, width: 200) { + let container = [content1, content2, content3, content4].container(in: mainView, relation: .width(200)) { content1.configureFrame { maker in maker.top(inset: 10) maker.size(width: 100, height: 60) @@ -167,7 +168,81 @@ class ContainerTests: BaseTest { let content3 = UIView() let content4 = UIView() - let container = [content1, content2, content3, content4].container(in: mainView, height: 200) { + let container = [content1, content2, content3, content4].container(in: mainView, relation: .height(200)) { + content1.configureFrame { maker in + maker.left(inset: 10) + maker.size(width: 60, height: 100) + maker.centerY() + } + + content2.configureFrame { maker in + maker.top().bottom().left(to: content1.nui_right, inset: 10) + maker.width(50) + } + + content3.configureFrame { maker in + maker.top().bottom().left(to: content2.nui_right, inset: 10) + maker.width(70) + } + + content4.configureFrame { maker in + maker.left(to: content3.nui_right, inset: 20) + maker.size(width: 30, height: 30) + maker.centerY() + } + } + + XCTAssertEqual(container.frame, CGRect(x: 0, y: 0, width: 260, height: 200)) + XCTAssertEqual(content1.frame, CGRect(x: 10, y: 50, width: 60, height: 100)) + XCTAssertEqual(content2.frame, CGRect(x: 80, y: 0, width: 50, height: 200)) + XCTAssertEqual(content3.frame, CGRect(x: 140, y: 0, width: 70, height: 200)) + XCTAssertEqual(content4.frame, CGRect(x: 230, y: 85, width: 30, height: 30)) + } + + func testContainerConfigurationWithLeftAndRightRelations() { + let content1 = UIView() + let content2 = UIView() + let content3 = UIView() + let content4 = UIView() + + let container = [content1, content2, content3, content4].container(in: mainView, relation: .horizontal(left: 150, right: 150)) { + content1.configureFrame { maker in + maker.top(inset: 10) + maker.size(width: 100, height: 60) + maker.centerX() + } + + content2.configureFrame { maker in + maker.left().right().top(to: content1.nui_bottom, inset: 10) + maker.height(50) + } + + content3.configureFrame { maker in + maker.left().right().top(to: content2.nui_bottom, inset: 10) + maker.height(70) + } + + content4.configureFrame { maker in + maker.top(to: content3.nui_bottom, inset: 20) + maker.size(width: 30, height: 30) + maker.centerX() + } + } + + XCTAssertEqual(container.frame, CGRect(x: 0, y: 0, width: 200, height: 260)) + XCTAssertEqual(content1.frame, CGRect(x: 50, y: 10, width: 100, height: 60)) + XCTAssertEqual(content2.frame, CGRect(x: 0, y: 80, width: 200, height: 50)) + XCTAssertEqual(content3.frame, CGRect(x: 0, y: 140, width: 200, height: 70)) + XCTAssertEqual(content4.frame, CGRect(x: 85, y: 230, width: 30, height: 30)) + } + + func testContainerConfigurationWithTopAndBottomRelations() { + let content1 = UIView() + let content2 = UIView() + let content3 = UIView() + let content4 = UIView() + + let container = [content1, content2, content3, content4].container(in: mainView, relation: .vertical(top: 150, bottom: 150)) { content1.configureFrame { maker in maker.left(inset: 10) maker.size(width: 60, height: 100) From 3ebb644dce49354fa07f6b12d1637a7323ae49b3 Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Fri, 2 Feb 2018 14:55:02 +0600 Subject: [PATCH 7/9] Remove the padding feature :( --- Sources/UIView+Installer.swift | 86 +++++++++------------------------- 1 file changed, 21 insertions(+), 65 deletions(-) diff --git a/Sources/UIView+Installer.swift b/Sources/UIView+Installer.swift index 771cae4..383e90a 100644 --- a/Sources/UIView+Installer.swift +++ b/Sources/UIView+Installer.swift @@ -138,17 +138,30 @@ public extension Collection where Iterator.Element: UIView, Self.Index == Int, S /// - `height`: The height of a container. If you specify a height only a dynamic width will be calculated. /// - `horizontal(left, right)`: The left and right insets of a container. If you specify these parameters only a dynamic height will be calculated. /// - `vertical(top, bottom)`: The top and bototm insets of a container. If you specify these parameters only a dynamic width will be calculated. - /// - parameter padding: The padding of a container. /// - parameter installerBlock: The installer block within which you should configure frames for all subviews. - public func configure(container: UIView, - relation: ContainerRelation? = nil, - padding: UIEdgeInsets = .zero, - installerBlock: () -> Void) { + public func configure(container: UIView, relation: ContainerRelation? = nil, installerBlock: () -> Void) { container.frame = .zero if let relation = relation { - configure(container: container, for: relation) + switch relation { + case let .width(width): container.frame.size.width = width.value + case let .height(height): container.frame.size.height = height.value + case let .horizontal(lInset, rInset): + container.configureFrame { maker in + maker.left(inset: lInset).right(inset: rInset) + } + let width = container.frame.width + container.frame = .zero + container.frame.size.width = width + case let .vertical(tInset, bInset): + container.configureFrame { maker in + maker.top(inset: tInset).bottom(inset: bInset) + } + let height = container.frame.height + container.frame = .zero + container.frame.size.height = height + } } for subview in self { @@ -160,59 +173,6 @@ public extension Collection where Iterator.Element: UIView, Self.Index == Int, S maker.container() } installerBlock() - - if padding != .zero { - let containerCenterX = container.frame.width / 2.0 - let containerCenterY = container.frame.height / 2.0 - - let subviewsWithCenterXRelation = self.filter { subview in - subview.center.x == containerCenterX - } - - let subviewsWithCenterYRelation = self.filter { subview in - subview.center.x == containerCenterY - } - - container.frame.size.width += padding.left + padding.right - container.frame.size.height += padding.top + padding.bottom - - for subview in self { - subview.frame.origin.x += padding.left - subview.frame.origin.y += padding.top - } - - let centerX = container.frame.width / 2.0 - let centerY = container.frame.height / 2.0 - - for subview in subviewsWithCenterXRelation { - subview.center.x = centerX - } - - for subview in subviewsWithCenterYRelation { - subview.center.y = centerY - } - } - } - - private func configure(container: UIView, for relation: ContainerRelation) { - switch relation { - case let .width(width): container.frame.size.width = width.value - case let .height(height): container.frame.size.height = height.value - case let .horizontal(lInset, rInset): - container.configureFrame { maker in - maker.left(inset: lInset).right(inset: rInset) - } - let width = container.frame.width - container.frame = .zero - container.frame.size.width = width - case let .vertical(tInset, bInset): - container.configureFrame { maker in - maker.top(inset: tInset).bottom(inset: bInset) - } - let height = container.frame.height - container.frame = .zero - container.frame.size.height = height - } } /// Creates a сontainer view and configures all subview within this container. @@ -229,15 +189,11 @@ public extension Collection where Iterator.Element: UIView, Self.Index == Int, S /// - `height`: The height of a container. If you specify a height only a dynamic width will be calculated. /// - `horizontal(left, right)`: The left and right insets of a container. If you specify these parameters only a dynamic height will be calculated. /// - `vertical(top, bottom)`: The top and bototm insets of a container. If you specify these parameters only a dynamic width will be calculated. - /// - parameter padding: The padding of a container. /// - parameter installerBlock: The installer block within which you should configure frames for all subviews. /// /// - returns: Container view. - public func container(in view: UIView, - relation: ContainerRelation? = nil, - padding: UIEdgeInsets = .zero, - installerBlock: () -> Void) -> UIView { + public func container(in view: UIView, relation: ContainerRelation? = nil, installerBlock: () -> Void) -> UIView { let container: UIView if let superView = self.first?.superview { container = superView @@ -247,7 +203,7 @@ public extension Collection where Iterator.Element: UIView, Self.Index == Int, S } view.addSubview(container) - configure(container: container, relation: relation, padding: padding, installerBlock: installerBlock) + configure(container: container, relation: relation, installerBlock: installerBlock) return container } } From 16aac108f5a9a8a96311b49e199aa2a1d3e7c103 Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Fri, 2 Feb 2018 15:02:42 +0600 Subject: [PATCH 8/9] Update container example --- .../FramezillaExample/ViewController.swift | 105 +++--------------- 1 file changed, 14 insertions(+), 91 deletions(-) diff --git a/Example/FramezillaExample/ViewController.swift b/Example/FramezillaExample/ViewController.swift index 4b4d1c4..5bb0977 100644 --- a/Example/FramezillaExample/ViewController.swift +++ b/Example/FramezillaExample/ViewController.swift @@ -52,109 +52,32 @@ class ViewController: UIViewController { override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() -// [content1, label1, label2, label3].configure(container: container, width: 200) { -// content1.configureFrame { maker in -// maker.top(inset: 10) -// maker.size(width: 100, height: 60) -// maker.centerX() -// } -// -// label1.configureFrame { maker in -// maker.left().right().top(to: content1.nui_bottom, inset: 10) -// maker.heightToFit() -// } -// -// label2.configureFrame { maker in -// maker.left().right().top(to: label1.nui_bottom, inset: 10) -// maker.heightToFit() -// } -// -// label3.configureFrame { maker in -// maker.left().right().top(to: label2.nui_bottom, inset: 20) -// maker.heightToFit() -// } -// } - - -// [content1, content2, content3, content4].configure(container: container, width: 200) { -// content1.configureFrame { maker in -// maker.top(inset: 10) -// maker.size(width: 100, height: 60) -// maker.centerX() -// } -// -// content2.configureFrame { maker in -// maker.left().right().top(to: content1.nui_bottom, inset: 10) -// maker.height(50) -// } -// -// content3.configureFrame { maker in -// maker.left().right().top(to: content2.nui_bottom, inset: 10) -// maker.height(70) -// } -// -// content4.configureFrame { maker in -// maker.top(to: content3.nui_bottom, inset: 20) -// maker.size(width: 30, height: 30) -// maker.centerX() -// } -// } - - - let container = [content1, content2, content3, content4].container(in: view, r) { + [content1, label1, label2, label3].configure(container: container, relation: .horizontal(left: 20, right: 20)) { content1.configureFrame { maker in - maker.top() - maker.size(width: 150, height: 50) + maker.top(inset: 10) + maker.size(width: 100, height: 60) maker.centerX() } - content2.configureFrame { maker in - maker.top(to: content1.nui_bottom, inset: 10) - maker.size(width: 50, height: 50) - maker.centerX() + label1.configureFrame { maker in + maker.left().right().top(to: content1.nui_bottom, inset: 10) + maker.heightToFit() } - content3.configureFrame { maker in - maker.left().top(to: content2.nui_bottom, inset: 5) - maker.size(width: 20, height: 20) + label2.configureFrame { maker in + maker.left().right().top(to: label1.nui_bottom, inset: 10) + maker.heightToFit() } - content4.configureFrame { maker in - maker.top(to: content3.nui_bottom, inset: 40) - maker.size(width: 60, height: 60) - maker.right() + label3.configureFrame { maker in + maker.left().right().top(to: label2.nui_bottom, inset: 20) + maker.heightToFit() } } - print("") - - -// let container = [content1, label1, label2, label3].container(in: view, width: 100) { -// content1.configureFrame { maker in -// maker.top(inset: 10) -// maker.size(width: 100, height: 60) -// maker.centerX() -// } -// -// label1.configureFrame { maker in -// maker.left().right().top(to: content1.nui_bottom, inset: 10) -// maker.heightToFit() -// } -// -// label2.configureFrame { maker in -// maker.left().right().top(to: label1.nui_bottom, inset: 10) -// maker.heightToFit() -// } -// -// label3.configureFrame { maker in -// maker.left().right().top(to: label2.nui_bottom, inset: 20) -// maker.heightToFit() -// } -// } - - container.backgroundColor = .yellow container.configureFrame { maker in - maker.center() + maker.centerX() + maker.bottom(inset: 20) } } } From 7bd6ec41692dc365440f257e0516727dabe44a63 Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Fri, 2 Feb 2018 15:26:46 +0600 Subject: [PATCH 9/9] Update readme --- README.md | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 40c2843..73eeed2 100644 --- a/README.md +++ b/README.md @@ -222,18 +222,31 @@ label.configureFrame { maker in ## Container -Use this method when you want to set `width` and `height` by wrapping all subviews. +Use this method when you want to calculate a `width` and `height` by wrapping all subviews. + +You can also specify a special container relation: + +```swift +public enum ContainerRelation { + case width(Number) + case height(Number) + case horizontal(left: Number, right: Number) + case vertical(top: Number, bottom: Number) +} +``` + +For instance, if you set a width for a container, only a dynamic height will be calculated. ### NOTE: **It atomatically adds all subviews to the container. Don't add subviews manually.** -**Also important to understand, that it's not correct to call 'left' and 'right' relations together by subview, because `container` sets width relatively width of subview and here is some ambiguous.** +**If you don't use a static width for instance, important to understand, that it's not correct to call `left` and `right` relations together by subviews, because `container` sets width relatively width of subviews and here is some ambiguous.** ![](img/container.png) ```swift -let container = [content1, content2, content3, content4].container(in: view) { +let container = [content1, content2, content3, content4].container(in: view, relation: /* if needed */) { content1.configureFrame { maker in maker.centerX() maker.top() @@ -265,6 +278,14 @@ container.configureFrame { maker in } ``` +If you have already configured container, then this method will be more convenient for you: + +```swift +[content1, label1, label2, label3].configure(container: container, relation: .horizontal(left: 20, right: 20)) { +// do configuration +} +``` + ## Cool things: Sometimes you want to configure a few views with the same size, for examlple. There is a convinience method: