diff --git a/.gitignore b/.gitignore index 2c22487b..828c794e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,65 +1,10 @@ # Xcode # -# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore -## Build generated build/ DerivedData/ - -## Various settings -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 -xcuserdata/ - -## Other -*.moved-aside -*.xcuserstate - -## Obj-C/Swift specific -*.hmap -*.ipa -*.dSYM.zip -*.dSYM - -## Playgrounds -timeline.xctimeline -playground.xcworkspace - -# Swift Package Manager -# -# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. -# Packages/ -.build/ - -# CocoaPods -# -# We recommend against adding the Pods directory to your .gitignore. However -# you should judge for yourself, the pros and cons are mentioned at: -# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control -# -# Pods/ - -# Carthage -# -# Add this line if you want to avoid checking in source code from Carthage dependencies. -# Carthage/Checkouts - +Pods/ Carthage/Build - -# fastlane -# -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the -# screenshots whenever they are needed. -# For more information about the recommended setup visit: -# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md - -fastlane/report.xml -fastlane/Preview.html -fastlane/screenshots -fastlane/test_output +MCSwiftLayoutSample/Pods +xcuserdata +*.xcworkspace \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..a74f243e --- /dev/null +++ b/Gemfile @@ -0,0 +1,5 @@ +# Gemfile +source 'https://rubygems.org' + +gem 'cocoapods', '~>1.1.0' +gem 'synx', '~> 0.2.1' diff --git a/Keynote Layout presentation.key b/Keynote Layout presentation.key index c7d4b19a..26837908 100644 Binary files a/Keynote Layout presentation.key and b/Keynote Layout presentation.key differ diff --git a/MCSwiftLayout.xcodeproj/project.pbxproj b/MCSwiftLayout.xcodeproj/project.pbxproj index c4742ac2..81827fed 100644 --- a/MCSwiftLayout.xcodeproj/project.pbxproj +++ b/MCSwiftLayout.xcodeproj/project.pbxproj @@ -8,9 +8,12 @@ /* Begin PBXBuildFile section */ 2439CC311E6659FA003326FB /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2439CC301E6659FA003326FB /* Layout.swift */; }; + 2469C4FC1E74855D00073BEE /* PinEdgeCoordinateSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2469C4FB1E74855D00073BEE /* PinEdgeCoordinateSpec.swift */; }; + 246D36481E6C46F50050F202 /* PinPointCoordinatesSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 246D36471E6C46F50050F202 /* PinPointCoordinatesSpec.swift */; }; 249EFE841E64FB4C00165E39 /* MCSwiftLayout.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 249EFE7A1E64FB4C00165E39 /* MCSwiftLayout.framework */; }; 249EFE891E64FB4C00165E39 /* MCSwiftLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 249EFE881E64FB4C00165E39 /* MCSwiftLayoutTests.swift */; }; 249EFE8B1E64FB4C00165E39 /* MCSwiftLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 249EFE7D1E64FB4C00165E39 /* MCSwiftLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E88B11042FE48DD3D4E7EB0C /* Pods_MCSwiftLayoutTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB717599553AA71EE659C312 /* Pods_MCSwiftLayoutTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -25,12 +28,17 @@ /* Begin PBXFileReference section */ 2439CC301E6659FA003326FB /* Layout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Layout.swift; sourceTree = ""; }; + 2469C4FB1E74855D00073BEE /* PinEdgeCoordinateSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinEdgeCoordinateSpec.swift; sourceTree = ""; }; + 246D36471E6C46F50050F202 /* PinPointCoordinatesSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinPointCoordinatesSpec.swift; sourceTree = ""; }; 249EFE7A1E64FB4C00165E39 /* MCSwiftLayout.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MCSwiftLayout.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 249EFE7D1E64FB4C00165E39 /* MCSwiftLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCSwiftLayout.h; sourceTree = ""; }; 249EFE7E1E64FB4C00165E39 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 249EFE831E64FB4C00165E39 /* MCSwiftLayoutTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MCSwiftLayoutTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 249EFE881E64FB4C00165E39 /* MCSwiftLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCSwiftLayoutTests.swift; sourceTree = ""; }; 249EFE8A1E64FB4C00165E39 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 657A2A825B5BD607ACB09F58 /* Pods-MCSwiftLayoutTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MCSwiftLayoutTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MCSwiftLayoutTests/Pods-MCSwiftLayoutTests.debug.xcconfig"; sourceTree = ""; }; + DB717599553AA71EE659C312 /* Pods_MCSwiftLayoutTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MCSwiftLayoutTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E8C9302287EF4331340F627C /* Pods-MCSwiftLayoutTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MCSwiftLayoutTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-MCSwiftLayoutTests/Pods-MCSwiftLayoutTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -46,18 +54,29 @@ buildActionMask = 2147483647; files = ( 249EFE841E64FB4C00165E39 /* MCSwiftLayout.framework in Frameworks */, + E88B11042FE48DD3D4E7EB0C /* Pods_MCSwiftLayoutTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 138A1F602470415292B7194B /* Frameworks */ = { + isa = PBXGroup; + children = ( + DB717599553AA71EE659C312 /* Pods_MCSwiftLayoutTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 249EFE701E64FB4C00165E39 = { isa = PBXGroup; children = ( 249EFE7C1E64FB4C00165E39 /* MCSwiftLayout */, 249EFE871E64FB4C00165E39 /* MCSwiftLayoutTests */, 249EFE7B1E64FB4C00165E39 /* Products */, + 8604B3F1C9C7A0EB2F11356F /* Pods */, + 138A1F602470415292B7194B /* Frameworks */, ); sourceTree = ""; }; @@ -84,11 +103,22 @@ isa = PBXGroup; children = ( 249EFE881E64FB4C00165E39 /* MCSwiftLayoutTests.swift */, + 246D36471E6C46F50050F202 /* PinPointCoordinatesSpec.swift */, + 2469C4FB1E74855D00073BEE /* PinEdgeCoordinateSpec.swift */, 249EFE8A1E64FB4C00165E39 /* Info.plist */, ); path = MCSwiftLayoutTests; sourceTree = ""; }; + 8604B3F1C9C7A0EB2F11356F /* Pods */ = { + isa = PBXGroup; + children = ( + 657A2A825B5BD607ACB09F58 /* Pods-MCSwiftLayoutTests.debug.xcconfig */, + E8C9302287EF4331340F627C /* Pods-MCSwiftLayoutTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -125,9 +155,12 @@ isa = PBXNativeTarget; buildConfigurationList = 249EFE911E64FB4C00165E39 /* Build configuration list for PBXNativeTarget "MCSwiftLayoutTests" */; buildPhases = ( + DD0B0B702D32991A998E967B /* [CP] Check Pods Manifest.lock */, 249EFE7F1E64FB4C00165E39 /* Sources */, 249EFE801E64FB4C00165E39 /* Frameworks */, 249EFE811E64FB4C00165E39 /* Resources */, + 45B171B50A18EC68ECE6BBC9 /* [CP] Embed Pods Frameworks */, + 876430984866C52BFD6C79B8 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -197,6 +230,54 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 45B171B50A18EC68ECE6BBC9 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/MCSwiftLayoutSample/Pods/Target Support Files/Pods-MCSwiftLayoutTests/Pods-MCSwiftLayoutTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 876430984866C52BFD6C79B8 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/MCSwiftLayoutSample/Pods/Target Support Files/Pods-MCSwiftLayoutTests/Pods-MCSwiftLayoutTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + DD0B0B702D32991A998E967B /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 249EFE751E64FB4C00165E39 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -210,6 +291,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 2469C4FC1E74855D00073BEE /* PinEdgeCoordinateSpec.swift in Sources */, + 246D36481E6C46F50050F202 /* PinPointCoordinatesSpec.swift in Sources */, 249EFE891E64FB4C00165E39 /* MCSwiftLayoutTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -336,6 +419,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = MCSwiftLayout/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mirego.MCSwiftLayout; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -357,6 +441,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = MCSwiftLayout/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mirego.MCSwiftLayout; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -367,6 +452,7 @@ }; 249EFE921E64FB4C00165E39 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 657A2A825B5BD607ACB09F58 /* Pods-MCSwiftLayoutTests.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; DEVELOPMENT_TEAM = 4Q596JWQC5; @@ -380,6 +466,7 @@ }; 249EFE931E64FB4C00165E39 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = E8C9302287EF4331340F627C /* Pods-MCSwiftLayoutTests.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; DEVELOPMENT_TEAM = 4Q596JWQC5; diff --git a/MCSwiftLayout/Layout.swift b/MCSwiftLayout/Layout.swift index e246cd2a..c1fe725f 100644 --- a/MCSwiftLayout/Layout.swift +++ b/MCSwiftLayout/Layout.swift @@ -8,20 +8,57 @@ import UIKit /* + + QUESTIONS: + =============================================== + + - Names: + - view.layout.pinTopLeft(.bottomRight, of: backgroundView).margin(10) + - view.layout.pinTopLeft(to: .bottomRight, of: backgroundView).margin(10) + - view.layout.pinTopLeft(to: backgroundView, .bottomRight).margin(10) + - view.layout.pinTopLeft(backgroundView, .bottomRight).margin(10) + - view.layout.pinTopLeft(equal: backgroundView, .bottomRight).margin(10) + + - view.pin.topLeft(to: .bottomRight, of: backgroundView).margin(10) + - view.pin.topLeft(.bottomRight, of: backgroundView).margin(10) + + - view.layout.snapTopLeft(.bottomRight, of: backgroundView) + - view.layout.snapTopLeft(to: .bottomRight, of: backgroundView).margin(10) + - view.layout.snapTopLeft(to: backgroundView, .bottomRight).margin(10) + - view.layout.snapTop(to: .bottom, of: backgroundView).margin(10) + + - layout.snapTopLeft(to: backgroundView, .bottomRight) + + - layout.top(to: .bottom, of: backgroundView) + + - position related to superview + - layout.bottomLeft().margin(10) + - layout.bottomLeftOfSuperview().margin(10) + + - enum Snap ou Pin ou Point? + + TODO: + =============================================== - implement sizeThatFits - - Insets should be applied to width and height before calling view.sizeThatFits() + - Insets should be applied to width and height before calling view.sizeThatFits() - Implement Layout + Layout operator - - Implement - - marginHorizontal - - marginVertical - - insetHorizontal - - insetVertical - - inset(t:? = nil l:? = nil b:? = nil r:? = nil ) ?? - - margin(t:? = nil l:? = nil b:? = nil r:? = nil ) ?? + - left(of), above(of), below(of), ... devrait fonctionner même si les des view n'ont pas le même parent! + comme pour pinTopLeft(of), ... + + - width(percentage: CGFloat, of view: UIView) + - maxWidth(), minWidth(), maxHeight(), minHeight() + + - marginHorizontal + - marginVertical + - insetHorizontal + - insetVertical + - inset(t:? = nil l:? = nil b:? = nil r:? = nil ) ?? + - margin(t:? = nil l:? = nil b:? = nil r:? = nil ) ?? */ +fileprivate typealias Context = () -> String public extension UIView { public var top: CGFloat { @@ -147,16 +184,38 @@ public extension UIView { return Layout(view: self) } -// func layout(_ positionning: Layout) { -// positionning.apply(onView: self) -// } + func layout(with layout: Layout) { + layout.apply(onView: self) + } } +public enum Pin: String { + case topLeft + case topCenter + case topRight + case leftCenter + case center + case rightCenter + case bottomLeft + case bottomCenter + case bottomRight +} + +public enum VerticalEdge: String { + case top + case bottom +} + +public enum HorizontalEdge: String { + case left + case right +} public class Layout { static var logConflicts = true + //static var useBottomRightCssStyle = false - fileprivate let view: UIView? + fileprivate let view: UIView fileprivate var top: CGFloat? fileprivate var left: CGFloat? @@ -180,7 +239,7 @@ public class Layout { fileprivate var bottomInset: CGFloat? fileprivate var rightInset: CGFloat? - public init (view: UIView? = nil) { + public init (view: UIView) { self.view = view } @@ -189,123 +248,410 @@ public class Layout { } // - // top, left, bottom, right, topLeft, topCenter, topRight, ... + // top, left, bottom, right // @discardableResult public func top(_ value: CGFloat) -> Layout { - setTopCoordinate(value, setterContext: "top(\(value))") + setTop(value, context: { return "top(\(value))" }) return self } @discardableResult public func left(_ value: CGFloat) -> Layout { - setLeftCoordinate(value, setterContext: "left(\(value))") + setLeft(value, context: { return "left(\(value))" }) return self } @discardableResult public func bottom(_ value: CGFloat) -> Layout { - setBottomCoordinate(value, setterContext: "bottom(\(value))") + setBottom(value, context: { return "bottom(\(value))" }) return self } @discardableResult public func right(_ value: CGFloat) -> Layout { - setRightCoordinate(value, setterContext: "right(\(value))") + setRight(value, context: { return "right(\(value))" }) + return self + } + + + // + // pinTop, pinLeft, pinBottom, pinRight + // + @discardableResult + public func pinTop(_ edge: VerticalEdge, of relativeView: UIView) -> Layout { + func context() -> String { return "pinTop(\(edge.rawValue), of: \(view))" } + if let coordinate = computeCoordinates(forEdge: edge, of: relativeView, context: context) { + setTop(coordinate, context: context) + } + return self + } + + @discardableResult + public func pinLeft(_ edge: HorizontalEdge, of relativeView: UIView) -> Layout { + func context() -> String { return "pinLeft(\(edge.rawValue), of: \(view))" } + if let coordinate = computeCoordinates(forEdge: edge, of: relativeView, context: context) { + setLeft(coordinate, context: context) + } + return self + } + + @discardableResult + public func pinBottom(_ edge: VerticalEdge, of relativeView: UIView) -> Layout { + func context() -> String { return "pinBottom(\(edge.rawValue), of: \(view))" } + if let coordinate = computeCoordinates(forEdge: edge, of: relativeView, context: context) { + setBottom(coordinate, context: context) + } + return self + } + + @discardableResult + public func pinRight(_ edge: HorizontalEdge, of relativeView: UIView) -> Layout { + func context() -> String { return "pinRight(\(edge.rawValue), of: \(view))" } + if let coordinate = computeCoordinates(forEdge: edge, of: relativeView, context: context) { + setRight(coordinate, context: context) + } return self } //TODO: Add hCenter and vCenter + // + // pinTopLeft, pinTopCenter, pinTopRight, + // pinLeftCenter, pinCenter, pinRightCenter, + // pinBottomLeft, pinBottomCenter, pinBottomRight, + // @discardableResult - public func topLeft(_ point: CGPoint) -> Layout { - return topLeft(x: point.x, y: point.y) + public func pinTopLeft(to point: CGPoint) -> Layout { + return setTopLeft(point, context: { return "topLeft(CGPoint(x: \(point.x), y: \(point.y)))" }) } + /// Position the topLeft on the specified view's pin. @discardableResult - public func topLeft(x: CGFloat, y: CGFloat) -> Layout { - return setTopLeft(x: x, y: y, setterContext: "topLeft(\(x), \(y))") + public func pinTopLeft(_ pin: Pin, of relativeView: UIView) -> Layout { + func context() -> String { return "topLeft(\(pin.rawValue), of: \(view))" } + if let coordinates = computeCoordinates(forPin: pin, of: relativeView, context: context) { + setTopLeft(coordinates, context: context) + } + return self } + /// Position on the topLeft corner of the specified view. @discardableResult - public func topCenter(_ point: CGPoint) -> Layout { - return topCenter(x: point.x, y: point.y) + public func pinTopLeft(to relativeView: UIView) -> Layout { + func context() -> String { return "topLeft(of: \(view))" } + if let coordinates = computeCoordinates(forPin: .topLeft, of: relativeView, context: context) { + setTopLeft(coordinates, context: context) + } + return self } + /// Position on the topLeft corner of its parent. @discardableResult - public func topCenter(x: CGFloat, y: CGFloat) -> Layout { - return setTopCenter(x: x, y: y, setterContext: "topCenter(\(x), \(y))") + public func pinTopLeft() -> Layout { + func context() -> String { return "topLeft()" } + if let layoutSuperview = validateLayoutSuperview(context: context) { + if let coordinates = computeCoordinates(forPin: .topLeft, of: layoutSuperview, context: context) { + setTopLeft(coordinates, context: context) + } + } + return self } @discardableResult - public func topRight(_ point: CGPoint) -> Layout { - return topRight(x: point.x, y: point.y) + public func pinTopCenter(to point: CGPoint) -> Layout { + return setTopCenter(point, context: { return "topCenter(CGPoint(x: \(point.x), y: \(point.y)))" }) } + /// Position the topCenter on the specified view's pin. @discardableResult - public func topRight(x: CGFloat, y: CGFloat) -> Layout { - return setTopRight(x: x, y: y, setterContext: "topRight(\(x), \(y))") + public func pinTopCenter(_ pin: Pin, of relativeView: UIView) -> Layout { + func context() -> String { return "topCenter(\(pin.rawValue), of: \(view))" } + if let coordinates = computeCoordinates(forPin: pin, of: relativeView, context: context) { + setTopCenter(coordinates, context: context) + } + return self } + /// Position on the topCenter corner of the specified view. @discardableResult - public func leftCenter(_ point: CGPoint) -> Layout { - return leftCenter(x: point.x, y: point.y) + public func pinTopCenter(to relativeView: UIView) -> Layout { + func context() -> String { return "topCenter(of: \(view))" } + if let coordinates = computeCoordinates(forPin: .topCenter, of: relativeView, context: context) { + setTopCenter(coordinates, context: context) + } + return self + } + + /// Position on the topCenter corner of its parent. + @discardableResult + public func pinTopCenter() -> Layout { + func context() -> String { return "topCenter()" } + if let layoutSuperview = validateLayoutSuperview(context: context) { + if let coordinates = computeCoordinates(forPin: .topCenter, of: layoutSuperview, context: context) { + setTopCenter(coordinates, context: context) + } + } + return self } @discardableResult - public func leftCenter(x: CGFloat, y: CGFloat) -> Layout { - return setLeftCenter(x: x, y: y, setterContext: "leftCenter(\(x), \(y))") + public func pinTopRight(to point: CGPoint) -> Layout { + return setTopRight(point, context: { return "topRight(CGPoint(x: \(point.x), y: \(point.y)))" }) + } + + /// Position the topRight on the specified view's pin. + @discardableResult + public func pinTopRight(_ pin: Pin, of relativeView: UIView) -> Layout { + func context() -> String { return "topRight(\(pin.rawValue), of: \(view))" } + if let coordinates = computeCoordinates(forPin: pin, of: relativeView, context: context) { + setTopRight(coordinates, context: context) + } + return self } - // TODO center!!! -// var centers: CGPoint { -// get { return CGPoint(x: hCenter, y: vCenter) } -// set { -// left = newValue.x - (width / 2) -// top = newValue.y - (width / 2) -// } -// } + /// Position on the topRight corner of the specified view. + @discardableResult + public func pinTopRight(to relativeView: UIView) -> Layout { + func context() -> String { return "topRight(of: \(view))" } + if let coordinates = computeCoordinates(forPin: .topRight, of: relativeView, context: context) { + setTopRight(coordinates, context: context) + } + return self + } + + /// Position on the topRight corner of its parent. + @discardableResult + public func pinTopRight() -> Layout { + func context() -> String { return "topRight()" } + if let layoutSuperview = validateLayoutSuperview(context: context) { + if let coordinates = computeCoordinates(forPin: .topRight, of: layoutSuperview, context: context) { + setTopRight(coordinates, context: context) + } + } + return self + } + + @discardableResult + public func pinLeftCenter(to point: CGPoint) -> Layout { + return setLeftCenter(point, context: { return "leftCenter(CGPoint(x: \(point.x), y: \(point.y)))" }) + } + + /// Position the leftCenter on the specified view's pin. + @discardableResult + public func pinLeftCenter(_ pin: Pin, of relativeView: UIView) -> Layout { + func context() -> String { return "leftCenter(\(pin.rawValue), of: \(view))" } + if let coordinates = computeCoordinates(forPin: pin, of: relativeView, context: context) { + setLeftCenter(coordinates, context: context) + } + return self + } + + /// Position on the leftCenter corner of the specified view. + @discardableResult + public func pinLeftCenter(to relativeView: UIView) -> Layout { + func context() -> String { return "leftCenter(of: \(view))" } + if let coordinates = computeCoordinates(forPin: .leftCenter, of: relativeView, context: context) { + setLeftCenter(coordinates, context: context) + } + return self + } + + /// Position on the leftCenter corner of its parent. + @discardableResult + public func pinLeftCenter() -> Layout { + func context() -> String { return "leftCenter()" } + if let layoutSuperview = validateLayoutSuperview(context: context) { + if let coordinates = computeCoordinates(forPin: .leftCenter, of: layoutSuperview, context: context) { + setLeftCenter(coordinates, context: context) + } + } + return self + } @discardableResult - public func rightCenter(_ point: CGPoint) -> Layout { - return rightCenter(x: point.x, y: point.y) + public func pinCenter(to point: CGPoint) -> Layout { + return setCenter(point, context: { return "centers(CGPoint(x: \(point.x), y: \(point.y)))" }) + } + + /// Position the centers on the specified view's pin. + @discardableResult + public func pinCenter(_ pin: Pin, of relativeView: UIView) -> Layout { + func context() -> String { return "centers(\(pin.rawValue), of: \(view))" } + if let coordinates = computeCoordinates(forPin: pin, of: relativeView, context: context) { + setCenter(coordinates, context: context) + } + return self + } + + @discardableResult + public func pinCenter(to relativeView: UIView) -> Layout { + func context() -> String { return "centers(of: \(view))" } + if let coordinates = computeCoordinates(forPin: Pin.center, of: relativeView, context: context) { + setCenter(coordinates, context: context) + } + return self + } + + @discardableResult + public func pinCenter() -> Layout { + func context() -> String { return "center()" } + if let layoutSuperview = validateLayoutSuperview(context: context) { + if let coordinates = computeCoordinates(forPin: .center, of: layoutSuperview, context: context) { + setCenter(coordinates, context: context) + } + } + return self + } + + @discardableResult + public func pinRightCenter(to point: CGPoint) -> Layout { + return setRightCenter(point, context: { return "rightCenter(CGPoint(x: \(point.x), y: \(point.y)))" }) + } + + /// Position the rightCenter on the specified view's pin. + @discardableResult + public func pinRightCenter(_ pin: Pin, of relativeView: UIView) -> Layout { + func context() -> String { return "rightCenter(\(pin.rawValue), of: \(view))" } + if let coordinates = computeCoordinates(forPin: pin, of: relativeView, context: context) { + setRightCenter(coordinates, context: context) + } + return self + } + + /// Position on the rightCenter corner of the specified view. + @discardableResult + public func pinRightCenter(to relativeView: UIView) -> Layout { + func context() -> String { return "rightCenter(of: \(view))" } + if let coordinates = computeCoordinates(forPin: .rightCenter, of: relativeView, context: context) { + setRightCenter(coordinates, context: context) + } + return self + } + + /// Position on the rightCenter corner of its parent. + @discardableResult + public func pinRightCenter() -> Layout { + func context() -> String { return "rightCenter()" } + if let layoutSuperview = validateLayoutSuperview(context: context) { + if let coordinates = computeCoordinates(forPin: .rightCenter, of: layoutSuperview, context: context) { + setRightCenter(coordinates, context: context) + } + } + return self } @discardableResult - public func rightCenter(x: CGFloat, y: CGFloat) -> Layout { - return setRightCenter(x: x, y: y, setterContext: "rightCenter(\(x), \(y))") + public func pinBottomLeft(to point: CGPoint) -> Layout { + return setBottomLeft(point, context: { return "bottomLeft(CGPoint(x: \(point.x), y: \(point.y)))" }) } + /// Position the bottomLeft on the specified view's pin. @discardableResult - public func bottomLeft(_ point: CGPoint) -> Layout { - return bottomLeft(x: point.x, y: point.y) + public func pinBottomLeft(_ pin: Pin, of relativeView: UIView) -> Layout { + func context() -> String { return "bottomLeft(\(pin.rawValue), of: \(view))" } + if let coordinates = computeCoordinates(forPin: pin, of: relativeView, context: context) { + setBottomLeft(coordinates, context: context) + } + return self } + /// Position on the bottomLeft corner of the specified view. @discardableResult - public func bottomLeft(x: CGFloat, y: CGFloat) -> Layout { - return setBottomLeft(x: x, y: y, setterContext: "bottomLeft(\(x), \(y))") + public func pinBottomLeft(to relativeView: UIView) -> Layout { + func context() -> String { return "bottomLeft(of: \(view))" } + if let coordinates = computeCoordinates(forPin: Pin.bottomLeft, of: relativeView, context: context) { + setBottomLeft(coordinates, context: context) + } + return self + } + + /// Position on the bottomLeft corner of its parent. + @discardableResult + public func pinBottomLeft() -> Layout { + func context() -> String { return "bottomLeft()" } + if let layoutSuperview = validateLayoutSuperview(context: context) { + if let coordinates = computeCoordinates(forPin: .bottomLeft, of: layoutSuperview, context: context) { + setBottomLeft(coordinates, context: context) + } + } + return self } @discardableResult - public func bottomCenter(_ point: CGPoint) -> Layout { - return bottomCenter(x: point.x, y: point.y) + public func pinBottomCenter(to point: CGPoint) -> Layout { + return setBottomCenter(point, context: { return "bottomCenter(CGPoint(x: \(point.x), y: \(point.y)))" }) } + /// Position the bottomCenter on the specified view's pin. @discardableResult - public func bottomCenter(x: CGFloat, y: CGFloat) -> Layout { - return setBottomCenter(x: x, y: y, setterContext: "bottomCenter(\(x), \(y))") + public func pinBottomCenter(_ pin: Pin, of relativeView: UIView) -> Layout { + func context() -> String { return "bottomCenter(\(pin.rawValue), of: \(view))" } + if let coordinates = computeCoordinates(forPin: pin, of: relativeView, context: context) { + setBottomCenter(coordinates, context: context) + } + return self } + /// Position on the bottomCenter corner of the specified view. @discardableResult - public func bottomRight(_ point: CGPoint) -> Layout { - return bottomRight(x: point.x, y: point.y) + public func pinBottomCenter(to relativeView: UIView) -> Layout { + func context() -> String { return "bottomCenter(of: \(view))" } + if let coordinates = computeCoordinates(forPin: Pin.bottomCenter, of: relativeView, context: context) { + setBottomCenter(coordinates, context: context) + } + return self } + /// Position on the bottomCenter corner of its parent. + @discardableResult + public func pinBottomCenter() -> Layout { + func context() -> String { return "bottomCenter()" } + if let layoutSuperview = validateLayoutSuperview(context: context) { + if let coordinates = computeCoordinates(forPin: .bottomCenter, of: layoutSuperview, context: context) { + setBottomCenter(coordinates, context: context) + } + } + return self + } + @discardableResult - public func bottomRight(x: CGFloat, y: CGFloat) -> Layout { - return setBottomRight(x: x, y: y, setterContext: "bottomRight(\(x), \(y))") + public func pinBottomRight(to point: CGPoint) -> Layout { + return setBottomRight(point, context: { return "bottomRight(CGPoint(x: \(point.x), y: \(point.y)))" }) } + /// Position the bottomRight on the specified view's pin. + @discardableResult + public func pinBottomRight(_ pin: Pin, of relativeView: UIView) -> Layout { + func context() -> String { return "bottomRight(\(pin.rawValue), of: \(view))" } + if let coordinates = computeCoordinates(forPin: pin, of: relativeView, context: context) { + setBottomRight(coordinates, context: context) + } + return self + } + + /// Position on the bottomRight corner of the specified view. + @discardableResult + public func pinBottomRight(to relativeView: UIView) -> Layout { + func context() -> String { return "bottomRight(of: \(view))" } + if let coordinates = computeCoordinates(forPin: .bottomRight, of: relativeView, context: context) { + setBottomRight(coordinates, context: context) + } + return self + } + + /// Position on the bottomRight corner of its parent. + @discardableResult + public func pinBottomRight() -> Layout { + func context() -> String { return "bottomRight()" } + if let layoutSuperview = validateLayoutSuperview(context: context) { + if let coordinates = computeCoordinates(forPin: .bottomRight, of: layoutSuperview, context: context) { + setBottomRight(coordinates, context: context) + } + } + return self + } + // RELATIVE POSITION public enum HorizontalAlignment { case left @@ -319,62 +665,66 @@ public class Layout { case bottom } + /// Set the view's bottom coordinate above of the specified view. @discardableResult public func above(of view: UIView) -> Layout { - setBottomCoordinate(view.top, setterContext: "above(of: \(view))") + setBottom(view.top, context: { return "above(of: \(view))" }) return self } @discardableResult public func above(of view: UIView, aligned: HorizontalAlignment) -> Layout { switch aligned { - case .left: return setBottomLeft(x: view.topLeft.x, y: view.topLeft.y, setterContext: "above(of: \(view), aligned: .left)") - case .center: return setBottomCenter(x: view.topCenter.x, y: view.topCenter.y, setterContext: "above(of: \(view), aligned: .center)") - case .right: return setBottomRight(x: view.topRight.x, y: view.topRight.y, setterContext: "above(of: \(view), aligned: .right)") + case .left: return setBottomLeft(view.topLeft, context: { return "above(of: \(view), aligned: .left)" }) + case .center: return setBottomCenter(view.topCenter, context: { return "above(of: \(view), aligned: .center)" }) + case .right: return setBottomRight(view.topRight, context: { return "above(of: \(view), aligned: .right)" }) } } + /// Set the view's top coordinate below of the specified view. @discardableResult public func below(of view: UIView) -> Layout { - setTopCoordinate(view.bottom, setterContext: "below(of: \(view))") + setTop(view.bottom, context: { return "below(of: \(view))" }) return self } @discardableResult public func below(of view: UIView, aligned: HorizontalAlignment) -> Layout { switch aligned { - case .left: return setTopLeft(x: view.bottomLeft.x, y: view.bottomLeft.y, setterContext: "below(of: \(view), aligned: .left)") - case .center: return setTopCenter(x: view.bottomCenter.x, y: view.bottomCenter.y, setterContext: "below(of: \(view), aligned: .center)") - case .right: return setTopRight(x: view.bottomRight.x, y: view.bottomRight.y, setterContext: "below(of: \(view), aligned: .right)") + case .left: return setTopLeft(view.bottomLeft, context: { return "below(of: \(view), aligned: .left)" }) + case .center: return setTopCenter(view.bottomCenter, context: { return "below(of: \(view), aligned: .center)" }) + case .right: return setTopRight(view.bottomRight, context: { return "below(of: \(view), aligned: .right)" }) } } + /// Set the view's right coordinate left of the specified view. @discardableResult public func left(of view: UIView) -> Layout { - setRightCoordinate(view.left, setterContext: "left(of: \(view))") + setRight(view.left, context: { return "left(of: \(view))" }) return self } @discardableResult public func left(of view: UIView, aligned: VerticalAlignment) -> Layout { switch aligned { - case .top: return setTopRight(x: view.topLeft.x, y: view.topLeft.y, setterContext: "left(of: \(view), aligned: .top)") - case .center: return setRightCenter(x: view.leftCenter.x, y: view.leftCenter.y, setterContext: "left(of: \(view), aligned: .center)") - case .bottom: return setBottomRight(x: view.bottomLeft.x, y: view.bottomLeft.y, setterContext: "left(of: \(view), aligned: .bottom)") + case .top: return setTopRight(view.topLeft, context: { return "left(of: \(view), aligned: .top)" }) + case .center: return setRightCenter(view.leftCenter, context: { return "left(of: \(view), aligned: .center)" }) + case .bottom: return setBottomRight(view.bottomLeft, context: { return "left(of: \(view), aligned: .bottom)" }) } } + /// Set the view's left coordinate right of the specified view. @discardableResult public func right(of view: UIView) -> Layout { - setLeftCoordinate(view.right, setterContext: "right(of: \(view))") + setLeft(view.right, context: { return "right(of: \(view))" }) return self } @discardableResult public func right(of view: UIView, aligned: VerticalAlignment) -> Layout { switch aligned { - case .top: return setTopLeft(x: view.topRight.x, y: view.topRight.y, setterContext: "right(of: \(view), aligned: .top)") - case .center: return setLeftCenter(x: view.rightCenter.x, y: view.rightCenter.y, setterContext: "right(of: \(view), aligned: .center)") - case .bottom: return setBottomLeft(x: view.bottomRight.x, y: view.bottomRight.y, setterContext: "right(of: \(view), aligned: .bottom)") + case .top: return setTopLeft(view.topRight, context: { return "right(of: \(view), aligned: .top)" }) + case .center: return setLeftCenter(view.rightCenter, context: { return "right(of: \(view), aligned: .center)" }) + case .bottom: return setBottomLeft(view.bottomRight, context: { return "right(of: \(view), aligned: .bottom)" }) } } @@ -383,27 +733,27 @@ public class Layout { // @discardableResult public func width(_ width: CGFloat) -> Layout { - return setWidth(width, setterContext: "width(\(width))") + return setWidth(width, context: { return "width(\(width))" }) } @discardableResult public func width(of view: UIView) -> Layout { - return setWidth(view.width, setterContext: "width(of: \(view))") + return setWidth(view.width, context: { return "width(of: \(view))" }) } @discardableResult public func height(_ height: CGFloat) -> Layout { - return setHeight(height, setterContext: "height(\(height))") + return setHeight(height, context: { return "height(\(height))" }) } @discardableResult public func height(of view: UIView) -> Layout { - return setHeight(view.height, setterContext: "height(of: \(view))") + return setHeight(view.height, context: { return "height(of: \(view))" }) } @discardableResult public func size(_ size: CGSize) -> Layout { - if isSizeNotSet(setterContext: "size(CGSize(width: \(size.width), height: \(size.height)))") { + if isSizeNotSet(context: { return "size(CGSize(width: \(size.width), height: \(size.height)))" }) { width(size.width) height(size.height) } @@ -412,18 +762,19 @@ public class Layout { @discardableResult public func size(of view: UIView) -> Layout { + func context() -> String { return "size(of \(view))" } let viewSize = view.frame.size - if isSizeNotSet(setterContext: "size(of \(viewSize))") { - setWidth(viewSize.width) - setHeight(view.height) + if isSizeNotSet(context: context) { + setWidth(viewSize.width, context: context) + setHeight(view.height, context: context) } return self } @discardableResult public func sizeThatFits(size: CGSize) -> Layout { - if isSizeNotSet(setterContext: "sizeThatFits(CGSize(width: \(size.width), height: \(size.height)))") { + if isSizeNotSet(context: { return "sizeThatFits(CGSize(width: \(size.width), height: \(size.height)))" }) { fitSize = size } return self @@ -431,7 +782,7 @@ public class Layout { @discardableResult public func sizeThatFits(sizeOf view: UIView) -> Layout { - if isSizeNotSet(setterContext: "sizeThatFitsViewSize(\(view))") { + if isSizeNotSet(context: { return "sizeThatFitsViewSize(\(view))" }) { fitSize = view.size } return self @@ -439,7 +790,7 @@ public class Layout { @discardableResult public func sizeThatFits(width: CGFloat) -> Layout { - if isSizeNotSet(setterContext: "sizeThatFitsWidth(\(width))") { + if isSizeNotSet(context: { return "sizeThatFitsWidth(\(width))" }) { if fitSize == nil { fitSize = CGSize(width: width, height: .greatestFiniteMagnitude) } else { @@ -451,7 +802,7 @@ public class Layout { @discardableResult public func sizeThatFits(widthOf view: UIView) -> Layout { - if isSizeNotSet(setterContext: "sizeThatFitsWidth(of: \(view))") { + if isSizeNotSet(context: { return "sizeThatFitsWidth(of: \(view))" }) { sizeThatFits(width: view.size.width) } return self @@ -459,7 +810,7 @@ public class Layout { @discardableResult public func sizeThatFits(height: CGFloat) -> Layout { - if isSizeNotSet(setterContext: "sizeThatFitsHeight(\(height))") { + if isSizeNotSet(context: { return "sizeThatFitsHeight(\(height))" }) { if fitSize == nil { fitSize = CGSize(width: .greatestFiniteMagnitude, height: height) } else { @@ -471,7 +822,7 @@ public class Layout { @discardableResult public func sizeThatFits(heightOf view: UIView) -> Layout { - if isSizeNotSet(setterContext: "sizeThatFitsHeightof: \(view))") { + if isSizeNotSet(context: { return "sizeThatFitsHeightof: \(view))" }) { sizeThatFits(height: view.size.height) } return self @@ -598,7 +949,7 @@ public class Layout { } fileprivate func apply() { - guard let view = view else { return } + //guard let view = view else { return } apply(onView: view) } @@ -623,7 +974,7 @@ public class Layout { } else if bottom != nil { // bottom is set => adjust the origin and the height newRect.origin.y = applyMarginsAndInsets(top: top) - newRect.size.height = applyTopBottomInsets(bottom! - top) + newRect.size.height = applyMarginsAndInsets(bottom: bottom! - newRect.origin.y) } else if height != nil { // height is set => adjust the origin and the height newRect.origin.y = applyMarginsAndInsets(top: top) @@ -676,7 +1027,7 @@ public class Layout { } else if right != nil { // right is set => adjust the origin and the width newRect.origin.x = applyMarginsAndInsets(left: left) - newRect.size.width = right! - newRect.origin.x - (rightMargin ?? 0) - (rightInset ?? 0) + newRect.size.width = applyMarginsAndInsets(right: right!) - newRect.origin.x//! - newRect.origin.x - (rightMargin ?? 0) - (rightInset ?? 0) } else if width != nil { // width is set => adjust the origin and the height newRect.origin.x = applyMarginsAndInsets(left: left) @@ -726,145 +1077,153 @@ public class Layout { // MARK: Private methods extension Layout { - fileprivate func setTopCoordinate(_ value: CGFloat, setterContext: @autoclosure () -> String) { + fileprivate func setTop(_ value: CGFloat, context: Context) { if bottom != nil && height != nil { - warnConflict(setterContext, ["bottom": bottom!, "height": height!]) + warnConflict(context, ["bottom": bottom!, "height": height!]) } else if vCenter != nil { - warnConflict(setterContext, ["Vertical Center": vCenter!]) + warnConflict(context, ["Vertical Center": vCenter!]) } else if top != nil { - warnPropertyAlreadySet("top", propertyValue: top!, setterContext: setterContext) + warnPropertyAlreadySet("top", propertyValue: top!, context: context) } else { top = value } } - fileprivate func setLeftCoordinate(_ value: CGFloat, setterContext: @autoclosure () -> String) { + fileprivate func setLeft(_ value: CGFloat, context: Context) { if right != nil && width != nil { - warnConflict(setterContext, ["right": right!, "width": width!]) + warnConflict(context, ["right": right!, "width": width!]) } else if hCenter != nil { - warnConflict(setterContext, ["Horizontal Center": hCenter!]) + warnConflict(context, ["Horizontal Center": hCenter!]) } else if left != nil { - warnPropertyAlreadySet("left", propertyValue: left!, setterContext: setterContext) + warnPropertyAlreadySet("left", propertyValue: left!, context: context) } else { left = value } } - fileprivate func setRightCoordinate(_ value: CGFloat, setterContext: @autoclosure () -> String) { + fileprivate func setRight(_ value: CGFloat, context: Context) { if left != nil && width != nil { - warnConflict(setterContext, ["left": left!, "width": width!]) + warnConflict(context, ["left": left!, "width": width!]) } else if hCenter != nil { - warnConflict(setterContext, ["Horizontal Center": hCenter!]) + warnConflict(context, ["Horizontal Center": hCenter!]) } else if right != nil { - warnPropertyAlreadySet("right", propertyValue: right!, setterContext: setterContext) + warnPropertyAlreadySet("right", propertyValue: right!, context: context) } else { right = value } } - fileprivate func setBottomCoordinate(_ value: CGFloat, setterContext: @autoclosure () -> String) { + fileprivate func setBottom(_ value: CGFloat, context: Context) { if top != nil && height != nil { - warnConflict(setterContext, ["top": top!, "height": height!]) + warnConflict(context, ["top": top!, "height": height!]) } else if vCenter != nil { - warnConflict(setterContext, ["Vertical Center": vCenter!]) + warnConflict(context, ["Vertical Center": vCenter!]) } else if bottom != nil { - warnPropertyAlreadySet("bottom", propertyValue: bottom!, setterContext: setterContext) + warnPropertyAlreadySet("bottom", propertyValue: bottom!, context: context) } else { bottom = value } } - fileprivate func setHorizontalCenter(_ value: CGFloat, setterContext: @autoclosure () -> String) { + fileprivate func setHorizontalCenter(_ value: CGFloat, context: Context) { if left != nil { - warnConflict(setterContext, ["left": left!]) + warnConflict(context, ["left": left!]) } else if right != nil { - warnConflict(setterContext, ["right": right!]) + warnConflict(context, ["right": right!]) } else if hCenter != nil { - warnPropertyAlreadySet("hCenter", propertyValue: hCenter!, setterContext: setterContext) + warnPropertyAlreadySet("hCenter", propertyValue: hCenter!, context: context) } else { hCenter = value } } - fileprivate func setVerticalCenter(_ value: CGFloat, setterContext: @autoclosure () -> String) { + fileprivate func setVerticalCenter(_ value: CGFloat, context: Context) { if top != nil { - warnConflict(setterContext, ["top": top!]) + warnConflict(context, ["top": top!]) } else if bottom != nil { - warnConflict(setterContext, ["bottom": bottom!]) + warnConflict(context, ["bottom": bottom!]) } else if vCenter != nil { - warnPropertyAlreadySet("vCenter", propertyValue: vCenter!, setterContext: setterContext) + warnPropertyAlreadySet("vCenter", propertyValue: vCenter!, context: context) } else { vCenter = value } } - fileprivate func setTopLeft(x: CGFloat, y: CGFloat, setterContext: @autoclosure () -> String) -> Layout { - setLeftCoordinate(x, setterContext: setterContext) - setTopCoordinate(y, setterContext: setterContext) + @discardableResult + fileprivate func setTopLeft(_ point: CGPoint, context: Context) -> Layout { + setLeft(point.x, context: context) + setTop(point.y, context: context) + return self + } + + @discardableResult + fileprivate func setTopCenter(_ point: CGPoint, context: Context) -> Layout { + setHorizontalCenter(point.x, context: context) + setTop(point.y, context: context) return self } @discardableResult - fileprivate func setTopCenter(x: CGFloat, y: CGFloat, setterContext: @autoclosure () -> String) -> Layout { - setHorizontalCenter(x, setterContext: setterContext) - setTopCoordinate(y, setterContext: setterContext) + fileprivate func setTopRight(_ point: CGPoint, context: Context) -> Layout { + setRight(point.x, context: context) + setTop(point.y, context: context) return self } @discardableResult - fileprivate func setTopRight(x: CGFloat, y: CGFloat, setterContext: @autoclosure () -> String) -> Layout { - setRightCoordinate(x, setterContext: setterContext) - setTopCoordinate(y, setterContext: setterContext) + fileprivate func setLeftCenter(_ point: CGPoint, context: Context) -> Layout { + setLeft(point.x, context: context) + setVerticalCenter(point.y, context: context) return self } @discardableResult - fileprivate func setLeftCenter(x: CGFloat, y: CGFloat, setterContext: @autoclosure () -> String) -> Layout { - setLeftCoordinate(x, setterContext: setterContext) - setVerticalCenter(y, setterContext: setterContext) + fileprivate func setCenter(_ point: CGPoint, context: Context) -> Layout { + setHorizontalCenter(point.x, context: context) + setVerticalCenter(point.y, context: context) return self } @discardableResult - fileprivate func setRightCenter(x: CGFloat, y: CGFloat, setterContext: @autoclosure () -> String) -> Layout { - setRightCoordinate(x, setterContext: setterContext) - setVerticalCenter(y, setterContext: setterContext) + fileprivate func setRightCenter(_ point: CGPoint, context: Context) -> Layout { + setRight(point.x, context: context) + setVerticalCenter(point.y, context: context) return self } @discardableResult - fileprivate func setBottomLeft(x: CGFloat, y: CGFloat, setterContext: @autoclosure () -> String) -> Layout { - setLeftCoordinate(x, setterContext: setterContext) - setBottomCoordinate(y, setterContext: setterContext) + fileprivate func setBottomLeft(_ point: CGPoint, context: Context) -> Layout { + setLeft(point.x, context: context) + setBottom(point.y, context: context) return self } @discardableResult - fileprivate func setBottomCenter(x: CGFloat, y: CGFloat, setterContext: @autoclosure () -> String) -> Layout { - setHorizontalCenter(x, setterContext: setterContext) - setBottomCoordinate(y, setterContext: setterContext) + fileprivate func setBottomCenter(_ point: CGPoint, context: Context) -> Layout { + setHorizontalCenter(point.x, context: context) + setBottom(point.y, context: context) return self } @discardableResult - fileprivate func setBottomRight(x: CGFloat, y: CGFloat, setterContext: @autoclosure () -> String) -> Layout { - setRightCoordinate(x, setterContext: setterContext) - setBottomCoordinate(y, setterContext: setterContext) + fileprivate func setBottomRight(_ point: CGPoint, context: Context) -> Layout { + setRight(point.x, context: context) + setBottom(point.y, context: context) return self } @discardableResult - fileprivate func setWidth(_ value: CGFloat, setterContext: @autoclosure () -> String = "") -> Layout { + fileprivate func setWidth(_ value: CGFloat, context: Context) -> Layout { guard value >= 0 else { - warn("The width (\(value)) must be greater or equal to 0.", setterContext: setterContext); return self + warn("The width (\(value)) must be greater or equal to 0.", context: context); return self } if left != nil && right != nil { - warnConflict(setterContext, ["left": left!, "right": right!]) + warnConflict(context, ["left": left!, "right": right!]) } else if fitSize != nil { - warnConflict(setterContext, ["fitSize.width": fitSize!.width, "fitSize.height": fitSize!.height]) + warnConflict(context, ["fitSize.width": fitSize!.width, "fitSize.height": fitSize!.height]) } else if width != nil { - warnPropertyAlreadySet("width", propertyValue: width!, setterContext: setterContext) + warnPropertyAlreadySet("width", propertyValue: width!, context: context) } else { width = value } @@ -872,49 +1231,123 @@ extension Layout { } @discardableResult - fileprivate func setHeight(_ value: CGFloat, setterContext: @autoclosure () -> String = "") -> Layout { + fileprivate func setHeight(_ value: CGFloat, context: Context) -> Layout { guard value >= 0 else { - warn("The height (\(value)) must be greater or equal to 0.", setterContext: setterContext); return self + warn("The height (\(value)) must be greater or equal to 0.", context: context); return self } if top != nil && bottom != nil { - warnConflict(setterContext, ["top": top!, "bottom": bottom!]) + warnConflict(context, ["top": top!, "bottom": bottom!]) } else if fitSize != nil { - warnConflict(setterContext, ["fitSize.width": fitSize!.width, "fitSize.height": fitSize!.height]) + warnConflict(context, ["fitSize.width": fitSize!.width, "fitSize.height": fitSize!.height]) } else if height != nil { - warnPropertyAlreadySet("height", propertyValue: height!, setterContext: setterContext) + warnPropertyAlreadySet("height", propertyValue: height!, context: context) } else { height = value } return self } - fileprivate func isSizeNotSet(setterContext: String) -> Bool { + fileprivate func isSizeNotSet(context: Context) -> Bool { if top != nil && bottom != nil { - warnConflict(setterContext, ["top": top!, "bottom": bottom!]) + warnConflict(context, ["top": top!, "bottom": bottom!]) return false } else if height != nil { - warnConflict(setterContext, ["height": height!]) + warnConflict(context, ["height": height!]) return false } else if width != nil { - warnConflict(setterContext, ["width": width!]) + warnConflict(context, ["width": width!]) return false } else if fitSize != nil { - warnPropertyAlreadySet("fitSize", propertyValue: fitSize!, setterContext: setterContext) + warnPropertyAlreadySet("fitSize", propertyValue: fitSize!, context: context) return false } else { return true } } + fileprivate func computeCoordinates(forPin pin: Pin, of relativeView: UIView, context: Context) -> CGPoint? { + guard let layoutSuperview = validateLayoutSuperview(context: context) else { return nil } + guard let relativeSuperview = relativeView.superview else { warn("relative view's superview is nil", context: context); return nil } + + return computeCoordinates(point(forPin: pin, of: relativeView), layoutSuperview, relativeView, relativeSuperview) + } + + fileprivate func computeCoordinates(_ point: CGPoint, _ layoutSuperview: UIView, _ relativeView: UIView, _ relativeSuperview: UIView) -> CGPoint { + if layoutSuperview == relativeSuperview { + return point // same parent => no coordinates conversion required. + } else { + return relativeSuperview.convert(point, to: layoutSuperview) + } + } + + fileprivate func point(forPin pin: Pin, of view: UIView) -> CGPoint { + switch pin { + case .topLeft: return view.topLeft + case .topCenter: return view.topCenter + case .topRight: return view.topRight + case .leftCenter: return view.leftCenter + case .center: return view.center + case .rightCenter: return view.rightCenter + case .bottomLeft: return view.bottomLeft + case .bottomCenter: return view.bottomCenter + case .bottomRight: return view.bottomRight + } + } + + fileprivate func computeCoordinates(forEdge edge: VerticalEdge, of relativeView: UIView, context: Context) -> CGFloat? { + guard let layoutSuperview = validateLayoutSuperview(context: context) else { return nil } + guard let relativeSuperview = relativeView.superview else { warn("relative view's superview is nil", context: context); return nil } + + return computeCoordinates(coordinate(forEdge: edge, of: relativeView), layoutSuperview, relativeView, relativeSuperview).y + } + + fileprivate func coordinate(forEdge edge: VerticalEdge, of view: UIView) -> CGPoint { + switch edge { + case .top: return CGPoint(x: 0, y: view.top) + case .bottom: return CGPoint(x: 0, y: view.bottom) + } + } + + fileprivate func computeCoordinates(forEdge edge: HorizontalEdge, of relativeView: UIView, context: Context) -> CGFloat? { + guard let layoutSuperview = validateLayoutSuperview(context: context) else { return nil } + guard let relativeSuperview = relativeView.superview else { warn("relative view's superview is nil", context: context); return nil } + + return computeCoordinates(coordinate(forEdge: edge, of: relativeView), layoutSuperview, relativeView, relativeSuperview).x + } + + fileprivate func coordinate(forEdge edge: HorizontalEdge, of view: UIView) -> CGPoint { + switch edge { + case .left: return CGPoint(x: view.left, y: 0) + case .right: return CGPoint(x: view.right, y: 0) + } + } + + fileprivate func validateLayoutSuperview(context: Context) -> UIView? { + if let parentView = view.superview { + return parentView + } else { + warn("Layout's view must be added to a UIView before being layouted using this method.", context: context) + return nil + } + } + fileprivate func applyMarginsAndInsets(top: CGFloat) -> CGFloat { return top + (topMargin ?? 0) + (topInset ?? 0) } + fileprivate func applyMarginsAndInsets(bottom: CGFloat) -> CGFloat { + return bottom - (bottomMargin ?? 0) - (bottomInset ?? 0) + } + fileprivate func applyMarginsAndInsets(left: CGFloat) -> CGFloat { return left + (leftMargin ?? 0) + (leftInset ?? 0) } + fileprivate func applyMarginsAndInsets(right: CGFloat) -> CGFloat { + return right - (rightMargin ?? 0) - (rightInset ?? 0) + } + fileprivate func applyTopBottomInsets(_ height: CGFloat) -> CGFloat { return height - (topInset ?? 0) - (bottomInset ?? 0) } @@ -923,23 +1356,23 @@ extension Layout { return width - (leftInset ?? 0) - (rightInset ?? 0) } - fileprivate func warn(_ text: String, setterContext: @autoclosure () -> String) { + fileprivate func warn(_ text: String, context: Context) { guard Layout.logConflicts else { return } - print("\n\(text)(\(setterContext()))\n") + print("\n\(text)(\(context()))\n") } - fileprivate func warnPropertyAlreadySet(_ propertyName: String, propertyValue: CGFloat, setterContext: @autoclosure () -> String) { + fileprivate func warnPropertyAlreadySet(_ propertyName: String, propertyValue: CGFloat, context: Context) { guard Layout.logConflicts else { return } - print("\nLayou2: The \(propertyName) property has already been set to \(propertyValue). (\(setterContext()))\n") + print("\nLayou2: The \(propertyName) property has already been set to \(propertyValue). (\(context()))\n") } - fileprivate func warnPropertyAlreadySet(_ propertyName: String, propertyValue: CGSize, setterContext: @autoclosure () -> String) { - print("\nLayou2: The \(propertyName) property has already been set to CGSize(width: \(propertyValue.width), height: \(propertyValue.height)). (\(setterContext()))\n") + fileprivate func warnPropertyAlreadySet(_ propertyName: String, propertyValue: CGSize, context: Context) { + print("\nLayou2: The \(propertyName) property has already been set to CGSize(width: \(propertyValue.width), height: \(propertyValue.height)). (\(context()))\n") } - fileprivate func warnConflict(_ setterContext: @autoclosure () -> String, _ properties: [String: CGFloat]) { + fileprivate func warnConflict(_ context: Context, _ properties: [String: CGFloat]) { guard Layout.logConflicts else { return } - var warning = "\nLayout Conflict: '\(setterContext())' won't be applied since it conflicts with the following already set properties:\n" + var warning = "\nLayout Conflict: '\(context())' won't be applied since it conflicts with the following already set properties:\n" properties.forEach { (key, value) in warning += " \(key): \(value)\n" } diff --git a/MCSwiftLayoutSample/.swiftlint.yml b/MCSwiftLayoutSample/.swiftlint.yml new file mode 100644 index 00000000..316f7348 --- /dev/null +++ b/MCSwiftLayoutSample/.swiftlint.yml @@ -0,0 +1,34 @@ +opt_in_rules: # some rules are only opt-in + - attributes + - closure_end_indentation + - closure_spacing + - empty_count + - explicit_init + - nimble_operator + - number_separator + - operator_usage_whitespace + - overridden_super_call + - private_outlet + - prohibited_super_call + - redundant_nil_coalescing + +disabled_rules: # rule identifiers to exclude from running +# - function_body_length + - trailing_whitespace + - force_cast + - type_name +# - todo +# - file_length +# - type_body_length +# - valid_docs +# - cyclomatic_complexity +# - nesting +# - function_parameter_count +# - large_tuple +# - variable_name +# - empty_parentheses_with_trailing_closure + - line_length + +excluded: # paths to ignore during linting. overridden by `included`. + - Pods + diff --git a/MCSwiftLayoutSample/MCSwiftLayoutSample.xcodeproj/project.pbxproj b/MCSwiftLayoutSample/MCSwiftLayoutSample.xcodeproj/project.pbxproj index 3f46d19b..be9e863e 100644 --- a/MCSwiftLayoutSample/MCSwiftLayoutSample.xcodeproj/project.pbxproj +++ b/MCSwiftLayoutSample/MCSwiftLayoutSample.xcodeproj/project.pbxproj @@ -29,8 +29,11 @@ 249EFE431E64FAFE00165E39 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 249EFE421E64FAFE00165E39 /* AppDelegate.swift */; }; 249EFE4A1E64FAFE00165E39 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 249EFE491E64FAFE00165E39 /* Assets.xcassets */; }; 249EFE4D1E64FAFE00165E39 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 249EFE4B1E64FAFE00165E39 /* LaunchScreen.storyboard */; }; - 249EFE581E64FAFE00165E39 /* MCSwiftLayoutSampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 249EFE571E64FAFE00165E39 /* MCSwiftLayoutSampleTests.swift */; }; 249EFE631E64FAFE00165E39 /* MCSwiftLayoutSampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 249EFE621E64FAFE00165E39 /* MCSwiftLayoutSampleUITests.swift */; }; + 24E6547A1E68F27D00A72A8B /* BothEdgesSnappedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E654781E68F27D00A72A8B /* BothEdgesSnappedView.swift */; }; + 24E6547B1E68F27D00A72A8B /* BothEdgesSnappedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E654791E68F27D00A72A8B /* BothEdgesSnappedViewController.swift */; }; + 24E654821E69041B00A72A8B /* Expect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E654811E69041B00A72A8B /* Expect.swift */; }; + DE6C3D736B571B80E207DF6A /* Pods_MCSwiftLayoutSample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AAD69688AA2A3F0994F3074E /* Pods_MCSwiftLayoutSample.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -110,11 +113,16 @@ 249EFE4C1E64FAFE00165E39 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 249EFE4E1E64FAFE00165E39 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 249EFE531E64FAFE00165E39 /* MCSwiftLayoutSampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MCSwiftLayoutSampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 249EFE571E64FAFE00165E39 /* MCSwiftLayoutSampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCSwiftLayoutSampleTests.swift; sourceTree = ""; }; 249EFE591E64FAFE00165E39 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 249EFE5E1E64FAFE00165E39 /* MCSwiftLayoutSampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MCSwiftLayoutSampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 249EFE621E64FAFE00165E39 /* MCSwiftLayoutSampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCSwiftLayoutSampleUITests.swift; sourceTree = ""; }; 249EFE641E64FAFE00165E39 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 24E654781E68F27D00A72A8B /* BothEdgesSnappedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BothEdgesSnappedView.swift; path = Test/BothEdgesSnapped/BothEdgesSnappedView.swift; sourceTree = ""; }; + 24E654791E68F27D00A72A8B /* BothEdgesSnappedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BothEdgesSnappedViewController.swift; path = Test/BothEdgesSnapped/BothEdgesSnappedViewController.swift; sourceTree = ""; }; + 24E654811E69041B00A72A8B /* Expect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Expect.swift; path = Domain/Expect.swift; sourceTree = ""; }; + AAD69688AA2A3F0994F3074E /* Pods_MCSwiftLayoutSample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MCSwiftLayoutSample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F121E291CADD796B007C04BB /* Pods-MCSwiftLayoutSample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MCSwiftLayoutSample.release.xcconfig"; path = "Pods/Target Support Files/Pods-MCSwiftLayoutSample/Pods-MCSwiftLayoutSample.release.xcconfig"; sourceTree = ""; }; + F1ACB0EACBD84B57B50D472D /* Pods-MCSwiftLayoutSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MCSwiftLayoutSample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MCSwiftLayoutSample/Pods-MCSwiftLayoutSample.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -123,6 +131,7 @@ buildActionMask = 2147483647; files = ( 2439CC2B1E6658CC003326FB /* MCSwiftLayout.framework in Frameworks */, + DE6C3D736B571B80E207DF6A /* Pods_MCSwiftLayoutSample.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -143,6 +152,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 160FB83905049FCEDD18DC8A /* Frameworks */ = { + isa = PBXGroup; + children = ( + AAD69688AA2A3F0994F3074E /* Pods_MCSwiftLayoutSample.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 2439CC1F1E665858003326FB /* Products */ = { isa = PBXGroup; children = ( @@ -165,6 +182,7 @@ 2439CC371E665C5E003326FB /* Test */ = { isa = PBXGroup; children = ( + 24E654761E68F20400A72A8B /* BothEdgesSnapped */, 2439CC601E665FDE003326FB /* ChainedLayoutView */, 2439CC621E666043003326FB /* MarginsAndPaddingsLeftWidthView */, 2439CC611E665FFD003326FB /* MarginsAndPaddingsLeftRightView */, @@ -274,6 +292,8 @@ 249EFE561E64FAFE00165E39 /* MCSwiftLayoutSampleTests */, 249EFE611E64FAFE00165E39 /* MCSwiftLayoutSampleUITests */, 249EFE401E64FAFE00165E39 /* Products */, + F143180314A617EFD07C5709 /* Pods */, + 160FB83905049FCEDD18DC8A /* Frameworks */, ); sourceTree = ""; }; @@ -290,6 +310,7 @@ 249EFE411E64FAFE00165E39 /* MCSwiftLayoutSample */ = { isa = PBXGroup; children = ( + 24E654801E69040000A72A8B /* Domain */, 249EFE421E64FAFE00165E39 /* AppDelegate.swift */, 2439CC321E665BE3003326FB /* UI */, 2439CC671E66614D003326FB /* Supporting Files */, @@ -300,7 +321,6 @@ 249EFE561E64FAFE00165E39 /* MCSwiftLayoutSampleTests */ = { isa = PBXGroup; children = ( - 249EFE571E64FAFE00165E39 /* MCSwiftLayoutSampleTests.swift */, 249EFE591E64FAFE00165E39 /* Info.plist */, ); path = MCSwiftLayoutSampleTests; @@ -315,6 +335,32 @@ path = MCSwiftLayoutSampleUITests; sourceTree = ""; }; + 24E654761E68F20400A72A8B /* BothEdgesSnapped */ = { + isa = PBXGroup; + children = ( + 24E654781E68F27D00A72A8B /* BothEdgesSnappedView.swift */, + 24E654791E68F27D00A72A8B /* BothEdgesSnappedViewController.swift */, + ); + name = BothEdgesSnapped; + sourceTree = ""; + }; + 24E654801E69040000A72A8B /* Domain */ = { + isa = PBXGroup; + children = ( + 24E654811E69041B00A72A8B /* Expect.swift */, + ); + name = Domain; + sourceTree = ""; + }; + F143180314A617EFD07C5709 /* Pods */ = { + isa = PBXGroup; + children = ( + F1ACB0EACBD84B57B50D472D /* Pods-MCSwiftLayoutSample.debug.xcconfig */, + F121E291CADD796B007C04BB /* Pods-MCSwiftLayoutSample.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -322,10 +368,14 @@ isa = PBXNativeTarget; buildConfigurationList = 249EFE671E64FAFE00165E39 /* Build configuration list for PBXNativeTarget "MCSwiftLayoutSample" */; buildPhases = ( + DDE1EE639E8C959FEBC41FDC /* [CP] Check Pods Manifest.lock */, 249EFE3B1E64FAFE00165E39 /* Sources */, 249EFE3C1E64FAFE00165E39 /* Frameworks */, 249EFE3D1E64FAFE00165E39 /* Resources */, 2460ACCE1E64FD9D000BCAC5 /* Embed Frameworks */, + 5D3C4568AFC08267110D9971 /* [CP] Embed Pods Frameworks */, + CCCCC7EE5AE16BA960D7DB4F /* [CP] Copy Pods Resources */, + 24E6547E1E68F88D00A72A8B /* Run Swiftlint */, ); buildRules = ( ); @@ -471,6 +521,68 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 24E6547E1E68F88D00A72A8B /* Run Swiftlint */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Swiftlint"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = Pods/SwiftLint/swiftlint; + }; + 5D3C4568AFC08267110D9971 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MCSwiftLayoutSample/Pods-MCSwiftLayoutSample-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + CCCCC7EE5AE16BA960D7DB4F /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MCSwiftLayoutSample/Pods-MCSwiftLayoutSample-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + DDE1EE639E8C959FEBC41FDC /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 249EFE3B1E64FAFE00165E39 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -482,6 +594,7 @@ 2439CC551E665C6B003326FB /* RelativeViewController.swift in Sources */, 2439CC4E1E665C6B003326FB /* MarginsAndPaddingsLeftRightView.swift in Sources */, 2439CC351E665BF6003326FB /* MenuView.swift in Sources */, + 24E6547A1E68F27D00A72A8B /* BothEdgesSnappedView.swift in Sources */, 2439CC4F1E665C6B003326FB /* MarginsAndPaddingsLeftRightViewController.swift in Sources */, 2439CC4B1E665C6B003326FB /* BasicView.swift in Sources */, 2439CC561E665C6B003326FB /* ViewExtensionsPositionningView.swift in Sources */, @@ -489,7 +602,9 @@ 2439CC4C1E665C6B003326FB /* ChainedLayoutView.swift in Sources */, 2439CC501E665C6B003326FB /* MarginsAndPaddingsLeftWidthView.swift in Sources */, 2439CC361E665BF6003326FB /* MenuViewController.swift in Sources */, + 24E6547B1E68F27D00A72A8B /* BothEdgesSnappedViewController.swift in Sources */, 2439CC531E665C6B003326FB /* MultiRelativeViewController.swift in Sources */, + 24E654821E69041B00A72A8B /* Expect.swift in Sources */, 2439CC581E665C6B003326FB /* ValidateConflictsView.swift in Sources */, 2439CC4D1E665C6B003326FB /* ChainedLayoutViewController.swift in Sources */, 2439CC511E665C6B003326FB /* MarginsAndPaddingsLeftWidthViewController.swift in Sources */, @@ -501,7 +616,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 249EFE581E64FAFE00165E39 /* MCSwiftLayoutSampleTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -640,6 +754,7 @@ }; 249EFE681E64FAFE00165E39 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = F1ACB0EACBD84B57B50D472D /* Pods-MCSwiftLayoutSample.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -654,6 +769,7 @@ }; 249EFE691E64FAFE00165E39 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = F121E291CADD796B007C04BB /* Pods-MCSwiftLayoutSample.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; diff --git a/MCSwiftLayoutSample/MCSwiftLayoutSample/AppDelegate.swift b/MCSwiftLayoutSample/MCSwiftLayoutSample/AppDelegate.swift index 46a1ef39..b1deee38 100644 --- a/MCSwiftLayoutSample/MCSwiftLayoutSample/AppDelegate.swift +++ b/MCSwiftLayoutSample/MCSwiftLayoutSample/AppDelegate.swift @@ -19,8 +19,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { window!.backgroundColor = UIColor.white window!.rootViewController = UINavigationController(rootViewController: MenuViewController()) window!.makeKeyAndVisible() - + return true } } - diff --git a/MCSwiftLayoutSample/MCSwiftLayoutSample/BasicView.swift b/MCSwiftLayoutSample/MCSwiftLayoutSample/BasicView.swift index c5358da9..36862624 100644 --- a/MCSwiftLayoutSample/MCSwiftLayoutSample/BasicView.swift +++ b/MCSwiftLayoutSample/MCSwiftLayoutSample/BasicView.swift @@ -7,17 +7,28 @@ // import UIKit -class BasicView: UILabel { +class BasicView: UIView { + fileprivate let label = UILabel() + init(text: String? = nil, color: UIColor) { super.init(frame: .zero) - self.text = text - font = UIFont.systemFont(ofSize: 7) - textColor = .white backgroundColor = color + + label.text = text + label.font = UIFont.systemFont(ofSize: 7) + label.textColor = .white + label.sizeToFit() + addSubview(label) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + override func layoutSubviews() { + super.layoutSubviews() + + label.layout.top(0).left(0) + } } diff --git a/MCSwiftLayoutSample/MCSwiftLayoutSample/ChainedLayoutView.swift b/MCSwiftLayoutSample/MCSwiftLayoutSample/ChainedLayoutView.swift index e211db32..38928b22 100644 --- a/MCSwiftLayoutSample/MCSwiftLayoutSample/ChainedLayoutView.swift +++ b/MCSwiftLayoutSample/MCSwiftLayoutSample/ChainedLayoutView.swift @@ -75,13 +75,13 @@ class ChainedLayoutView: UIView { // topRightView.bottomRight = centerView.topRight //bottomLeftView.layout2.topLeft(centerView.bottomLeft).margins(10) - bottomLeftView.layout.topLeft(centerView.bottomLeft).height(50).topMargin(10).bottomMargin(10) - bottomCenterView.layout.topCenter(centerView.bottomCenter).height(50).topMargin(10).bottomMargin(10) - bottomRightView.layout.topRight(centerView.bottomRight).height(50).topMargin(10).bottomMargin(10) + bottomLeftView.layout.pinTopLeft(to: centerView.bottomLeft).height(50).topMargin(10).bottomMargin(10) + bottomCenterView.layout.pinCenter(to: centerView.bottomCenter).height(50).topMargin(10).bottomMargin(10) + bottomRightView.layout.pinTopRight(to: centerView.bottomRight).height(50).topMargin(10).bottomMargin(10) - rightTopView.layout.topLeft(centerView.topRight).width(50).leftMargin(10).rightMargin(10) - rightCenterView.layout.leftCenter(centerView.rightCenter).height(50).leftMargin(10).rightMargin(10) - rightBottomView.layout.bottomLeft(centerView.bottomRight).height(50).leftMargin(10).rightMargin(10) + rightTopView.layout.pinTopLeft(to: centerView.topRight).width(50).leftMargin(10).rightMargin(10) + rightCenterView.layout.pinLeftCenter(to: centerView.rightCenter).height(50).leftMargin(10).rightMargin(10) + rightBottomView.layout.pinBottomLeft(to: centerView.bottomRight).height(50).leftMargin(10).rightMargin(10) // // leftTopView.topRight = centerView.topLeft diff --git a/MCSwiftLayoutSample/MCSwiftLayoutSample/Domain/Expect.swift b/MCSwiftLayoutSample/MCSwiftLayoutSample/Domain/Expect.swift new file mode 100644 index 00000000..0a9dabf4 --- /dev/null +++ b/MCSwiftLayoutSample/MCSwiftLayoutSample/Domain/Expect.swift @@ -0,0 +1,16 @@ +// +// Expect.swift +// MCSwiftLayoutSample +// +// Created by DION, Luc (MTL) on 2017-03-02. +// Copyright © 2017 mcswiftlayyout.mirego.com. All rights reserved. +// + +import UIKit + +func expect(view: UIView, toMatchRect rect: CGRect) { + if view.frame != rect { + print("Doesn't match rect") + } +} + diff --git a/MCSwiftLayoutSample/MCSwiftLayoutSample/MarginsAndPaddingsLeftRightView.swift b/MCSwiftLayoutSample/MCSwiftLayoutSample/MarginsAndPaddingsLeftRightView.swift index f30a2ea1..7ff022fc 100644 --- a/MCSwiftLayoutSample/MCSwiftLayoutSample/MarginsAndPaddingsLeftRightView.swift +++ b/MCSwiftLayoutSample/MCSwiftLayoutSample/MarginsAndPaddingsLeftRightView.swift @@ -8,11 +8,12 @@ import UIKit class MarginsAndPaddingsLeftRightView: UIView { + fileprivate let viewHeight: CGFloat = 40 private let contentScrollView = UIScrollView() private let descriptionLabel = UILabel() - private let noMarginsNoPaddings = BasicView(text: "70x30", color: .black) + private let noMarginsNoPaddings = BasicView(text: "70x40", color: .black) private let noMarginsLeftInsetView = BasicView(text: "LI", color: UIColor.red.withAlphaComponent(1.0)) private let noMarginsRightInsetView = BasicView(text: "RI", color: UIColor.red.withAlphaComponent(0.8)) @@ -32,7 +33,14 @@ class MarginsAndPaddingsLeftRightView: UIView { private let leftRightMarginsLeftInsetView = BasicView(text: "LM-RM LI", color: UIColor.purple.withAlphaComponent(0.8)) private let leftRightMarginsRightInsetView = BasicView(text: "LM-RM RI", color: UIColor.purple.withAlphaComponent(0.6)) private let leftRightMarginsLeftRightInsetView = BasicView(text: "LM-RM LI-RI", color: UIColor.purple.withAlphaComponent(0.4)) - + +// TODO + private let noMarginsNoPaddings2 = BasicView(text: "70x30", color: UIColor.black) + private let topMarginView = BasicView(text: "TM", color: UIColor.orange.withAlphaComponent(1)) + private let topMarginTopPaddingView = BasicView(text: "TM TP", color: UIColor.orange.withAlphaComponent(0.8)) + private let topMarginBottomPaddingView = BasicView(text: "TM BP", color: UIColor.orange.withAlphaComponent(0.6)) + private let topMarginTopBottomPaddingView = BasicView(text: "TM TP-BP", color: UIColor.orange.withAlphaComponent(0.4)) + init() { super.init(frame: .zero) @@ -52,7 +60,7 @@ class MarginsAndPaddingsLeftRightView: UIView { addView(noMarginsLeftInsetView) addView(noMarginsRightInsetView) addView(noMarginsLeftRightInsetView) - + // Left margin addView(leftMarginView) addView(leftMarginLeftInsetView) @@ -64,7 +72,7 @@ class MarginsAndPaddingsLeftRightView: UIView { addView(rigthMarginLeftInsetView) addView(rigthMarginRightInsetView) addView(rigthMarginLeftRightInsetView) - + // Left and right margins addView(leftRightMarginsView) addView(leftRightMarginsLeftInsetView) @@ -73,7 +81,7 @@ class MarginsAndPaddingsLeftRightView: UIView { } fileprivate func addView(_ view: BasicView) { - view.layout.height(30).width(70) + view.layout.height(viewHeight).width(70) contentScrollView.addSubview(view) } @@ -86,40 +94,86 @@ class MarginsAndPaddingsLeftRightView: UIView { let leftPosition: CGFloat = 0 - contentScrollView.layout.topLeft(CGPoint(x: 0, y: 0)).width(width).height(height).topInset(64) + contentScrollView.layout.pinTopLeft().width(width).height(height).topInset(64) descriptionLabel.size = descriptionLabel.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude)) - descriptionLabel.layout.topLeft(CGPoint(x: leftPosition, y: 10)) + descriptionLabel.layout.pinTopLeft(to: CGPoint(x: leftPosition, y: 10)) // No margins let rightPosition: CGFloat = 70 - //var index = 0 - //var nextBottomPosition: CGFloat = descriptionLabel.bottom + 30 - noMarginsNoPaddings.layout.topLeft(descriptionLabel.bottomLeft).bottomRight(x: descriptionLabel.bottom + 30, y: rightPosition) + var topPosition = descriptionLabel.bottom + 10 - noMarginsLeftInsetView.layout.topLeft(noMarginsNoPaddings.bottomLeft).bottomRight(x: noMarginsNoPaddings.bottom + 30, y: rightPosition).leftInset(10) - noMarginsRightInsetView.layout.topLeft(noMarginsLeftInsetView.bottomLeft).bottomRight(x: noMarginsLeftInsetView.bottom + 30, y: rightPosition).rightInset(10) - noMarginsLeftRightInsetView.layout.topLeft(noMarginsRightInsetView.bottomLeft).bottomRight(x: noMarginsRightInsetView.bottom + 30, y: rightPosition).leftInset(10).rightInset(10) -// -// // Left margin -// leftMarginView.layout2.top(noMarginsLeftRightInsetView.bottom + 5).left(leftPosition).width(70).leftMargin(10) -// leftMarginLeftPaddingView.layout2.top(leftMarginView.bottom).left(leftPosition).width(70).leftMargin(10).leftInset(10) -// leftMarginRightInsetView.layout2.top(leftMarginLeftInsetView.bottom).left(leftPosition).width(70).leftMargin(10).RightInset(10) -// leftMarginLeftRightInsetView.layout2.top(leftMarginRightInsetView.bottom).left(leftPosition).width(70).leftMargin(10).leftInset(10).RightInset(10) -// -// // Right margin -// rigthMarginView.layout2.top(leftMarginLeftRightInsetView.bottom + 5).left(leftPosition).width(70).rightMargin(10) -// rigthMarginLeftInsetView.layout2.top(rigthMarginView.bottom).left(leftPosition).width(70).rightMargin(10).leftInset(10) -// rigthMarginRightInsetView.layout2.top(rigthMarginleftInsetView.bottom).left(leftPosition).width(70).rightMargin(10).RightInset(10) -// rigthMarginLeftRightInsetView.layout2.top(rigthMarginRightInsetView.bottom).left(leftPosition).width(70).rightMargin(10).leftInset(10).RightInset(10) -// -// // Left and right margins -// leftRightMarginsView.layout2.top(rigthMarginLeftRightInsetView.bottom + 5).left(leftPosition).width(70).leftMargin(10).rightMargin(10) -// leftRightMarginsleftInsetView.layout2.top(leftRightMarginsView.bottom).left(leftPosition).width(70).leftMargin(10).rightMargin(10).leftInset(10) -// leftRightMarginsRightInsetView.layout2.top(leftRightMarginsleftInsetView.bottom).left(leftPosition).width(70).leftMargin(10).rightMargin(10).RightInset(10) -// leftRightMarginsLeftRightInsetView.layout2.top(leftRightMarginsRightInsetView.bottom).left(leftPosition).width(70).leftMargin(10).rightMargin(10).leftInset(10).RightInset(10) - - contentScrollView.contentSize = CGSize(width: width, height: leftRightMarginsLeftRightInsetView.bottom) + noMarginsNoPaddings.layout.top(topPosition).left(0).bottom(topPosition + viewHeight).right(rightPosition) + expect(view: noMarginsNoPaddings, toMatchRect: CGRect(x: 0, y: 63, width: 70, height: 40)) + topPosition += viewHeight + + noMarginsLeftInsetView.layout.top(topPosition).left(0).bottom(topPosition + viewHeight).right(rightPosition).leftInset(10) + expect(view: noMarginsLeftInsetView, toMatchRect: CGRect(x: 10, y: 103, width: 60, height: 40)) + topPosition += viewHeight + + noMarginsRightInsetView.layout.top(topPosition).left(0).bottom(topPosition + viewHeight).right(rightPosition).rightInset(10) + expect(view: noMarginsRightInsetView, toMatchRect: CGRect(x: 0, y: 143, width: 60, height: 40)) + topPosition += viewHeight + + noMarginsLeftRightInsetView.layout.top(topPosition).left(0).bottom(topPosition + viewHeight).right(rightPosition).leftInset(10).rightInset(10) + expect(view: noMarginsLeftRightInsetView, toMatchRect: CGRect(x: 10, y: 183, width: 50, height: 40)) + topPosition += viewHeight + + // Left margin + topPosition += 5 + leftMarginView.layout.top(topPosition).left(leftPosition).bottom(topPosition + viewHeight).right(rightPosition).leftMargin(10) + expect(view: leftMarginView, toMatchRect: CGRect(x: 10, y: 228, width: 60, height: 40)) + topPosition += viewHeight + + leftMarginLeftInsetView.layout.top(topPosition).left(leftPosition).bottom(topPosition + viewHeight).right(rightPosition).leftMargin(10).leftInset(10) + expect(view: leftMarginLeftInsetView, toMatchRect: CGRect(x: 20, y: 268, width: 50, height: 40)) + topPosition += viewHeight + + leftMarginRightInsetView.layout.top(topPosition).left(leftPosition).bottom(topPosition + viewHeight).right(rightPosition).leftMargin(10).rightInset(10) + expect(view: leftMarginRightInsetView, toMatchRect: CGRect(x: 10, y: 308, width: 50, height: 40)) + topPosition += viewHeight + + leftMarginLeftRightInsetView.layout.top(topPosition).left(leftPosition).bottom(topPosition + viewHeight).right(rightPosition).leftMargin(10).leftInset(10).rightInset(10) + expect(view: leftMarginLeftRightInsetView, toMatchRect: CGRect(x: 20, y: 348, width: 40, height: 40)) + topPosition += viewHeight + + // Right margin + topPosition += 5 + rigthMarginView.layout.top(topPosition).left(leftPosition).bottom(topPosition + viewHeight).right(rightPosition).rightMargin(10) + expect(view: rigthMarginView, toMatchRect: CGRect(x: 0, y: 393, width: 60, height: 40)) + topPosition += viewHeight + + rigthMarginLeftInsetView.layout.top(topPosition).left(leftPosition).bottom(topPosition + viewHeight).right(rightPosition).rightMargin(10).leftInset(10) + expect(view: rigthMarginLeftInsetView, toMatchRect: CGRect(x: 10, y: 433, width: 50, height: 40)) + topPosition += viewHeight + + rigthMarginRightInsetView.layout.top(topPosition).left(leftPosition).bottom(topPosition + viewHeight).right(rightPosition).rightMargin(10).rightInset(10) + expect(view: rigthMarginRightInsetView, toMatchRect: CGRect(x: 0, y: 473, width: 50, height: 40)) + topPosition += viewHeight + + rigthMarginLeftRightInsetView.layout.top(topPosition).left(leftPosition).bottom(topPosition + viewHeight).right(rightPosition).rightMargin(10).leftInset(10).rightInset(10) + expect(view: rigthMarginLeftRightInsetView, toMatchRect: CGRect(x: 10, y: 513, width: 40, height: 40)) + topPosition += viewHeight + + // Left and right margins + topPosition += 5 + leftRightMarginsView.layout.top(topPosition).left(leftPosition).bottom(topPosition + viewHeight).right(rightPosition).leftMargin(10).rightMargin(10) + expect(view: leftRightMarginsView, toMatchRect: CGRect(x: 10, y: 558, width: 50, height: 40)) + topPosition += viewHeight + + leftRightMarginsLeftInsetView.layout.top(topPosition).left(leftPosition).bottom(topPosition + viewHeight).right(rightPosition).leftMargin(10).rightMargin(10).leftInset(10) + expect(view: leftRightMarginsLeftInsetView, toMatchRect: CGRect(x: 20, y: 598, width: 40, height: 40)) + topPosition += viewHeight + + leftRightMarginsRightInsetView.layout.top(topPosition).left(leftPosition).bottom(topPosition + viewHeight).right(rightPosition).leftMargin(10).rightMargin(10).rightInset(10) + expect(view: leftRightMarginsRightInsetView, toMatchRect: CGRect(x: 10, y: 638, width: 40, height: 40)) + topPosition += viewHeight + + leftRightMarginsLeftRightInsetView.layout.top(topPosition).left(leftPosition).bottom(topPosition + viewHeight).right(rightPosition).leftMargin(10).rightMargin(10).leftInset(10).rightInset(10) + expect(view: leftRightMarginsLeftRightInsetView, toMatchRect: CGRect(x: 20, y: 678, width: 30, height: 40)) + topPosition += viewHeight + + contentScrollView.contentSize = CGSize(width: width, height: topPosition) contentScrollView.contentInset = UIEdgeInsets.zero } } diff --git a/MCSwiftLayoutSample/MCSwiftLayoutSample/MarginsAndPaddingsLeftWidthView.swift b/MCSwiftLayoutSample/MCSwiftLayoutSample/MarginsAndPaddingsLeftWidthView.swift index 9dd278ae..826c6223 100644 --- a/MCSwiftLayoutSample/MCSwiftLayoutSample/MarginsAndPaddingsLeftWidthView.swift +++ b/MCSwiftLayoutSample/MCSwiftLayoutSample/MarginsAndPaddingsLeftWidthView.swift @@ -103,13 +103,13 @@ class MarginsAndPaddingsLeftWidthView: UIView { let leftPosition: CGFloat = 0 - contentScrollView.layout.topLeft(CGPoint(x: 0, y: 0)).width(width).height(height).topInset(64) + contentScrollView.layout.pinTopLeft().width(width).height(height).topInset(64) descriptionLabel.size = descriptionLabel.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude)) - descriptionLabel.layout.topLeft(CGPoint(x: leftPosition, y: 10)) + descriptionLabel.layout.pinTopLeft(to: CGPoint(x: leftPosition, y: 10)) // No margins - noMarginsNoPaddings.layout.topLeft(descriptionLabel.bottomLeft).topMargin(5) + noMarginsNoPaddings.layout.pinTopLeft(to: descriptionLabel.bottomLeft).topMargin(5) noMarginsLeftInsetView.layout.top(noMarginsNoPaddings.bottom).left(leftPosition).width(70).leftInset(10) noMarginsRightInsetView.layout.top(noMarginsLeftInsetView.bottom).left(leftPosition).width(70).rightInset(10) noMarginsLeftRightInsetView.layout.top(noMarginsRightInsetView.bottom).left(leftPosition).width(70).leftInset(10).rightInset(10) diff --git a/MCSwiftLayoutSample/MCSwiftLayoutSample/MenuViewController.swift b/MCSwiftLayoutSample/MCSwiftLayoutSample/MenuViewController.swift index 4dcc1c89..39307b77 100644 --- a/MCSwiftLayoutSample/MCSwiftLayoutSample/MenuViewController.swift +++ b/MCSwiftLayoutSample/MCSwiftLayoutSample/MenuViewController.swift @@ -15,6 +15,7 @@ enum Page: Int { case chainedLayout + case bothEdgesSnapped case marginsAndPaddingLeftWidth case marginsAndPaddingLeftRight @@ -27,8 +28,9 @@ enum Page: Int { case .relativePositions: return "Test Relative" case .multiRelativePositions: return "Test Multiple Relatives" case .chainedLayout: return "Chained Layout" - case .marginsAndPaddingLeftWidth: return "Test margings and paddings - Left+Width" - case .marginsAndPaddingLeftRight: return "Test margings and paddings - Left+Right" + case .bothEdgesSnapped: return "NOT USED YET" + case .marginsAndPaddingLeftWidth: return "topLeft & width - Test margings and paddings" + case .marginsAndPaddingLeftRight: return "topLeft & bottomRight - Test margings and paddings" case .validateConflicts: return "Validate properties conflicts" case .count: return "Unknown" } @@ -52,6 +54,11 @@ class MenuViewController: UIViewController { view = MenuView() mainView.delegate = self } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(true) + didSelect(page: .bothEdgesSnapped) + } } // MARK: MenuViewDelegate @@ -68,6 +75,8 @@ extension MenuViewController: MenuViewDelegate { controller = MultiRelativeViewController() case .chainedLayout: controller = ChainedLayoutViewController() + case .bothEdgesSnapped: + controller = BothEdgesSnappedViewController() case .marginsAndPaddingLeftWidth: controller = MarginsAndPaddingsLeftWidthViewController() case .marginsAndPaddingLeftRight: diff --git a/MCSwiftLayoutSample/MCSwiftLayoutSample/MultiRelativeView.swift b/MCSwiftLayoutSample/MCSwiftLayoutSample/MultiRelativeView.swift index cab31b31..8fbdf63c 100644 --- a/MCSwiftLayoutSample/MCSwiftLayoutSample/MultiRelativeView.swift +++ b/MCSwiftLayoutSample/MCSwiftLayoutSample/MultiRelativeView.swift @@ -32,7 +32,7 @@ class MultiRelativeView: UIView { override func layoutSubviews() { super.layoutSubviews() - view1.layout.topLeft(x: 10, y: 64).width(100).height(100) + view1.layout.top(64).left(10).width(100).height(100) view2.layout.right(of: view1, aligned: .top).leftMargin(150).width(100).height(100) view.layout.right(of: view1, aligned: .top).left(of: view2).height(75).leftInset(10).rightInset(10) diff --git a/MCSwiftLayoutSample/MCSwiftLayoutSample/RelativeView.swift b/MCSwiftLayoutSample/MCSwiftLayoutSample/RelativeView.swift index 74fc844e..c58836d9 100644 --- a/MCSwiftLayoutSample/MCSwiftLayoutSample/RelativeView.swift +++ b/MCSwiftLayoutSample/MCSwiftLayoutSample/RelativeView.swift @@ -69,7 +69,7 @@ class RelativeView: UIView { override func layoutSubviews() { super.layoutSubviews() - belowNavBarView.layout.topLeft(.zero).size(size).topInset(64) + belowNavBarView.layout.pinTopLeft().size(size).topInset(64) centerView.width = 200 centerView.height = 200 diff --git a/MCSwiftLayoutSample/MCSwiftLayoutSample/Test/BothEdgesSnapped/BothEdgesSnappedView.swift b/MCSwiftLayoutSample/MCSwiftLayoutSample/Test/BothEdgesSnapped/BothEdgesSnappedView.swift new file mode 100644 index 00000000..44be6146 --- /dev/null +++ b/MCSwiftLayoutSample/MCSwiftLayoutSample/Test/BothEdgesSnapped/BothEdgesSnappedView.swift @@ -0,0 +1,250 @@ +// +// BothEdgesSnappedView.swift +// MCLayoutExample +// +// Created by DION, Luc (MTL) on 2017-02-21. +// Copyright (c) 2017 Mirego. All rights reserved. +// +import UIKit +import MCSwiftLayout + +class BothEdgesSnappedView: UIView { + private let contentScrollView = UIScrollView() + + private let descriptionLabel = UILabel() + + private let noMarginsNoPaddings = BasicView(text: "70x30", color: .black) + +// private let noMarginsLeftInsetView = BasicView(text: "LI", color: UIColor.red.withAlphaComponent(1.0)) +// private let noMarginsRightInsetView = BasicView(text: "RI", color: UIColor.red.withAlphaComponent(0.8)) +// private let noMarginsLeftRightInsetView = BasicView(text: "LI-RI", color: UIColor.red.withAlphaComponent(0.6)) +// +// private let leftMarginView = BasicView(text: "LM", color: UIColor.blue.withAlphaComponent(1.0)) +// private let leftMarginLeftInsetView = BasicView(text: "LM LI", color: UIColor.blue.withAlphaComponent(0.8)) +// private let leftMarginRightInsetView = BasicView(text: "LM RI", color: UIColor.blue.withAlphaComponent(0.6)) +// private let leftMarginLeftRightInsetView = BasicView(text: "LM LI-RI", color: UIColor.blue.withAlphaComponent(0.4)) +// +// private let rigthMarginView = BasicView(text: "RM", color: UIColor.green.withAlphaComponent(1)) +// private let rigthMarginLeftInsetView = BasicView(text: "RM LI", color: UIColor.green.withAlphaComponent(0.8)) +// private let rigthMarginRightInsetView = BasicView(text: "RM RI", color: UIColor.green.withAlphaComponent(0.6)) +// private let rigthMarginLeftRightInsetView = BasicView(text: "RM LI-RI", color: UIColor.green.withAlphaComponent(0.4)) +// +// private let leftRightMarginsView = BasicView(text: "LM-RM", color: UIColor.purple.withAlphaComponent(1)) +// private let leftRightMarginsLeftInsetView = BasicView(text: "LM-RM LI", color: UIColor.purple.withAlphaComponent(0.8)) +// private let leftRightMarginsRightInsetView = BasicView(text: "LM-RM RI", color: UIColor.purple.withAlphaComponent(0.6)) +// private let leftRightMarginsLeftRightInsetView = BasicView(text: "LM-RM LI-RI", color: UIColor.purple.withAlphaComponent(0.4)) + + var rootView: UILabel! + var aView: UILabel! + var aViewChild: UILabel! + + var bView: UILabel! + var bViewChild: UILabel! + + init() { + super.init(frame: .zero) + + backgroundColor = .black + + rootView = UILabel() + setup(rootView, color: .white, text: "") + addSubview(rootView) + + aView = UILabel() + setup(aView, color: UIColor.red.withAlphaComponent(0.5), text: "View A") + rootView.addSubview(aView) + + aViewChild = UILabel() + setup(aViewChild, color: UIColor.red.withAlphaComponent(1), text: "View A Child") + aView.addSubview(aViewChild) + + bView = UILabel() + setup(bView, color: UIColor.blue.withAlphaComponent(0.5), text: "View B") + rootView.addSubview(bView) + + bViewChild = UILabel() + setup(bViewChild, color: UIColor.blue.withAlphaComponent(0.7), text: "View B Child") + bView.addSubview(bViewChild) + +// contentScrollView.backgroundColor = .yellow +// addSubview(contentScrollView) +// +// descriptionLabel.text = "In this test the topLeft coordinates and the bottomRight have been set.\nMargins and Insets always have a value of 10\nL=Left, R=Right, M=Margin, I=Inset" +// descriptionLabel.numberOfLines = 0 +// descriptionLabel.font = UIFont.systemFont(ofSize: 12) +// contentScrollView.addSubview(descriptionLabel) +// +// addView(noMarginsNoPaddings) +// +// // No margins +// addView(noMarginsLeftInsetView) +// addView(noMarginsRightInsetView) +// addView(noMarginsLeftRightInsetView) +// +// // Left margin +// addView(leftMarginView) +// addView(leftMarginLeftInsetView) +// addView(leftMarginRightInsetView) +// addView(leftMarginLeftRightInsetView) +// +// // Right margin +// addView(rigthMarginView) +// addView(rigthMarginLeftInsetView) +// addView(rigthMarginRightInsetView) +// addView(rigthMarginLeftRightInsetView) +// +// // Left and right margins +// addView(leftRightMarginsView) +// addView(leftRightMarginsLeftInsetView) +// addView(leftRightMarginsRightInsetView) +// addView(leftRightMarginsLeftRightInsetView) + } + + fileprivate func addView(_ view: BasicView) { + view.layout.height(30).width(70) + contentScrollView.addSubview(view) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override func layoutSubviews() { + super.layoutSubviews() + + print("\n") + + rootView.frame = CGRect(x: 0, y: 0, width: 400, height: 400) + + aView.frame = CGRect(x: 140, y: 100, width: 100, height: 60) + aViewChild.frame = CGRect(x: 10, y: 20, width: 50, height: 30) + + bView.frame = CGRect(x: 160, y: 200, width: 110, height: 80) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + + // + // pin coordinate + // +// aViewChild.layout.pinTop(.top, of: aView) //CGRect(x: 10.0, y: 0.0, width: 50.0, height: 30.0) +// aViewChild.layout.pinTop(.top, of: bView) // CGRect(x: 10.0, y: 100.0, width: 50.0, height: 30.0) +// bViewChild.layout.pinTop(.top, of: aViewChild) //CGRect(x: 40.0, y: -80.0, width: 60.0, height: 20.0) + +// aViewChild.layout.pinTop(.bottom, of: aView) //CGRect(x: 10.0, y: 60.0, width: 50.0, height: 30.0) +// aViewChild.layout.pinTop(.bottom, of: bView) //CGRect(x: 10.0, y: 180.0, width: 50.0, height: 30.0) +// bViewChild.layout.pinTop(.bottom, of: aViewChild) //CGRect(x: 40.0, y: -50.0, width: 60.0, height: 20.0) + +// aViewChild.layout.pinBottom(.top, of: aView) //CGRect(x: 140.0, y: 100.0, width: 100.0, height: 60.0) +// aViewChild.layout.pinBottom(.top, of: bView) //CGRect(x: 10.0, y: 70.0, width: 50.0, height: 30.0) +// bViewChild.layout.pinBottom(.top, of: aViewChild) //CGRect(x: 40.0, y: -100.0, width: 60.0, height: 20.0) +// +// aViewChild.layout.pinBottom(.bottom, of: aView) //CGRect(x: 140.0, y: 100.0, width: 100.0, height: 60.0) +// aViewChild.layout.pinBottom(.bottom, of: bView) //CGRect(x: 10.0, y: 150.0, width: 50.0, height: 30.0) +// bViewChild.layout.pinBottom(.bottom, of: aViewChild) //CGRect(x: 40.0, y: -70.0, width: 60.0, height: 20.0) + +// aViewChild.layout.pinLeft(.left, of: aView) //CGRect(x: 0.0, y: 20.0, width: 50.0, height: 30.0) +// aViewChild.layout.pinLeft(.left, of: bView) //CGRect(x: 20.0, y: 20.0, width: 50.0, height: 30.0) +// bViewChild.layout.pinLeft(.left, of: aViewChild) //CGRect(x: -10.0, y: 10.0, width: 60.0, height: 20.0) +// +// aViewChild.layout.pinLeft(.right, of: aView) //CGRect(x: 100.0, y: 20.0, width: 50.0, height: 30.0) +// aViewChild.layout.pinLeft(.right, of: bView) //CGRect(x: 130.0, y: 20.0, width: 50.0, height: 30.0) +// bViewChild.layout.pinLeft(.right, of: aViewChild) //CGRect(x: 40.0, y: 10.0, width: 60.0, height: 20.0) + +// aViewChild.layout.pinRight(.left, of: aView) //CGRect(x: 140.0, y: 100.0, width: 100.0, height: 60.0) +// aViewChild.layout.pinRight(.left, of: bView) //CGRect(x: -30.0, y: 20.0, width: 50.0, height: 30.0) +// bViewChild.layout.pinRight(.left, of: aViewChild) //CGRect(x: -70.0, y: 10.0, width: 60.0, height: 20.0) +// +// aViewChild.layout.pinRight(.right, of: aView) //CGRect(x: 140.0, y: 100.0, width: 100.0, height: 60.0) +// aViewChild.layout.pinRight(.right, of: bView) //CGRect(x: 80.0, y: 20.0, width: 50.0, height: 30.0) +// bViewChild.layout.pinRight(.right, of: aViewChild) //CGRect(x: -20.0, y: 10.0, width: 60.0, height: 20.0) + + // + // pin point + // + + // topLeft +// aView.layout.topLeft() // CGRect(x: 0.0, y: 0.0, width: 100.0, height: 60.0) +// aViewChild.layout.pinTopLeft(.topLeft, of: aView) //(of: aView) // CGRect(x: 0.0, y: 0.0, width: 50.0, height: 30.0) +// aViewChild.layout.pinTopLeft(of: aView) // CGRect(x: 0.0, y: 0.0, width: 50.0, height: 30.0) +// aViewChild.layout.pinTopLeft(of: aView).pinBottomRight() +// aViewChild.layout.topLeft(of: bView) // CGRect(x: 20.0, y: 100.0, width: 50.0, height: 30.0) +// bView.layout.topLeft(of: aViewChild) // CGRect(x: 150.0, y: 120.0, width: 110.0, height: 80.0) +// bViewChild.layout.pinTopLeft(to: aViewChild)< // CGRect(x: -10.0, y: -80.0, width: 60.0, height: 20.0) + + +// bView.layout.pinBottomRight().pinTopLeft(to: .zero).below(of: bViewChild) + + + // pinTopCenter +// aView.layout.pinTopCenter() // CGRect(x: 150.0, y: 0.0, width: 100.0, height: 60.0) +// aViewChild.layout.pinTopCenter(of: aView) //CGRect(x: 25.0, y: 0.0, width: 50.0, height: 30.0) +// aViewChild.layout.pinTopCenter(of: bView) //CGRect(x: 50.0, y: 100.0, width: 50.0, height: 30.0) +// bView.layout.pinTopCenter(of: aViewChild) //CGRect(x: 120.0, y: 120.0, width: 110.0, height: 80.0) +// bViewChild.layout.pinTopCenter(of: aViewChild) //CGRect(x: -15.0, y: -80.0, width: 60.0, height: 20.0) + + // topRight +// aView.layout.topRight() //CGRect(x: 300.0, y: 0.0, width: 100.0, height: 60.0) +// aViewChild.layout.topRight(of: aView) //GRect(x: 50.0, y: 0.0, width: 50.0, height: 30.0) +// aViewChild.layout.topRight(of: bView) //CGRect(x: 80.0, y: 100.0, width: 50.0, height: 30.0) +// bView.layout.topRight(of: aViewChild) //CGRect(x: 90.0, y: 120.0, width: 110.0, height: 80.0) +// bViewChild.layout.topRight(of: aViewChild) //CGRect(x: -20.0, y: -80.0, width: 60.0, height: 20.0) + + // leftCenter +// aView.layout.leftCenter() //CGRect(x: 0.0, y: 170.0, width: 100.0, height: 60.0) +// aViewChild.layout.leftCenter(of: aView) //CGRect(x: 0.0, y: 15.0, width: 50.0, height: 30.0) +// aViewChild.layout.leftCenter(of: bView) //CGRect(x: 20.0, y: 125.0, width: 50.0, height: 30.0) +// bView.layout.leftCenter(of: aViewChild) //CGRect(x: 150.0, y: 95.0, width: 110.0, height: 80.0) +// bViewChild.layout.leftCenter(of: aViewChild) //CGRect(x: -10.0, y: -75.0, width: 60.0, height: 20.0) + + // Centers +// aView.layout.centers() //CGRect(x: 150.0, y: 170.0, width: 100.0, height: 60.0) +// aViewChild.layout.centers(of: aView) //CGRect(x: 25.0, y: 15.0, width: 50.0, height: 30.0) +// aViewChild.layout.centers(of: bView) //CGRect(x: 50.0, y: 125.0, width: 50.0, height: 30.0) +// bView.layout.centers(of: aViewChild) //CGRect(x: 120.0, y: 95.0, width: 110.0, height: 80.0) +// bViewChild.layout.centers(of: aViewChild) //CGRect(x: -15.0, y: -75.0, width: 60.0, height: 20.0) + + // rightCenter +// aView.layout.rightCenter() //CGRect(x: 300.0, y: 170.0, width: 100.0, height: 60.0) +// aViewChild.layout.rightCenter(of: aView) //CGRect(x: 50.0, y: 15.0, width: 50.0, height: 30.0) +// aViewChild.layout.rightCenter(of: bView) //CGRect(x: 80.0, y: 125.0, width: 50.0, height: 30.0) +// bView.layout.rightCenter(of: aViewChild) //CGRect(x: 90.0, y: 95.0, width: 110.0, height: 80.0) +// bViewChild.layout.rightCenter(of: aViewChild) //CGRect(x: -20.0, y: -75.0, width: 60.0, height: 20.0) + + // bottomLeft +// aView.layout.bottomLeft() //CGRect(x: 0.0, y: 340.0, width: 100.0, height: 60.0) +// aViewChild.layout.bottomLeft(of: aView) //CGRect(x: 0.0, y: 30.0, width: 50.0, height: 30.0) +// aViewChild.layout.bottomLeft(of: bView) //CGRect(x: 20.0, y: 150.0, width: 50.0, height: 30.0) +// bView.layout.bottomLeft(of: aViewChild) //CGRect(x: 150.0, y: 70.0, width: 110.0, height: 80.0) +// bViewChild.layout.bottomLeft(of: aViewChild) //CGRect(x: -10.0, y: -70.0, width: 60.0, height: 20.0) + + // bottomCenter +// aView.layout.bottomCenter() //CGRect(x: 150.0, y: 340.0, width: 100.0, height: 60.0) +// aViewChild.layout.bottomCenter(of: aView) //CGRect(x: 25.0, y: 30.0, width: 50.0, height: 30.0) +// aViewChild.layout.bottomCenter(of: bView) //CGRect(x: 50.0, y: 150.0, width: 50.0, height: 30.0) +// bView.layout.bottomCenter(of: aViewChild) //CGRect(x: 120.0, y: 70.0, width: 110.0, height: 80.0) +// bViewChild.layout.bottomCenter(of: aViewChild) //CGRect(x: -15.0, y: -70.0, width: 60.0, height: 20.0) + + // bottomRight +// aView.layout.bottomRight() //CGRect(x: 300.0, y: 340.0, width: 100.0, height: 60.0) +// aViewChild.layout.bottomRight(of: aView) //CGRect(x: 50.0, y: 30.0, width: 50.0, height: 30.0) +// aViewChild.layout.bottomRight(of: bView) //CGRect(x: 80.0, y: 150.0, width: 50.0, height: 30.0) +// bView.layout.bottomRight(of: aViewChild) //CGRect(x: 90.0, y: 70.0, width: 110.0, height: 80.0) +// bViewChild.layout.bottomRight(of: aViewChild) //CGRect(x: -20.0, y: -70.0, width: 60.0, height: 20.0) + + printViewFrame(aView, name: "aView") + printViewFrame(aViewChild, name: "aViewChild") + printViewFrame(bView, name: "bView") + printViewFrame(bViewChild, name: "bViewChild") + } + + fileprivate func setup(_ view: UILabel, color: UIColor, text: String) { + view.text = text + view.font = UIFont.systemFont(ofSize: 6) + view.backgroundColor = color + view.layer.borderWidth = 1 + view.layer.borderColor = UIColor.lightGray.cgColor + } + + fileprivate func printViewFrame(_ view: UIView, name: String) { + print("\(name): CGRect(x: \(view.frame.origin.x), y: \(view.frame.origin.y), width: \(view.frame.size.width), height: \(view.frame.size.height))") + } +} diff --git a/MCSwiftLayoutSample/MCSwiftLayoutSample/Test/BothEdgesSnapped/BothEdgesSnappedViewController.swift b/MCSwiftLayoutSample/MCSwiftLayoutSample/Test/BothEdgesSnapped/BothEdgesSnappedViewController.swift new file mode 100644 index 00000000..4fef2825 --- /dev/null +++ b/MCSwiftLayoutSample/MCSwiftLayoutSample/Test/BothEdgesSnapped/BothEdgesSnappedViewController.swift @@ -0,0 +1,32 @@ +// +// BothEdgesSnappedViewController.swift +// MCLayoutExample +// +// Created by DION, Luc (MTL) on 2017-02-21. +// Copyright (c) 2017 Mirego. All rights reserved. +// +import UIKit + +class BothEdgesSnappedViewController: UIViewController { + fileprivate var mainView: BothEdgesSnappedView { + return self.view as! BothEdgesSnappedView + } + + init() { + super.init(nibName: nil, bundle: nil) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + view = BothEdgesSnappedView() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(true) + + navigationController?.isNavigationBarHidden = true + } +} diff --git a/MCSwiftLayoutSample/MCSwiftLayoutSample/ValidateConflictsView.swift b/MCSwiftLayoutSample/MCSwiftLayoutSample/ValidateConflictsView.swift index 2a9688be..3f38be5e 100644 --- a/MCSwiftLayoutSample/MCSwiftLayoutSample/ValidateConflictsView.swift +++ b/MCSwiftLayoutSample/MCSwiftLayoutSample/ValidateConflictsView.swift @@ -32,6 +32,6 @@ class ValidateConflictsView: UIView { override func layoutSubviews() { super.layoutSubviews() - topLeftView.layout.right(10).width(20).topLeft(x: 10, y: 10).left(10) + topLeftView.layout.right(10).width(20).top(10).left(10).left(10) } } diff --git a/MCSwiftLayoutSample/MCSwiftLayoutSampleTests/MCSwiftLayoutSampleTests.swift b/MCSwiftLayoutSample/MCSwiftLayoutSampleTests/MCSwiftLayoutSampleTests.swift deleted file mode 100644 index b902d786..00000000 --- a/MCSwiftLayoutSample/MCSwiftLayoutSampleTests/MCSwiftLayoutSampleTests.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// MCSwiftLayoutSampleTests.swift -// MCSwiftLayoutSampleTests -// -// Created by DION, Luc (MTL) on 2017-02-27. -// Copyright © 2017 mcswiftlayyout.mirego.com. All rights reserved. -// - -import XCTest -@testable import MCSwiftLayoutSample - -class MCSwiftLayoutSampleTests: XCTestCase { - - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testPerformanceExample() { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/MCSwiftLayoutSample/Podfile b/MCSwiftLayoutSample/Podfile new file mode 100644 index 00000000..2879cfc3 --- /dev/null +++ b/MCSwiftLayoutSample/Podfile @@ -0,0 +1,18 @@ +use_frameworks! + +workspace 'path/to/Workspace.xcworkspace' + +target 'MCSwiftLayoutSample' do + project 'MCSwiftLayoutSample.xcodeproj' + #pod 'YogaKit', '~> 1.1' + + # Debug only + pod 'Reveal-SDK', :configurations => ['Debug'] + pod 'SwiftLint' +end + +target 'MCSwiftLayoutTests' do + project '../MCSwiftLayout.xcodeproj' + pod 'Quick' + pod 'Nimble' +end diff --git a/MCSwiftLayoutSample/Podfile.lock b/MCSwiftLayoutSample/Podfile.lock new file mode 100644 index 00000000..fad2da64 --- /dev/null +++ b/MCSwiftLayoutSample/Podfile.lock @@ -0,0 +1,21 @@ +PODS: + - Nimble (6.0.1) + - Quick (1.1.0) + - Reveal-SDK (7) + - SwiftLint (0.16.1) + +DEPENDENCIES: + - Nimble + - Quick + - Reveal-SDK + - SwiftLint + +SPEC CHECKSUMS: + Nimble: 1527fd1bd2b4cf0636251a36bc8ab37e81da8347 + Quick: dafc587e21eed9f4cab3249b9f9015b0b7a7f71d + Reveal-SDK: 564657bc66c93d2718b1c0121cdebe99d354ddab + SwiftLint: b8b683208cc09640898f16318a7a452274e91f61 + +PODFILE CHECKSUM: 2def317a86457403b59cecf0cfc91047198c8e4d + +COCOAPODS: 1.1.1 diff --git a/MCSwiftLayoutTests/MCSwiftLayoutTests.swift b/MCSwiftLayoutTests/MCSwiftLayoutTests.swift index ccebb160..340761b2 100644 --- a/MCSwiftLayoutTests/MCSwiftLayoutTests.swift +++ b/MCSwiftLayoutTests/MCSwiftLayoutTests.swift @@ -10,20 +10,25 @@ import XCTest @testable import MCSwiftLayout class MCSwiftLayoutTests: XCTestCase { + var viewController: UIViewController! + var rootView: UIView! override func setUp() { super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() + + viewController = UIViewController() + + rootView = UIView() + rootView.frame = CGRect(x: 0, y: 0, width: 100, height: 100) + viewController.view.addSubview(rootView) } func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. + let child1 = UIView() + rootView.addSubview(child1) + +// child1.layout.pinCenter(of: rootView) +// print("child1.frame: \(child1.frame)") } func testPerformanceExample() { diff --git a/MCSwiftLayoutTests/PinEdgeCoordinateSpec.swift b/MCSwiftLayoutTests/PinEdgeCoordinateSpec.swift new file mode 100644 index 00000000..e2a462bc --- /dev/null +++ b/MCSwiftLayoutTests/PinEdgeCoordinateSpec.swift @@ -0,0 +1,157 @@ +// +// BasicTests.swift +// MCSwiftLayout +// +// Created by DION, Luc (MTL) on 2017-03-05. +// Copyright © 2017 mcswiftlayyout.mirego.com. All rights reserved. +// +import Quick +import Nimble +import MCSwiftLayout + +class PinEdgeCoordinateSpec: QuickSpec { + override func spec() { + var viewController: UIViewController! + var rootView: UIView! + + var aView: UIView! + var aViewChild: UIView! + + var bView: UIView! + var bViewChild: UIView! + + /* + root + | + - childLevel1 + | | + | - aViewChild + | + - bView + | + - bViewChild + */ + + beforeEach { + viewController = UIViewController() + + rootView = UIView() + rootView.frame = CGRect(x: 0, y: 0, width: 400, height: 400) + viewController.view.addSubview(rootView) + + aView = UIView() + aView.frame = CGRect(x: 140, y: 100, width: 100, height: 60) + rootView.addSubview(aView) + + aViewChild = UIView() + aViewChild.frame = CGRect(x: 10, y: 20, width: 50, height: 30) + aView.addSubview(aViewChild) + + bView = UIView() + bView.frame = CGRect(x: 160, y: 200, width: 110, height: 80) + rootView.addSubview(bView) + + bViewChild = UIView() + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + bView.addSubview(bViewChild) + } + + describe("the result of the pinTop(edge: VerticalEdge, of: UIView) method") { + it("should warns that the view is not added to any view") { + let unAttachedView = UIView(frame: CGRect(x: 10, y: 10, width: 10, height: 10)) + unAttachedView.layout.pinTop(.top, of: aView) + + expect(unAttachedView.frame).to(equal(CGRect(x: 10, y: 10, width: 10, height: 10))) + } + } + + + // aViewChild.layout.pinBottom(.top, of: aView) //CGRect(x: 140.0, y: 100.0, width: 100.0, height: 60.0) + // aViewChild.layout.pinBottom(.top, of: bView) //CGRect(x: 10.0, y: 70.0, width: 50.0, height: 30.0) + // bViewChild.layout.pinBottom(.top, of: aViewChild) //CGRect(x: 40.0, y: -100.0, width: 60.0, height: 20.0) + // + // aViewChild.layout.pinBottom(.bottom, of: aView) //CGRect(x: 140.0, y: 100.0, width: 100.0, height: 60.0) + // aViewChild.layout.pinBottom(.bottom, of: bView) //CGRect(x: 10.0, y: 150.0, width: 50.0, height: 30.0) + // bViewChild.layout.pinBottom(.bottom, of: aViewChild) //CGRect(x: 40.0, y: -70.0, width: 60.0, height: 20.0) + + // aViewChild.layout.pinRight(.left, of: aView) //CGRect(x: 140.0, y: 100.0, width: 100.0, height: 60.0) + // aViewChild.layout.pinRight(.left, of: bView) //CGRect(x: -30.0, y: 20.0, width: 50.0, height: 30.0) + // bViewChild.layout.pinRight(.left, of: aViewChild) //CGRect(x: -70.0, y: 10.0, width: 60.0, height: 20.0) + // + // aViewChild.layout.pinRight(.right, of: aView) //CGRect(x: 140.0, y: 100.0, width: 100.0, height: 60.0) + // aViewChild.layout.pinRight(.right, of: bView) //CGRect(x: 80.0, y: 20.0, width: 50.0, height: 30.0) + // bViewChild.layout.pinRight(.right, of: aViewChild) //CGRect(x: -20.0, y: 10.0, width: 60.0, height: 20.0) + + // + // pinTop + // + describe("the result of the pinTop(edge: VerticalEdge, of: UIView)") { + + it("should position the aViewChild's top edge on its parent's top edge") { + aViewChild.layout.pinTop(.top, of: aView) + expect(aViewChild.frame).to(equal(CGRect(x: 10.0, y: 0.0, width: 50.0, height: 30.0))) + } + + it("should position the aViewChild's top edge on its parent sibling (bView)'s top edge") { + aViewChild.layout.pinTop(.top, of: bView) + expect(aViewChild.frame).to(equal(CGRect(x: 10.0, y: 100.0, width: 50.0, height: 30.0))) + } + + it("should position the bViewChild's top edge on its sibling's children (aViewChild)'s top edge") { + bViewChild.layout.pinTop(.top, of: aViewChild) + expect(bViewChild.frame).to(equal(CGRect(x: 40.0, y: -80.0, width: 60.0, height: 20.0))) + } + + it("should position the aViewChild's top edge on its parent's bottom edge") { + aViewChild.layout.pinTop(.bottom, of: aView) + expect(aViewChild.frame).to(equal(CGRect(x: 10.0, y: 60.0, width: 50.0, height: 30.0))) + } + + it("should position the aViewChild's top edge on its parent sibling (bView)'s bottom edge") { + aViewChild.layout.pinTop(.bottom, of: bView) + expect(aViewChild.frame).to(equal(CGRect(x: 10.0, y: 180.0, width: 50.0, height: 30.0))) + } + + it("should position the bViewChild's top edge on its sibling's children (aViewChild)'s bottom edge") { + bViewChild.layout.pinTop(.bottom, of: aViewChild) + expect(bViewChild.frame).to(equal(CGRect(x: 40.0, y: -50.0, width: 60.0, height: 20.0))) + } + } + + // + // pinLeft + // + describe("the result of the pinLeft(edge: VerticalEdge, of: UIView)") { + + it("should position the aViewChild's left edge on its parent's left edge") { + aViewChild.layout.pinLeft(.left, of: aView) + expect(aViewChild.frame).to(equal(CGRect(x: 0.0, y: 20.0, width: 50.0, height: 30.0))) + } + + it("should position the aViewChild's left edge on its parent sibling (bView)'s left edge") { + aViewChild.layout.pinLeft(.left, of: bView) + expect(aViewChild.frame).to(equal(CGRect(x: 20.0, y: 20.0, width: 50.0, height: 30.0))) + } + + it("should position the bViewChild's left edge on its sibling's children (aViewChild)'s left edge") { + bViewChild.layout.pinLeft(.left, of: aViewChild) + expect(bViewChild.frame).to(equal(CGRect(x: -10.0, y: 10.0, width: 60.0, height: 20.0))) + } + + it("should position the aViewChild's left edge on its parent's right edge") { + aViewChild.layout.pinLeft(.right, of: aView) + expect(aViewChild.frame).to(equal(CGRect(x: 100.0, y: 20.0, width: 50.0, height: 30.0))) + } + + it("should position the aViewChild's left edge on its parent sibling (bView)'s right edge") { + aViewChild.layout.pinLeft(.right, of: bView) + expect(aViewChild.frame).to(equal(CGRect(x: 130.0, y: 20.0, width: 50.0, height: 30.0))) + } + + it("should position the bViewChild's left edge on its sibling's children (aViewChild)'s right edge") { + bViewChild.layout.pinLeft(.right, of: aViewChild) + expect(bViewChild.frame).to(equal(CGRect(x: 40.0, y: 10.0, width: 60.0, height: 20.0))) + } + } + } +} diff --git a/MCSwiftLayoutTests/PinPointCoordinatesSpec.swift b/MCSwiftLayoutTests/PinPointCoordinatesSpec.swift new file mode 100644 index 00000000..cadae115 --- /dev/null +++ b/MCSwiftLayoutTests/PinPointCoordinatesSpec.swift @@ -0,0 +1,147 @@ +// +// BasicTests.swift +// MCSwiftLayout +// +// Created by DION, Luc (MTL) on 2017-03-05. +// Copyright © 2017 mcswiftlayyout.mirego.com. All rights reserved. +// +import Quick +import Nimble +import MCSwiftLayout + +class PinPointCoordinatesSpec: QuickSpec { + override func spec() { + var viewController: UIViewController! + var rootView: UIView! + + var aView: UIView! + var aViewChild: UIView! + + var bView: UIView! + var bViewChild: UIView! + + /* + root + | + - childLevel1 + | | + | - aViewChild + | + - bView + | + - bViewChild + */ + + beforeEach { + viewController = UIViewController() + + rootView = UIView() + rootView.frame = CGRect(x: 0, y: 0, width: 400, height: 400) + viewController.view.addSubview(rootView) + + aView = UIView() + aView.frame = CGRect(x: 140, y: 100, width: 100, height: 60) + rootView.addSubview(aView) + + aViewChild = UIView() + aViewChild.frame = CGRect(x: 10, y: 20, width: 50, height: 30) + aView.addSubview(aViewChild) + + bView = UIView() + bView.frame = CGRect(x: 160, y: 200, width: 110, height: 80) + rootView.addSubview(bView) + + bViewChild = UIView() + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + bView.addSubview(bViewChild) + } + + describe("the result of the centers(of: UIView) method") { + it("should warns that the view is not added to any view") { + let unAttachedView = UIView(frame: CGRect(x: 10, y: 10, width: 10, height: 10)) + unAttachedView.layout.pinCenter(to: rootView) + + expect(unAttachedView.frame).to(equal(CGRect(x: 10, y: 10, width: 10, height: 10))) + } + } + + // + // topLeft + // + describe("the result of the topLeft() and topLeft(of: UIView) methods") { + + it("should position the aView's topLeft corner at the specified position") { + aView.layout.pinTopLeft(to: CGPoint(x: 25, y: 25)) + expect(aView.frame).to(equal(CGRect(x: 25.0, y: 25.0, width: 100.0, height: 60.0))) + } + + it("should position the aView's topLeft corner on its parent's topLeft corner") { + aView.layout.pinTopLeft() + expect(aView.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 100.0, height: 60.0))) + } + + it("should position the aViewChild's topLeft corner on the specified view's topLeft corner") { + aViewChild.layout.pinTopLeft(Pin.topLeft, of: aView) + expect(aViewChild.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 50.0, height: 30.0))) + } + + it("should position the aViewChild's topLeft corner on its parent sibling (bView)'s topLeft corner") { + aViewChild.layout.pinTopLeft(to: bView) + expect(aViewChild.frame).to(equal(CGRect(x: 20.0, y: 100.0, width: 50.0, height: 30.0))) + } + + it("should position the bView's topLeft corner on its sibling's (aView) children (aViewChild)'s topLeft corner") { + bView.layout.pinTopLeft(to: aViewChild) + expect(bView.frame).to(equal(CGRect(x: 150.0, y: 120.0, width: 110.0, height: 80.0))) + } + + it("should position the bViewChild's topLeft corner on its parent sibling's (aView) children (aViewChild)'s topLeft corner") { + bViewChild.layout.pinTopLeft(to: aViewChild) + expect(bViewChild.frame).to(equal(CGRect(x: -10.0, y: -80.0, width: 60.0, height: 20.0))) + } + } + + // + // // + // // + // // + // // + // // + + // + // topCenter + // + describe("the result of the topCenter() and topCenter(of: UIView) methods") { + + it("should position the aView's topCenter corner at the specified position") { + aView.layout.pinTopCenter(to: CGPoint(x: 100, y: 100)) + expect(aView.frame).to(equal(CGRect(x: 50.0, y: 100.0, width: 100.0, height: 60.0))) + } + + it("should position the aView's topCenter corner on its parent's topCenter corner") { + aView.layout.pinTopCenter() + expect(aView.frame).to(equal(CGRect(x: 150.0, y: 0.0, width: 100.0, height: 60.0))) + } + + it("should position the aViewChild's topCenter corner on the specified view's topCenter corner") { + aViewChild.layout.pinTopCenter(to: aView) + expect(aViewChild.frame).to(equal(CGRect(x: 25.0, y: 0.0, width: 50.0, height: 30.0))) + } + + it("should position the aViewChild's topCenter corner on its parent sibling (bView)'s topCenter corner") { + aViewChild.layout.pinTopCenter(to: bView) + expect(aViewChild.frame).to(equal(CGRect(x: 50.0, y: 100.0, width: 50.0, height: 30.0))) + } + + it("should position the bView's topCenter corner on its sibling's (aView) children (aViewChild)'s topCenter corner") { + bView.layout.pinTopCenter(to: aViewChild) + expect(bView.frame).to(equal(CGRect(x: 120.0, y: 120.0, width: 110.0, height: 80.0))) + } + + it("should position the bViewChild's topCenter corner on its parent sibling's (aView) children (aViewChild)'s topCenter corner") { + bViewChild.layout.pinTopCenter(to: aViewChild) + expect(bViewChild.frame).to(equal(CGRect(x: -15.0, y: -80.0, width: 60.0, height: 20.0))) + } + } + } +} diff --git a/Podfile b/Podfile new file mode 100644 index 00000000..8adc1b88 --- /dev/null +++ b/Podfile @@ -0,0 +1,6 @@ +use_frameworks! + +target 'MCSwiftLayoutTests' do + pod 'Quick' + pod 'Nimble' +end diff --git a/Podfile.lock b/Podfile.lock new file mode 100644 index 00000000..badf7522 --- /dev/null +++ b/Podfile.lock @@ -0,0 +1,15 @@ +PODS: + - Nimble (6.0.1) + - Quick (1.1.0) + +DEPENDENCIES: + - Nimble + - Quick + +SPEC CHECKSUMS: + Nimble: 1527fd1bd2b4cf0636251a36bc8ab37e81da8347 + Quick: dafc587e21eed9f4cab3249b9f9015b0b7a7f71d + +PODFILE CHECKSUM: bf792fd392130a7a74fbcd6812b5af2131610dbd + +COCOAPODS: 1.1.1 diff --git a/scripts/synx.sh b/scripts/synx.sh new file mode 100755 index 00000000..20625b76 --- /dev/null +++ b/scripts/synx.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +synx --no-sort-by-name PAP.xcodeproj + +echo +echo +echo ===================================================== +echo List of empty forlders +echo ===================================================== +find ./PAP -depth -type d -empty +echo end \ No newline at end of file