From 67885efe6a4abbace1fa8f17140d66938dbecbf9 Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Sun, 15 Oct 2017 15:31:10 +0600 Subject: [PATCH 01/14] Remove force unwrap for `superview` - add `assertionFailure` --- Sources/MakerHelper.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/MakerHelper.swift b/Sources/MakerHelper.swift index d851bae..849454a 100644 --- a/Sources/MakerHelper.swift +++ b/Sources/MakerHelper.swift @@ -31,7 +31,12 @@ extension Maker { if let superview = self.view.superview, superview === view, superview.superview == nil { return CGRect(origin: .zero, size: superview.frame.size) } - return self.view.superview!.convert(view.frame, from: view.superview) + + if let supervew = self.view.superview { + return supervew.convert(view.frame, from: view.superview) + } + assertionFailure("Can't configure a frame for view: \(self.view) without superview.") + return .zero } var convertedRect = rect From a10c23c2db4f44fd426dc50889cec580e5df9ea4 Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Sun, 15 Oct 2017 17:34:22 +0600 Subject: [PATCH 02/14] Expand top/left/right/bottom methods for safe area. --- Sources/Maker.swift | 117 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 8 deletions(-) diff --git a/Sources/Maker.swift b/Sources/Maker.swift index a278780..611c0cc 100644 --- a/Sources/Maker.swift +++ b/Sources/Maker.swift @@ -14,6 +14,11 @@ enum HandlerPriority: Int { case low } +public class SafeArea {} +public var nui_safeArea: SafeArea { + return SafeArea() +} + public final class Maker { typealias HandlerType = () -> Void @@ -244,6 +249,30 @@ public final class Maker { return left(to: RelationView(view: superview, relation: .left), inset: inset) } + /// Creates a left relation to safe area. + /// + /// Use this method when you want to join a left side of current view with left edge of safe area. + /// + /// - note: In earlier versions of OS than iOS 11, it creates a left relation to superview. + /// + /// - parameter safeArea: The safe area of current view. Use a `nui_safeArea` global property. + /// - parameter inset: The inset for additional space to safe area. Default value: 0. + /// + /// - returns: `Maker` instance for chaining relations. + + @discardableResult public func left(to safeArea: SafeArea, inset: Number = 0.0) -> Maker { + if #available(iOS 11.0, *) { + guard let superview = view.superview else { + assertionFailure("Can not configure a left relation to safe area without superview.") + return self + } + return left(inset: superview.safeAreaInsets.left + inset.value) + } + else { + return left(inset: inset) + } + } + /// Creates left relation. /// /// Use this method when you want to join left side of current view with some horizontal side of another view. @@ -278,12 +307,36 @@ public final class Maker { @discardableResult public func top(inset: Number = 0.0) -> Maker { guard let superview = view.superview else { - assertionFailure("Can not configure top relation to superview without superview.") + assertionFailure("Can not configure a top relation to superview without superview.") return self } return top(to: RelationView(view: superview, relation: .top), inset: inset.value) } - + + /// Creates a top relation to safe area. + /// + /// Use this method when you want to join a top side of current view with top of safe area. + /// + /// - note: In earlier versions of OS than iOS 11, it creates a right relation to superview. + /// + /// - parameter safeArea: The safe area of current view. Use a `nui_safeArea` global property. + /// - parameter inset: The inset for additional space to safe area. Default value: 0. + /// + /// - returns: `Maker` instance for chaining relations. + + @discardableResult public func top(to safeArea: SafeArea, inset: Number = 0.0) -> Maker { + if #available(iOS 11.0, *) { + guard let superview = view.superview else { + assertionFailure("Can not configure a top relation to safe area without superview.") + return self + } + return top(inset: superview.safeAreaInsets.top + inset.value) + } + else { + return top(inset: inset) + } + } + /// Creates top relation. /// /// Use this method when you want to join top side of current view with some vertical side of another view. @@ -422,11 +475,35 @@ public final class Maker { @discardableResult public func bottom(inset: Number = 0.0) -> Maker { guard let superview = view.superview else { - assertionFailure("Can not configure bottom relation to superview without superview.") + assertionFailure("Can not configure a bottom relation to superview without superview.") return self } return bottom(to: RelationView(view: superview, relation: .bottom), inset: inset) } + + /// Creates a bottom relation to safe area. + /// + /// Use this method when you want to join a bottom side of current view with bottom edge of safe area. + /// + /// - note: In earlier versions of OS than iOS 11, it creates a bottom relation to superview. + /// + /// - parameter safeArea: The safe area of current view. Use a `nui_safeArea` global property. + /// - parameter inset: The inset for additional space to safe area. Default value: 0. + /// + /// - returns: `Maker` instance for chaining relations. + + @discardableResult public func bottom(to safeArea: SafeArea, inset: Number = 0.0) -> Maker { + if #available(iOS 11.0, *) { + guard let superview = view.superview else { + assertionFailure("Can not configure a bottom relation to safe area without superview.") + return self + } + return bottom(inset: superview.safeAreaInsets.bottom + inset.value) + } + else { + return bottom(inset: inset) + } + } /// Creates bottom relation. /// @@ -468,12 +545,36 @@ public final class Maker { @discardableResult public func right(inset: Number = 0.0) -> Maker { guard let superview = view.superview else { - assertionFailure("Can not configure right relation to superview without superview.") + assertionFailure("Can not configure a right relation to superview without superview.") return self } return right(to: RelationView(view: superview, relation: .right), inset: inset.value) } - + + /// Creates a right relation to safe area. + /// + /// Use this method when you want to join a right side of current view with right edge of safe area. + /// + /// - note: In earlier versions of OS than iOS 11, it creates a right relation to superview. + /// + /// - parameter safeArea: The safe area of current view. Use a `nui_safeArea` global property. + /// - parameter inset: The inset for additional space to safe area. Default value: 0. + /// + /// - returns: `Maker` instance for chaining relations. + + @discardableResult public func right(to safeArea: SafeArea, inset: Number = 0.0) -> Maker { + if #available(iOS 11.0, *) { + guard let superview = view.superview else { + assertionFailure("Can not configure a right relation to safe area without superview.") + return self + } + return right(inset: superview.safeAreaInsets.right + inset.value) + } + else { + return right(inset: inset) + } + } + /// Creates right relation. /// /// Use this method when you want to join right side of current view with some horizontal side of another view. @@ -514,7 +615,7 @@ public final class Maker { @discardableResult public func center() -> Maker { guard let superview = view.superview else { - assertionFailure("Can not configure center relation to superview without superview.") + assertionFailure("Can not configure a center relation to superview without superview.") return self } return center(to: superview) @@ -543,7 +644,7 @@ public final class Maker { @discardableResult public func centerY(offset: Number = 0.0) -> Maker { guard let superview = view.superview else { - assertionFailure("Can not configure centerY relation to superview without superview.") + assertionFailure("Can not configure a centerY relation to superview without superview.") return self } return centerY(to: RelationView(view: superview, relation: .centerY), offset: offset.value) @@ -606,7 +707,7 @@ public final class Maker { @discardableResult public func centerX(offset: Number = 0.0) -> Maker { guard let superview = view.superview else { - assertionFailure("Can not configure centerX relation to superview without superview.") + assertionFailure("Can not configure a centerX relation to superview without superview.") return self } return centerX(to: RelationView(view: superview, relation: .centerX), offset: offset.value) From c01dd8f601c08bce328be058eee64026b75a27ec Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Sat, 21 Oct 2017 13:57:31 +0600 Subject: [PATCH 03/14] Change class to struct for SafeArea --- Sources/Maker.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Maker.swift b/Sources/Maker.swift index 611c0cc..fe3b3c2 100644 --- a/Sources/Maker.swift +++ b/Sources/Maker.swift @@ -14,7 +14,7 @@ enum HandlerPriority: Int { case low } -public class SafeArea {} +public struct SafeArea {} public var nui_safeArea: SafeArea { return SafeArea() } From 412827ffbec4a86ee15a091f9d4d897ffd9bf64b Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Sat, 21 Oct 2017 14:07:27 +0600 Subject: [PATCH 04/14] Update documentation for the safe area methods --- Sources/Maker.swift | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Sources/Maker.swift b/Sources/Maker.swift index fe3b3c2..a5c3a51 100644 --- a/Sources/Maker.swift +++ b/Sources/Maker.swift @@ -249,13 +249,13 @@ public final class Maker { return left(to: RelationView(view: superview, relation: .left), inset: inset) } - /// Creates a left relation to safe area. + /// Creates a left relation to the safe area. /// - /// Use this method when you want to join a left side of current view with left edge of safe area. + /// Use this method when you want to join a left side of current view with left edge of the safe area. /// - /// - note: In earlier versions of OS than iOS 11, it creates a left relation to superview. + /// - note: In earlier versions of OS than iOS 11, it creates a left relation to a superview. /// - /// - parameter safeArea: The safe area of current view. Use a `nui_safeArea` global property. + /// - parameter safeArea: The safe area of current view. Use a `nui_safeArea` - global property. /// - parameter inset: The inset for additional space to safe area. Default value: 0. /// /// - returns: `Maker` instance for chaining relations. @@ -263,7 +263,7 @@ public final class Maker { @discardableResult public func left(to safeArea: SafeArea, inset: Number = 0.0) -> Maker { if #available(iOS 11.0, *) { guard let superview = view.superview else { - assertionFailure("Can not configure a left relation to safe area without superview.") + assertionFailure("Can not configure a left relation to the safe area without superview.") return self } return left(inset: superview.safeAreaInsets.left + inset.value) @@ -313,13 +313,13 @@ public final class Maker { return top(to: RelationView(view: superview, relation: .top), inset: inset.value) } - /// Creates a top relation to safe area. + /// Creates a top relation to the safe area. /// - /// Use this method when you want to join a top side of current view with top of safe area. + /// Use this method when you want to join a top side of current view with top edge of the safe area. /// - /// - note: In earlier versions of OS than iOS 11, it creates a right relation to superview. + /// - note: In earlier versions of OS than iOS 11, it creates a top relation to a superview. /// - /// - parameter safeArea: The safe area of current view. Use a `nui_safeArea` global property. + /// - parameter safeArea: The safe area of current view. Use a `nui_safeArea` - global property. /// - parameter inset: The inset for additional space to safe area. Default value: 0. /// /// - returns: `Maker` instance for chaining relations. @@ -327,7 +327,7 @@ public final class Maker { @discardableResult public func top(to safeArea: SafeArea, inset: Number = 0.0) -> Maker { if #available(iOS 11.0, *) { guard let superview = view.superview else { - assertionFailure("Can not configure a top relation to safe area without superview.") + assertionFailure("Can not configure a top relation to the safe area without superview.") return self } return top(inset: superview.safeAreaInsets.top + inset.value) @@ -481,13 +481,13 @@ public final class Maker { return bottom(to: RelationView(view: superview, relation: .bottom), inset: inset) } - /// Creates a bottom relation to safe area. + /// Creates a bottom relation to the safe area. /// - /// Use this method when you want to join a bottom side of current view with bottom edge of safe area. + /// Use this method when you want to join a bottom side of current view with bottom edge of the safe area. /// - /// - note: In earlier versions of OS than iOS 11, it creates a bottom relation to superview. + /// - note: In earlier versions of OS than iOS 11, it creates a bottom relation to a superview. /// - /// - parameter safeArea: The safe area of current view. Use a `nui_safeArea` global property. + /// - parameter safeArea: The safe area of current view. Use a `nui_safeArea` - global property. /// - parameter inset: The inset for additional space to safe area. Default value: 0. /// /// - returns: `Maker` instance for chaining relations. @@ -495,7 +495,7 @@ public final class Maker { @discardableResult public func bottom(to safeArea: SafeArea, inset: Number = 0.0) -> Maker { if #available(iOS 11.0, *) { guard let superview = view.superview else { - assertionFailure("Can not configure a bottom relation to safe area without superview.") + assertionFailure("Can not configure a bottom relation to the safe area without superview.") return self } return bottom(inset: superview.safeAreaInsets.bottom + inset.value) @@ -551,13 +551,13 @@ public final class Maker { return right(to: RelationView(view: superview, relation: .right), inset: inset.value) } - /// Creates a right relation to safe area. + /// Creates a right relation to the safe area. /// - /// Use this method when you want to join a right side of current view with right edge of safe area. + /// Use this method when you want to join a right side of current view with right edge of the safe area. /// - /// - note: In earlier versions of OS than iOS 11, it creates a right relation to superview. + /// - note: In earlier versions of OS than iOS 11, it creates a right relation to a superview. /// - /// - parameter safeArea: The safe area of current view. Use a `nui_safeArea` global property. + /// - parameter safeArea: The safe area of current view. Use a `nui_safeArea` - global property. /// - parameter inset: The inset for additional space to safe area. Default value: 0. /// /// - returns: `Maker` instance for chaining relations. @@ -565,7 +565,7 @@ public final class Maker { @discardableResult public func right(to safeArea: SafeArea, inset: Number = 0.0) -> Maker { if #available(iOS 11.0, *) { guard let superview = view.superview else { - assertionFailure("Can not configure a right relation to safe area without superview.") + assertionFailure("Can not configure a right relation to the safe area without superview.") return self } return right(inset: superview.safeAreaInsets.right + inset.value) From 8fecedb8f5a0adfca625f59b2f3b97904efde636 Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Sat, 21 Oct 2017 15:55:24 +0600 Subject: [PATCH 05/14] Add tests for the safe area --- Framezilla.xcodeproj/project.pbxproj | 4 + .../xcschemes/Framezilla iOS.xcscheme | 5 +- Tests/MakerSafeAreaTests.swift | 248 ++++++++++++++++++ 3 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 Tests/MakerSafeAreaTests.swift diff --git a/Framezilla.xcodeproj/project.pbxproj b/Framezilla.xcodeproj/project.pbxproj index 1f48950..9c0461b 100644 --- a/Framezilla.xcodeproj/project.pbxproj +++ b/Framezilla.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 845108071EE2F5BC006DC1C8 /* ScrollViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845108061EE2F5BC006DC1C8 /* ScrollViewTests.swift */; }; 8497C0111E59EB7700447E2F /* Number.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8497C0101E59EB7700447E2F /* Number.swift */; }; 8497C0131E59FA4B00447E2F /* Array+Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8497C0121E59FA4B00447E2F /* Array+Stack.swift */; }; + 84EDCC241F9B3AB10091FAB9 /* MakerSafeAreaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84EDCC231F9B3AB10091FAB9 /* MakerSafeAreaTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -63,6 +64,7 @@ 845108061EE2F5BC006DC1C8 /* ScrollViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollViewTests.swift; sourceTree = ""; }; 8497C0101E59EB7700447E2F /* Number.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Number.swift; sourceTree = ""; }; 8497C0121E59FA4B00447E2F /* Array+Stack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Stack.swift"; sourceTree = ""; }; + 84EDCC231F9B3AB10091FAB9 /* MakerSafeAreaTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MakerSafeAreaTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -133,6 +135,7 @@ 8442F93A1EC75A8500B72551 /* MakerStackTests.swift */, 8442F93B1EC75A8500B72551 /* MakerTests.swift */, 8442F93C1EC75A8500B72551 /* MakerWidthHeightTests.swift */, + 84EDCC231F9B3AB10091FAB9 /* MakerSafeAreaTests.swift */, 8442F93D1EC75A8500B72551 /* StateTests.swift */, 845108061EE2F5BC006DC1C8 /* ScrollViewTests.swift */, ); @@ -276,6 +279,7 @@ 845108071EE2F5BC006DC1C8 /* ScrollViewTests.swift in Sources */, 8442F9431EC75A8500B72551 /* MakerTests.swift in Sources */, 8442F93E1EC75A8500B72551 /* BaseTest.swift in Sources */, + 84EDCC241F9B3AB10091FAB9 /* MakerSafeAreaTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Framezilla.xcodeproj/xcshareddata/xcschemes/Framezilla iOS.xcscheme b/Framezilla.xcodeproj/xcshareddata/xcschemes/Framezilla iOS.xcscheme index 4e62dcf..b1308e3 100644 --- a/Framezilla.xcodeproj/xcshareddata/xcschemes/Framezilla iOS.xcscheme +++ b/Framezilla.xcodeproj/xcshareddata/xcschemes/Framezilla iOS.xcscheme @@ -26,7 +26,9 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + language = "" + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> @@ -55,6 +57,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/Tests/MakerSafeAreaTests.swift b/Tests/MakerSafeAreaTests.swift new file mode 100644 index 0000000..d12fa19 --- /dev/null +++ b/Tests/MakerSafeAreaTests.swift @@ -0,0 +1,248 @@ +// +// MakerSafeAreaTests.swift +// FramezillaTests +// +// Created by Nikita Ermolenko on 21/10/2017. +// + +import XCTest +import Framezilla + +class MakerSafeAreaTests: XCTestCase { + + private let viewController = UIViewController() + + override func setUp() { + super.setUp() + viewController.view.frame = CGRect(x: 0, y: 0, width: 300, height: 500) + + let window = UIWindow() + window.rootViewController = viewController + window.isHidden = false + } + + override func tearDown() { + if #available(iOS 11.0, *) { + viewController.additionalSafeAreaInsets = .zero + } + super.tearDown() + } + + /* top */ + + func testThatCorrectlyConfigures_top_to_SafeArea() { + if #available(iOS 11.0, *) { + viewController.additionalSafeAreaInsets.top = 10 + } + + let view = UIView() + viewController.view.addSubview(view) + + view.configureFrame { maker in + maker.top(to: nui_safeArea) + maker.left() + maker.size(width: 20, height: 20) + } + + if #available(iOS 11.0, *) { + XCTAssertEqual(view.frame, CGRect(x: 0, y: viewController.additionalSafeAreaInsets.top, width: 20, height: 20)) + } + else { + XCTAssertEqual(view.frame, CGRect(x: 0, y: 0, width: 20, height: 20)) + } + } + + func testThatCorrectlyConfigures_top_to_SafeAreaWithInset() { + if #available(iOS 11.0, *) { + viewController.additionalSafeAreaInsets.top = 10 + } + + let view = UIView() + viewController.view.addSubview(view) + + let inset: CGFloat = 5 + view.configureFrame { maker in + maker.top(to: nui_safeArea, inset: inset) + maker.left() + maker.size(width: 20, height: 20) + } + + if #available(iOS 11.0, *) { + XCTAssertEqual(view.frame, CGRect(x: 0, y: viewController.additionalSafeAreaInsets.top + inset, width: 20, height: 20)) + } + else { + XCTAssertEqual(view.frame, CGRect(x: 0, y: inset, width: 20, height: 20)) + } + } + + /* left */ + + func testThatCorrectlyConfigures_left_to_SafeArea() { + if #available(iOS 11.0, *) { + viewController.additionalSafeAreaInsets.left = 10 + } + + let view = UIView() + viewController.view.addSubview(view) + + view.configureFrame { maker in + maker.top() + maker.left(to: nui_safeArea) + maker.size(width: 20, height: 20) + } + + if #available(iOS 11.0, *) { + XCTAssertEqual(view.frame, CGRect(x: viewController.additionalSafeAreaInsets.left, y: 0, width: 20, height: 20)) + } + else { + XCTAssertEqual(view.frame, CGRect(x: 0, y: 0, width: 20, height: 20)) + } + } + + func testThatCorrectlyConfigures_left_to_SafeAreaWithInset() { + if #available(iOS 11.0, *) { + viewController.additionalSafeAreaInsets.left = 10 + } + + let view = UIView() + viewController.view.addSubview(view) + + let inset: CGFloat = 5 + view.configureFrame { maker in + maker.top() + maker.left(to: nui_safeArea, inset: inset) + maker.size(width: 20, height: 20) + } + + if #available(iOS 11.0, *) { + XCTAssertEqual(view.frame, CGRect(x: viewController.additionalSafeAreaInsets.left + inset, + y: 0, + width: 20, + height: 20)) + } + else { + XCTAssertEqual(view.frame, CGRect(x: inset, + y: 0, + width: 20, + height: 20)) + } + } + + /* bottom */ + + func testThatCorrectlyConfigures_bottom_to_SafeArea() { + if #available(iOS 11.0, *) { + viewController.additionalSafeAreaInsets.bottom = 10 + } + + let view = UIView() + viewController.view.addSubview(view) + + view.configureFrame { maker in + maker.bottom(to: nui_safeArea) + maker.left() + maker.size(width: 20, height: 20) + } + + if #available(iOS 11.0, *) { + XCTAssertEqual(view.frame, CGRect(x: 0, + y: viewController.view.frame.height - viewController.additionalSafeAreaInsets.bottom - 20, + width: 20, + height: 20)) + } + else { + XCTAssertEqual(view.frame, CGRect(x: 0, + y: viewController.view.frame.height - 20, + width: 20, + height: 20)) + } + } + + func testThatCorrectlyConfigures_bottom_to_SafeAreaWithInset() { + if #available(iOS 11.0, *) { + viewController.additionalSafeAreaInsets.bottom = 10 + } + + let view = UIView() + viewController.view.addSubview(view) + + let inset: CGFloat = 5 + view.configureFrame { maker in + maker.bottom(to: nui_safeArea, inset: inset) + maker.left() + maker.size(width: 20, height: 20) + } + + if #available(iOS 11.0, *) { + XCTAssertEqual(view.frame, CGRect(x: 0, + y: viewController.view.frame.height - viewController.additionalSafeAreaInsets.bottom - 20 - inset, + width: 20, + height: 20)) + } + else { + XCTAssertEqual(view.frame, CGRect(x: 0, + y: viewController.view.frame.height - 20 - inset, + width: 20, + height: 20)) + } + } + + /* right */ + + func testThatCorrectlyConfigures_right_to_SafeArea() { + if #available(iOS 11.0, *) { + viewController.additionalSafeAreaInsets.right = 10 + } + + let view = UIView() + viewController.view.addSubview(view) + + view.configureFrame { maker in + maker.right(to: nui_safeArea) + maker.bottom() + maker.size(width: 20, height: 20) + } + + if #available(iOS 11.0, *) { + XCTAssertEqual(view.frame, CGRect(x: viewController.view.frame.width - viewController.additionalSafeAreaInsets.right - 20, + y: viewController.view.frame.height - 20, + width: 20, + height: 20)) + } + else { + XCTAssertEqual(view.frame, CGRect(x: viewController.view.frame.width - 20, + y: viewController.view.frame.height - 20, + width: 20, + height: 20)) + } + } + + func testThatCorrectlyConfigures_right_to_SafeAreaWithInset() { + if #available(iOS 11.0, *) { + viewController.additionalSafeAreaInsets.right = 10 + } + + let view = UIView() + viewController.view.addSubview(view) + + let inset: CGFloat = 5 + view.configureFrame { maker in + maker.right(to: nui_safeArea, inset: inset) + maker.bottom() + maker.size(width: 20, height: 20) + } + + if #available(iOS 11.0, *) { + XCTAssertEqual(view.frame, CGRect(x: viewController.view.frame.width - viewController.additionalSafeAreaInsets.right - 20 - inset, + y: viewController.view.frame.height - 20, + width: 20, + height: 20)) + } + else { + XCTAssertEqual(view.frame, CGRect(x: viewController.view.frame.width - 20 - inset, + y: viewController.view.frame.height - 20, + width: 20, + height: 20)) + } + } +} From 00fb38fcbbe6ca7dcd0f03032661ba05b217ffd5 Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Sat, 21 Oct 2017 15:59:01 +0600 Subject: [PATCH 06/14] Configure example for iPhone X --- .../FramezillaExample/ViewController.swift | 56 ++++--------------- 1 file changed, 11 insertions(+), 45 deletions(-) diff --git a/Example/FramezillaExample/ViewController.swift b/Example/FramezillaExample/ViewController.swift index d1b1b05..7612c3c 100644 --- a/Example/FramezillaExample/ViewController.swift +++ b/Example/FramezillaExample/ViewController.swift @@ -22,63 +22,29 @@ class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + if #available(iOS 11.0, *) { + additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10) + } + scrollView.backgroundColor = .yellow scrollView.contentSize = CGSize(width: 500, height: 1000) content1.backgroundColor = .red content2.backgroundColor = .green content3.backgroundColor = .black - - view.addSubview(scrollView) - scrollView.addSubview(content2) -// view.addSubview(content3) + view.backgroundColor = .yellow + view.addSubview(content1) } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() - -// content1.configureFrame { maker in -// maker.edges(top: 0, left: 0, right: 0) -// maker.height(50) -// } -// -// content2.configureFrame { maker in -// maker.edges(left: 0, bottom: 0, right: 0) -// maker.height(200) -// } -// -// content3.configureFrame { maker in -// maker.size(width: 20, height: 70) -// maker.centerY(between: content2, content1) -// maker.centerX() -// } - - -// content1.configureFrame { maker in -// maker.edges(top: 0, left: 0, bottom: 0) -// maker.width(50) -// } -// -// content2.configureFrame { maker in -// maker.edges(top: 0, bottom: 0, right: 0) -// maker.width(200) -// } -// -// content3.configureFrame { maker in -// maker.size(width: 20, height: 70) -// maker.centerX(between: content1, content2) -// maker.centerY() -// } - - scrollView.configureFrame { maker in - maker.margin(20) - } - content2.configureFrame { maker in - maker.size(width: 100, height: 100) - maker.left() - maker.centerY() + content1.configureFrame { maker in + maker.top(to: nui_safeArea) + maker.bottom(to: nui_safeArea) + maker.right(to: nui_safeArea) + maker.left(to: nui_safeArea) } } } From cf70bbebecdb5265489d890f4cab8682bd0f8c20 Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Sat, 21 Oct 2017 16:00:43 +0600 Subject: [PATCH 07/14] Add image with the safe area example (for README) --- img/safe_area.png | Bin 0 -> 45166 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/safe_area.png diff --git a/img/safe_area.png b/img/safe_area.png new file mode 100644 index 0000000000000000000000000000000000000000..bd5eef41bf3be20e5d455366197c647c68a7ed47 GIT binary patch literal 45166 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 HH Date: Sat, 21 Oct 2017 16:07:01 +0600 Subject: [PATCH 08/14] Refactor the documentation for the safe area methods. --- Sources/Maker.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/Maker.swift b/Sources/Maker.swift index a5c3a51..ce4bf91 100644 --- a/Sources/Maker.swift +++ b/Sources/Maker.swift @@ -249,9 +249,9 @@ public final class Maker { return left(to: RelationView(view: superview, relation: .left), inset: inset) } - /// Creates a left relation to the safe area. + /// Creates a left relation to the superview's safe area. /// - /// Use this method when you want to join a left side of current view with left edge of the safe area. + /// Use this method when you want to join a left side of current view with left edge of the superview's safe area. /// /// - note: In earlier versions of OS than iOS 11, it creates a left relation to a superview. /// @@ -313,9 +313,9 @@ public final class Maker { return top(to: RelationView(view: superview, relation: .top), inset: inset.value) } - /// Creates a top relation to the safe area. + /// Creates a top relation to the superview's safe area. /// - /// Use this method when you want to join a top side of current view with top edge of the safe area. + /// Use this method when you want to join a top side of current view with top edge of the superview's safe area. /// /// - note: In earlier versions of OS than iOS 11, it creates a top relation to a superview. /// @@ -481,9 +481,9 @@ public final class Maker { return bottom(to: RelationView(view: superview, relation: .bottom), inset: inset) } - /// Creates a bottom relation to the safe area. + /// Creates a bottom relation to the superview's safe area. /// - /// Use this method when you want to join a bottom side of current view with bottom edge of the safe area. + /// Use this method when you want to join a bottom side of current view with bottom edge of the superview's safe area. /// /// - note: In earlier versions of OS than iOS 11, it creates a bottom relation to a superview. /// @@ -551,9 +551,9 @@ public final class Maker { return right(to: RelationView(view: superview, relation: .right), inset: inset.value) } - /// Creates a right relation to the safe area. + /// Creates a right relation to the superview's safe area. /// - /// Use this method when you want to join a right side of current view with right edge of the safe area. + /// Use this method when you want to join a right side of current view with right edge of the superview's safe area. /// /// - note: In earlier versions of OS than iOS 11, it creates a right relation to a superview. /// From 4773126af396ba2c3e756ff0925b349d6b4a4b6f Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Sat, 21 Oct 2017 16:17:25 +0600 Subject: [PATCH 09/14] Update README.md --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 69a7ae7..2496276 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ Run `carthage update` to build the framework and drag the built `Framezilla.fram - [x] Optional semantic - `and` - [x] Side relations: `nui_left`, `nui_bottom`, `nui_width`, `nui_centerX` and so on. - [x] States +- [x] Safe area support 😱 # Usage :rocket: @@ -146,6 +147,21 @@ Also possible to create relations with another view, not a superview: } ``` +In iOS 11 Apple has introduced the safe area, similar to `topLayoutGuide` and `bottomLayoutGuide`. Framezilla supports this new api as well: + +```swift +content.configureFrame { maker in + maker.top(to: nui_safeArea) + maker.bottom(to: nui_safeArea) + maker.right(to: nui_safeArea, inset: 10) + maker.left(to: nui_safeArea, inset: 10) +} +``` + + + +**Note**: In earlier versions of OS than iOS 11, these methods create a relation to a superview, not the safe area. + ## Center relations If you just want to center subview relative superview with constant `width` and `height`, this approach specially for you: From 93398880e9b5386b70c5874c4ce0fb4c8290f623 Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Sat, 21 Oct 2017 16:24:55 +0600 Subject: [PATCH 10/14] Add travis matrix for iOS11 --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4d13f2c..df9047a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,13 @@ language: objective-c -osx_image: xcode8 +osx_image: xcode9 env: global: - PROJECT=Framezilla.xcodeproj - IOS_FRAMEWORK_SCHEME="Framezilla iOS" - - IOS_SDK=iphonesimulator10.0 + - IOS_SDK=iphonesimulator11.0 matrix: - - DESTINATION="OS=10.0,name=iPhone 7 Plus" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" + - DESTINATION="OS=10.0,name=iPhone 7 Plus" + - DESTINATION="OS=11.0,name=iPhone 7 Plus" script: - set -o pipefail From 25ca89e5d905d0b80b126bf2a6be18f52afc8c32 Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Sat, 21 Oct 2017 16:36:32 +0600 Subject: [PATCH 11/14] Update to recommended project settings --- .../project.pbxproj | 14 ++++++++- Framezilla.xcodeproj/project.pbxproj | 30 ++++++++++++++----- .../xcschemes/Framezilla iOS.xcscheme | 2 +- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/Example/FramezillaExample.xcodeproj/project.pbxproj b/Example/FramezillaExample.xcodeproj/project.pbxproj index 852087e..658de24 100644 --- a/Example/FramezillaExample.xcodeproj/project.pbxproj +++ b/Example/FramezillaExample.xcodeproj/project.pbxproj @@ -111,7 +111,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0830; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = "Nikita Ermolenko"; TargetAttributes = { 8442F94E1EC75C9C00B72551 = { @@ -193,7 +193,9 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -201,7 +203,11 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -244,7 +250,9 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -252,7 +260,11 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; diff --git a/Framezilla.xcodeproj/project.pbxproj b/Framezilla.xcodeproj/project.pbxproj index 9c0461b..bb4482f 100644 --- a/Framezilla.xcodeproj/project.pbxproj +++ b/Framezilla.xcodeproj/project.pbxproj @@ -200,16 +200,16 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0800; - LastUpgradeCheck = 0820; + LastUpgradeCheck = 0900; TargetAttributes = { 115972131D8450F500BC5C20 = { CreatedOnToolsVersion = 8.0; - LastSwiftMigration = 0800; + LastSwiftMigration = 0900; ProvisioningStyle = Manual; }; 11FB41311D844D2B00700A40 = { CreatedOnToolsVersion = 8.0; - LastSwiftMigration = 0800; + LastSwiftMigration = 0900; ProvisioningStyle = Automatic; }; }; @@ -362,7 +362,8 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OBJC_BRIDGING_HEADER = "Tests/FramezillaTests-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -411,7 +412,8 @@ SDKROOT = iphoneos; SWIFT_OBJC_BRIDGING_HEADER = "Tests/FramezillaTests-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 4.0; VALIDATE_PRODUCT = YES; }; name = Release; @@ -419,12 +421,18 @@ 11FB412B1D844CD800700A40 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -444,12 +452,18 @@ 11FB412C1D844CD800700A40 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -522,7 +536,8 @@ SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -579,7 +594,8 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; diff --git a/Framezilla.xcodeproj/xcshareddata/xcschemes/Framezilla iOS.xcscheme b/Framezilla.xcodeproj/xcshareddata/xcschemes/Framezilla iOS.xcscheme index b1308e3..c462db0 100644 --- a/Framezilla.xcodeproj/xcshareddata/xcschemes/Framezilla iOS.xcscheme +++ b/Framezilla.xcodeproj/xcshareddata/xcschemes/Framezilla iOS.xcscheme @@ -1,6 +1,6 @@ Date: Sat, 21 Oct 2017 16:42:13 +0600 Subject: [PATCH 12/14] =?UTF-8?q?=F0=9F=98=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .swiftlint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index bea1ed3..fc23e7c 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -20,7 +20,7 @@ type_body_length: - 400 # error file_length: - warning: 800 + warning: 1000 error: 1200 colon: From a074c8c974bca58f311cb34150241a0e3156043d Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Sat, 21 Oct 2017 16:44:33 +0600 Subject: [PATCH 13/14] Change podspec version -> 2.3.0 --- Framezilla.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framezilla.podspec b/Framezilla.podspec index 1a527a9..b1a3c2c 100644 --- a/Framezilla.podspec +++ b/Framezilla.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "Framezilla" - spec.version = "2.2.1" + spec.version = "2.3.0" spec.summary = "Comfortable syntax for working with frames." spec.homepage = "https://github.com/Otbivnoe/Framezilla" From 843e06e4b8533e3412603bb30f61f89298e5a311 Mon Sep 17 00:00:00 2001 From: Nikita Ermolenko Date: Sat, 21 Oct 2017 16:44:56 +0600 Subject: [PATCH 14/14] Update swift badge to Swift 4.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2496276..192d57a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Version](https://img.shields.io/cocoapods/v/Framezilla.svg?style=flat)](http://cocoadocs.org/docsets/Framezilla) [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Platform](https://img.shields.io/cocoapods/p/Framezilla.svg?style=flat)](http://cocoadocs.org/docsets/Framezilla) -![Swift 3.0.x](https://img.shields.io/badge/Swift-3.0.x-orange.svg) +![Swift 4.0](https://img.shields.io/badge/Swift-4.0-orange.svg) [![License](https://img.shields.io/cocoapods/l/Framezilla.svg?style=flat)](http://cocoadocs.org/docsets/Framezilla) **Everyone wants to see smooth scrolling, that tableview or collectionview scrolls without any lags and it's right choice. But the constraints do not give it for us. Therefore, we have to choose manual calculation frames, but sometimes, when cell has a complex structure, code has not elegant, beautiful structure.**