diff --git a/Framezilla.podspec b/Framezilla.podspec index 80b8f47..9f3ce09 100644 --- a/Framezilla.podspec +++ b/Framezilla.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "Framezilla" - spec.version = "2.6.0" + spec.version = "2.7.0" spec.summary = "Comfortable syntax for working with frames." spec.homepage = "https://github.com/Otbivnoe/Framezilla" diff --git a/Sources/Maker.swift b/Sources/Maker.swift index de7db86..6cb0664 100644 --- a/Sources/Maker.swift +++ b/Sources/Maker.swift @@ -438,18 +438,37 @@ public final class Maker { /// - returns: `Maker` instance for chaining relations. @discardableResult public func heightToFit() -> Maker { - view.sizeToFit() - setHighPriorityValue(view.bounds.height, for: .height) - return self + return heightThatFits(maxHeight: CGFloat.greatestFiniteMagnitude) } /// Calculates the height that best fits the specified size. /// /// - returns: `Maker` instance for chaining relations. - @discardableResult public func heightThatFits(height: Number) -> Maker { - view.sizeToFit() - setHighPriorityValue(min(view.bounds.height, height.value), for: .height) + @discardableResult public func heightThatFits(maxHeight: Number) -> Maker { + let handler = { [unowned self] in + let fitWidth: CGFloat + + if let parameter = self.widthParameter { + fitWidth = parameter.value + } + else if let parameter = self.widthToParameter { + fitWidth = self.relationSize(view: parameter.view, for: parameter.relationType) * parameter.value + } + else if let leftParameter = self.leftParameter, let rightParameter = self.rightParameter { + let leftViewX = self.convertedValue(for: leftParameter.relationType, with: leftParameter.view) + leftParameter.value + let rightViewX = self.convertedValue(for: rightParameter.relationType, with: rightParameter.view) - rightParameter.value + + fitWidth = rightViewX - leftViewX + } + else { + fitWidth = .greatestFiniteMagnitude + } + + let fitSize = self.view.sizeThatFits(CGSize(width: fitWidth, height: .greatestFiniteMagnitude)) + self.newRect.setValue(min(maxHeight.value, fitSize.height), for: .height) + } + handlers.append((.high, handler)) return self } @@ -458,18 +477,38 @@ public final class Maker { /// - returns: `Maker` instance for chaining relations. @discardableResult public func widthToFit() -> Maker { - view.sizeToFit() - setHighPriorityValue(view.bounds.width, for: .width) - return self + return widthThatFits(maxWidth: CGFloat.greatestFiniteMagnitude) } /// Calculates the width that best fits the specified size. /// /// - returns: `Maker` instance for chaining relations. - @discardableResult public func widthThatFits(width: Number) -> Maker { - view.sizeToFit() - setHighPriorityValue(min(view.bounds.width, width.value), for: .width) + @discardableResult public func widthThatFits(maxWidth: Number) -> Maker { + let handler = { [unowned self] in + let fitHeight: CGFloat + + if let parameter = self.heightParameter { + fitHeight = parameter.value + } + else if let parameter = self.heightToParameter { + fitHeight = self.relationSize(view: parameter.view, for: parameter.relationType) * parameter.value + } + else if let topParameter = self.topParameter, let bottomParameter = self.bottomParameter { + let topViewY = self.convertedValue(for: topParameter.relationType, with: topParameter.view) + topParameter.value + let bottomViewY = self.convertedValue(for: bottomParameter.relationType, with: bottomParameter.view) - bottomParameter.value + + fitHeight = bottomViewY - topViewY + } + else { + fitHeight = .greatestFiniteMagnitude + } + + let fitSize = self.view.sizeThatFits(CGSize(width: .greatestFiniteMagnitude, height: fitHeight)) + self.newRect.setValue(min(maxWidth.value, fitSize.width), for: .width) + } + + handlers.append((.high, handler)) return self } @@ -890,3 +929,26 @@ public final class Maker { } } } + +// MARK: - Deprecated + +extension Maker { + + /// Calculates the width that best fits the specified size. + /// + /// - returns: `Maker` instance for chaining relations. + + @available(*, unavailable, renamed: "widthThatFits(maxWidth:)") + @discardableResult public func widthThatFits(width: Number) -> Maker { + return self + } + + /// Calculates the height that best fits the specified size. + /// + /// - returns: `Maker` instance for chaining relations. + + @available(*, unavailable, renamed: "heightThatFits(maxHeight:)") + @discardableResult public func heightThatFits(height: Number) -> Maker { + return self + } +} diff --git a/Tests/MakerWidthHeightTests.swift b/Tests/MakerWidthHeightTests.swift index cedaa18..9053a3d 100644 --- a/Tests/MakerWidthHeightTests.swift +++ b/Tests/MakerWidthHeightTests.swift @@ -9,6 +9,40 @@ import XCTest @testable import Framezilla +final class HeightFitView: UIView { + + let maxHeight: CGFloat = 100 + let middleHeight: CGFloat = 70 + let lowHeight: CGFloat = 50 + + override func sizeThatFits(_ size: CGSize) -> CGSize { + if size.width > 100 { + return CGSize(width: size.width, height: maxHeight) + } + else if size.width < 50 { + return CGSize(width: size.width, height: lowHeight) + } + return CGSize(width: size.width, height: middleHeight) + } +} + +final class WidthFitView: UIView { + + let maxWidth: CGFloat = 100 + let middleWidth: CGFloat = 70 + let lowWidth: CGFloat = 50 + + override func sizeThatFits(_ size: CGSize) -> CGSize { + if size.height > 100 { + return CGSize(width: maxWidth, height: size.height) + } + else if size.height < 50 { + return CGSize(width: lowWidth, height: size.width) + } + return CGSize(width: middleWidth, height: size.width) + } +} + class MakerWidthHeightTests: BaseTest { func testThatJustSetting_width_configuresCorrectly() { @@ -157,6 +191,57 @@ class MakerWidthHeightTests: BaseTest { /* width / height to fit */ + func testHeightToFit() { + + let label = UILabel() + label.text = "HelloHelloHelloHello" + + label.configureFrame { maker in + maker.heightToFit() + } + XCTAssertTrue(label.bounds.height > 0) + XCTAssertEqual(label.bounds.width, 0) + } + + func testHeightToFit_withWidthRelation() { + + let view = HeightFitView() + + view.configureFrame { maker in + maker.heightToFit() + maker.width(120) + } + XCTAssertEqual(view.frame, CGRect(x: 0, y: 0, width: 120, height: view.maxHeight)) + } + + func testHeightToFit_withLeftAndRightRelations() { + + let view = HeightFitView() + mainView.addSubview(view) + + view.configureFrame { maker in + maker.heightToFit() + maker.left(inset: 220).right(inset: 220) + maker.top() + } + XCTAssertEqual(view.frame, CGRect(x: 220, y: 0, width: 60, height: view.middleHeight)) + view.removeFromSuperview() + } + + func testHeightToFit_withWidthToRelation() { + + let view = HeightFitView() + mainView.addSubview(view) + + view.configureFrame { maker in + maker.heightToFit() + maker.width(to: mainView.nui_width, multiplier: 0.01) + maker.top() + } + XCTAssertEqual(view.frame, CGRect(x: 0, y: 0, width: 5, height: view.lowHeight)) + view.removeFromSuperview() + } + func testWidthToFit() { let label = UILabel() @@ -169,16 +254,43 @@ class MakerWidthHeightTests: BaseTest { XCTAssertEqual(label.bounds.height, 0) } - func testHeightToFit() { + func testWidthToFit_withHeightRelation() { - let label = UILabel() - label.text = "HelloHelloHelloHello" + let view = WidthFitView() - label.configureFrame { maker in - maker.heightToFit() + view.configureFrame { maker in + maker.widthToFit() + maker.height(120) } - XCTAssertTrue(label.bounds.height > 0) - XCTAssertEqual(label.bounds.width, 0) + XCTAssertEqual(view.frame, CGRect(x: 0, y: 0, width: view.maxWidth, height: 120)) + } + + func testWidthToFit_withTopAndBottomRelations() { + + let view = WidthFitView() + mainView.addSubview(view) + + view.configureFrame { maker in + maker.widthToFit() + maker.top(inset: 220).bottom(inset: 220) + maker.left() + } + XCTAssertEqual(view.frame, CGRect(x: 0, y: 220, width: view.middleWidth, height: 60)) + view.removeFromSuperview() + } + + func testWidthToFit_withHeightToRelation() { + + let view = WidthFitView() + mainView.addSubview(view) + + view.configureFrame { maker in + maker.widthToFit() + maker.height(to: mainView.nui_width, multiplier: 0.01) + maker.left() + } + XCTAssertEqual(view.frame, CGRect(x: 0, y: 0, width: view.lowWidth, height: 5)) + view.removeFromSuperview() } /* width / height that fits */ @@ -189,7 +301,7 @@ class MakerWidthHeightTests: BaseTest { label.text = "HelloHelloHelloHello" label.configureFrame { maker in - maker.widthThatFits(width: 30) + maker.widthThatFits(maxWidth: 30) } XCTAssertEqual(label.bounds.width, 30) XCTAssertEqual(label.bounds.height, 0) @@ -201,7 +313,7 @@ class MakerWidthHeightTests: BaseTest { label.text = "HelloHelloHelloHello" label.configureFrame { maker in - maker.widthThatFits(width: 300) + maker.widthThatFits(maxWidth: 300) } XCTAssertTrue(label.bounds.width != 300) XCTAssertEqual(label.bounds.height, 0) @@ -213,7 +325,7 @@ class MakerWidthHeightTests: BaseTest { label.text = "HelloHelloHelloHello" label.configureFrame { maker in - maker.heightThatFits(height: 5) + maker.heightThatFits(maxHeight: 5) } XCTAssertEqual(label.bounds.height, 5) XCTAssertEqual(label.bounds.width, 0) @@ -225,7 +337,7 @@ class MakerWidthHeightTests: BaseTest { label.text = "HelloHelloHelloHello" label.configureFrame { maker in - maker.heightThatFits(height: 300) + maker.heightThatFits(maxHeight: 300) } XCTAssertTrue(label.bounds.height != 300) XCTAssertEqual(label.bounds.width, 0)