├── .gitignore ├── FlooidLayout.podspec ├── FlooidLayout └── Source │ ├── Anchor combinations │ ├── NSLayoutEdges.swift │ ├── NSLayoutLocation.swift │ ├── NSLayoutSize.swift │ ├── NSLayoutXAxisEdges.swift │ └── NSLayoutYAxisEdges.swift │ ├── Constraint Paramters │ ├── DimensionConstraintParametersProvider.swift │ ├── EdgesConstraintParametersProvider.swift │ ├── LocationConstraintParametersProvider.swift │ ├── SizeConstraintParametersProvider.swift │ ├── XAxisConstraintParametersProvider.swift │ ├── XAxisEdgesConstraintParametersProvider.swift │ ├── YAxisConstraintParametersProvider.swift │ └── YAxisEdgesConstraintParametersProvider.swift │ ├── Constraints Creation │ ├── LayoutConstraintDimensionBuilder.swift │ ├── LayoutConstraintEdgesBuilder.swift │ ├── LayoutConstraintLocationBuilder.swift │ ├── LayoutConstraintSizeBuilder.swift │ ├── LayoutConstraintXAxisBuilder.swift │ ├── LayoutConstraintXAxisEdgesBuilder.swift │ ├── LayoutConstraintYAxisBuilder.swift │ └── LayoutConstraintYAxisEdgesBuilder.swift │ ├── NSLayoutConstraintAssignment.swift │ ├── NSLayoutConstraintPropertyModifier.swift │ └── NSLayoutConstraintsBuilder.swift ├── LICENSE ├── Package.swift └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /FlooidLayout.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "FlooidLayout" 4 | s.version = "1.0.9" 5 | s.summary = "Declarative Autolayout Library." 6 | s.description = "FloodLayout is an autolayout DSL, which helps you write a more intuitive and expressive code, by using custom operators and result builders." 7 | s.homepage = "http://github.com/martin-lalev/FlooidLayout" 8 | s.license = "MIT" 9 | s.author = "Martin Lalev" 10 | s.platform = :ios, "12.0" 11 | s.source = { :git => "https://github.com/martin-lalev/FlooidLayout.git", :tag => s.version } 12 | s.source_files = "FlooidLayout", "FlooidLayout/**/*.{swift}" 13 | s.swift_version = '6.0' 14 | s.user_target_xcconfig = { 'GENERATE_INFOPLIST_FILE' => 'YES' } 15 | 16 | end 17 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Anchor combinations/NSLayoutEdges.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSLayoutEdges.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 1.07.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | @MainActor 13 | public struct NSLayoutEdges { 14 | let leadingAxisAnchor: NSLayoutXAxisAnchor 15 | let trailingAxisAnchor: NSLayoutXAxisAnchor 16 | let topAxisAnchor: NSLayoutYAxisAnchor 17 | let bottomAxisAnchor: NSLayoutYAxisAnchor 18 | } 19 | 20 | 21 | @MainActor 22 | public protocol EdgesAnchorProvider { 23 | var leadingAnchor: NSLayoutXAxisAnchor { get } 24 | var trailingAnchor: NSLayoutXAxisAnchor { get } 25 | var topAnchor: NSLayoutYAxisAnchor { get } 26 | var bottomAnchor: NSLayoutYAxisAnchor { get } 27 | } 28 | extension UIView: EdgesAnchorProvider {} 29 | extension UILayoutGuide: EdgesAnchorProvider {} 30 | 31 | public extension EdgesAnchorProvider { 32 | var edgesAnchor: NSLayoutEdges { .init(leadingAxisAnchor: self.leadingAnchor, trailingAxisAnchor: self.trailingAnchor, topAxisAnchor: self.topAnchor, bottomAxisAnchor: self.bottomAnchor) } 33 | } 34 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Anchor combinations/NSLayoutLocation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSLayoutLocation.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 1.07.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | @MainActor 13 | public struct NSLayoutLocation { 14 | let xAxisAnchor: NSLayoutXAxisAnchor 15 | let yAxisAnchor: NSLayoutYAxisAnchor 16 | } 17 | 18 | 19 | @MainActor 20 | public protocol LayoutLocationProvider { 21 | var centerXAnchor: NSLayoutXAxisAnchor { get } 22 | var leadingAnchor: NSLayoutXAxisAnchor { get } 23 | var trailingAnchor: NSLayoutXAxisAnchor { get } 24 | var leftAnchor: NSLayoutXAxisAnchor { get } 25 | var rightAnchor: NSLayoutXAxisAnchor { get } 26 | var centerYAnchor: NSLayoutYAxisAnchor { get } 27 | var topAnchor: NSLayoutYAxisAnchor { get } 28 | var bottomAnchor: NSLayoutYAxisAnchor { get } 29 | } 30 | extension UIView: LayoutLocationProvider {} 31 | extension UILayoutGuide: LayoutLocationProvider {} 32 | 33 | public extension LayoutLocationProvider { 34 | var leadingTopAnchor: NSLayoutLocation { .init(xAxisAnchor: self.leadingAnchor, yAxisAnchor: self.topAnchor) } 35 | var leadingCenterAnchor: NSLayoutLocation { .init(xAxisAnchor: self.leadingAnchor, yAxisAnchor: self.centerYAnchor) } 36 | var leadingBottomAnchor: NSLayoutLocation { .init(xAxisAnchor: self.leadingAnchor, yAxisAnchor: self.bottomAnchor) } 37 | 38 | var centerTopAnchor: NSLayoutLocation { .init(xAxisAnchor: self.centerXAnchor, yAxisAnchor: self.topAnchor) } 39 | var centerAnchor: NSLayoutLocation { .init(xAxisAnchor: self.centerXAnchor, yAxisAnchor: self.centerYAnchor) } 40 | var centerBottomAnchor: NSLayoutLocation { .init(xAxisAnchor: self.centerXAnchor, yAxisAnchor: self.bottomAnchor) } 41 | 42 | var trailingTopAnchor: NSLayoutLocation { .init(xAxisAnchor: self.trailingAnchor, yAxisAnchor: self.topAnchor) } 43 | var trailingCenterAnchor: NSLayoutLocation { .init(xAxisAnchor: self.trailingAnchor, yAxisAnchor: self.centerYAnchor) } 44 | var trailingBottomAnchor: NSLayoutLocation { .init(xAxisAnchor: self.trailingAnchor, yAxisAnchor: self.bottomAnchor) } 45 | 46 | var leftTopAnchor: NSLayoutLocation { .init(xAxisAnchor: self.leftAnchor, yAxisAnchor: self.topAnchor) } 47 | var leftCenterAnchor: NSLayoutLocation { .init(xAxisAnchor: self.leftAnchor, yAxisAnchor: self.centerYAnchor) } 48 | var leftBottomAnchor: NSLayoutLocation { .init(xAxisAnchor: self.leftAnchor, yAxisAnchor: self.bottomAnchor) } 49 | 50 | var rightTopAnchor: NSLayoutLocation { .init(xAxisAnchor: self.rightAnchor, yAxisAnchor: self.topAnchor) } 51 | var rightCenterAnchor: NSLayoutLocation { .init(xAxisAnchor: self.rightAnchor, yAxisAnchor: self.centerYAnchor) } 52 | var rightBottomAnchor: NSLayoutLocation { .init(xAxisAnchor: self.rightAnchor, yAxisAnchor: self.bottomAnchor) } 53 | } 54 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Anchor combinations/NSLayoutSize.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSLayoutSize.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 1.07.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | @MainActor 13 | public struct NSLayoutSize { 14 | let widthAnchor: NSLayoutDimension 15 | let heightAnchor: NSLayoutDimension 16 | } 17 | 18 | 19 | @MainActor 20 | public protocol LayoutSizeProvider { 21 | var widthAnchor: NSLayoutDimension { get } 22 | var heightAnchor: NSLayoutDimension { get } 23 | } 24 | extension UIView: LayoutSizeProvider {} 25 | extension UILayoutGuide: LayoutSizeProvider {} 26 | 27 | public extension LayoutSizeProvider { 28 | var sizeAnchor: NSLayoutSize { .init(widthAnchor: self.widthAnchor, heightAnchor: self.heightAnchor) } 29 | } 30 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Anchor combinations/NSLayoutXAxisEdges.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSLayoutXAxisEdges.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 1.07.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | @MainActor 13 | public struct NSLayoutXAxisEdges { 14 | let leadingAxisAnchor: NSLayoutXAxisAnchor 15 | let trailingAxisAnchor: NSLayoutXAxisAnchor 16 | } 17 | 18 | 19 | @MainActor 20 | public protocol LayoutXAxisEdgesProvider { 21 | var leadingAnchor: NSLayoutXAxisAnchor { get } 22 | var trailingAnchor: NSLayoutXAxisAnchor { get } 23 | } 24 | extension UIView: LayoutXAxisEdgesProvider {} 25 | extension UILayoutGuide: LayoutXAxisEdgesProvider {} 26 | 27 | public extension LayoutXAxisEdgesProvider { 28 | var horizontalEdgesAnchor: NSLayoutXAxisEdges { .init(leadingAxisAnchor: self.leadingAnchor, trailingAxisAnchor: self.trailingAnchor) } 29 | } 30 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Anchor combinations/NSLayoutYAxisEdges.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSLayoutYAxisEdges.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 1.07.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | @MainActor 13 | public struct NSLayoutYAxisEdges { 14 | let topAxisAnchor: NSLayoutYAxisAnchor 15 | let bottomAxisAnchor: NSLayoutYAxisAnchor 16 | } 17 | 18 | 19 | @MainActor 20 | public protocol LayoutYAxisEdgesProvider { 21 | var topAnchor: NSLayoutYAxisAnchor { get } 22 | var bottomAnchor: NSLayoutYAxisAnchor { get } 23 | } 24 | extension UIView: LayoutYAxisEdgesProvider {} 25 | extension UILayoutGuide: LayoutYAxisEdgesProvider {} 26 | 27 | public extension LayoutYAxisEdgesProvider { 28 | var verticalEdgesAnchor: NSLayoutYAxisEdges { .init(topAxisAnchor: self.topAnchor, bottomAxisAnchor: self.bottomAnchor) } 29 | } 30 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraint Paramters/DimensionConstraintParametersProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DimensionConstraintParametersProvider.swift 3 | // FlooidLayout 4 | // 5 | // Created by Martin Lalev on 12.07.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | 13 | @MainActor 14 | public protocol DimensionConstraintParametersProvider { 15 | var anchor: NSLayoutDimension { get } 16 | var constant: CGFloat { get } 17 | var multiplyer: CGFloat { get } 18 | } 19 | 20 | public struct DimensionConstraintParameters: DimensionConstraintParametersProvider { 21 | public let anchor: NSLayoutDimension 22 | public let constant: CGFloat 23 | public let multiplyer: CGFloat 24 | } 25 | 26 | extension NSLayoutDimension: DimensionConstraintParametersProvider { 27 | public var anchor: NSLayoutDimension { return self } 28 | public var constant: CGFloat { return 0 } 29 | public var multiplyer: CGFloat { return 1 } 30 | } 31 | 32 | 33 | 34 | infix operator ++: AdditionPrecedence 35 | infix operator --: AdditionPrecedence 36 | 37 | extension DimensionConstraintParametersProvider { 38 | func expand(by value: CGFloat) -> DimensionConstraintParameters { return .init(anchor: self.anchor, constant: value, multiplyer: self.multiplyer) } 39 | func scale(by value: CGFloat) -> DimensionConstraintParameters { return .init(anchor: self.anchor, constant: self.constant, multiplyer: value) } 40 | } 41 | 42 | @MainActor 43 | public func + (lhs: DimensionConstraintParametersProvider, rhs: CGFloat) -> DimensionConstraintParameters { return lhs.expand(by: rhs) } 44 | @MainActor 45 | public func ++ (lhs: DimensionConstraintParametersProvider, rhs: CGFloat) -> DimensionConstraintParameters { return lhs.expand(by: 2*rhs) } 46 | @MainActor 47 | public func - (lhs: DimensionConstraintParametersProvider, rhs: CGFloat) -> DimensionConstraintParameters { return lhs.expand(by: -rhs) } 48 | @MainActor 49 | public func -- (lhs: DimensionConstraintParametersProvider, rhs: CGFloat) -> DimensionConstraintParameters { return lhs.expand(by: -2*rhs) } 50 | @MainActor 51 | public func * (lhs: DimensionConstraintParametersProvider, rhs: CGFloat) -> DimensionConstraintParameters { return lhs.scale(by: rhs) } 52 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraint Paramters/EdgesConstraintParametersProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EdgesConstraintParametersProvider.swift 3 | // FlooidLayout 4 | // 5 | // Created by Martin Lalev on 12.07.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | 13 | @MainActor 14 | public protocol EdgesConstraintParametersProvider { 15 | var leadingProperties: XAxisConstraintParametersProvider { get } 16 | var trailingProperties: XAxisConstraintParametersProvider { get } 17 | var topProperties: YAxisConstraintParametersProvider { get } 18 | var bottomProperties: YAxisConstraintParametersProvider { get } 19 | } 20 | 21 | public struct EdgesConstraintParameters: EdgesConstraintParametersProvider { 22 | public let leadingProperties: XAxisConstraintParametersProvider 23 | public let trailingProperties: XAxisConstraintParametersProvider 24 | public let topProperties: YAxisConstraintParametersProvider 25 | public let bottomProperties: YAxisConstraintParametersProvider 26 | } 27 | 28 | extension NSLayoutEdges: EdgesConstraintParametersProvider { 29 | public var leadingProperties: XAxisConstraintParametersProvider { return self.leadingAxisAnchor } 30 | public var trailingProperties: XAxisConstraintParametersProvider { return self.trailingAxisAnchor } 31 | public var topProperties: YAxisConstraintParametersProvider { return self.topAxisAnchor } 32 | public var bottomProperties: YAxisConstraintParametersProvider { return self.bottomAxisAnchor } 33 | } 34 | 35 | 36 | 37 | extension EdgesConstraintParametersProvider { 38 | func expand(leading: CGFloat, trailing: CGFloat, top: CGFloat, bottom: CGFloat) -> EdgesConstraintParameters { 39 | return .init( 40 | leadingProperties: self.leadingProperties.move(by: -leading), 41 | trailingProperties: self.trailingProperties.move(by: trailing), 42 | topProperties: self.topProperties.move(by: -top), 43 | bottomProperties: self.bottomProperties.move(by: bottom) 44 | ) 45 | } 46 | func expand(horizontally: CGFloat, vertically: CGFloat) -> EdgesConstraintParameters { return self.expand(leading: horizontally, trailing: horizontally, top: vertically, bottom: vertically) } 47 | func expand(by value: CGFloat) -> EdgesConstraintParameters { return self.expand(leading: value, trailing: value, top: value, bottom: value) } 48 | func inset(leading: CGFloat, trailing: CGFloat, top: CGFloat, bottom: CGFloat) -> EdgesConstraintParameters { return self.expand(leading: -leading, trailing: -trailing, top: -top, bottom: -bottom) } 49 | func inset(horizontally: CGFloat, vertically: CGFloat) -> EdgesConstraintParameters { return self.expand(horizontally: -horizontally, vertically: -vertically) } 50 | func inset(by value: CGFloat) -> EdgesConstraintParameters { return self.expand(by: -value) } 51 | } 52 | 53 | @MainActor 54 | public func ++ (lhs: EdgesConstraintParameters, rhs: CGFloat) -> EdgesConstraintParameters { return lhs.expand(by: rhs) } 55 | @MainActor 56 | public func -- (lhs: EdgesConstraintParameters, rhs: CGFloat) -> EdgesConstraintParameters { return lhs.expand(by: -rhs) } 57 | @MainActor 58 | public func ++ (lhs: EdgesConstraintParameters, rhs: (CGFloat,CGFloat)) -> EdgesConstraintParameters { return lhs.expand(horizontally: rhs.0, vertically: rhs.1) } 59 | @MainActor 60 | public func -- (lhs: EdgesConstraintParameters, rhs: (CGFloat,CGFloat)) -> EdgesConstraintParameters { return lhs.expand(horizontally: -rhs.0, vertically: -rhs.1) } 61 | @MainActor 62 | public func ++ (lhs: EdgesConstraintParameters, rhs: (CGFloat,CGFloat,CGFloat,CGFloat)) -> EdgesConstraintParameters { return lhs.expand(leading: rhs.0, trailing: rhs.1, top: rhs.2, bottom: rhs.3) } 63 | @MainActor 64 | public func -- (lhs: EdgesConstraintParameters, rhs: (CGFloat,CGFloat,CGFloat,CGFloat)) -> EdgesConstraintParameters { return lhs.expand(leading: -rhs.0, trailing: -rhs.1, top: rhs.2, bottom: rhs.3) } 65 | @MainActor 66 | public func ++ (lhs: EdgesConstraintParameters, rhs: NSDirectionalEdgeInsets) -> EdgesConstraintParameters { return lhs.expand(leading: rhs.leading, trailing: rhs.trailing, top: rhs.top, bottom: rhs.bottom) } 67 | @MainActor 68 | public func -- (lhs: EdgesConstraintParameters, rhs: NSDirectionalEdgeInsets) -> EdgesConstraintParameters { return lhs.expand(leading: -rhs.leading, trailing: -rhs.trailing, top: rhs.top, bottom: rhs.bottom) } 69 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraint Paramters/LocationConstraintParametersProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationConstraintParametersProvider.swift 3 | // FlooidLayout 4 | // 5 | // Created by Martin Lalev on 12.07.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | 13 | @MainActor 14 | public protocol LocationConstraintParametersProvider { 15 | var xAxisProperties: XAxisConstraintParametersProvider { get } 16 | var yAxisProperties: YAxisConstraintParametersProvider { get } 17 | } 18 | 19 | public struct LocationConstraintParameters: LocationConstraintParametersProvider { 20 | public let xAxisProperties: XAxisConstraintParametersProvider 21 | public let yAxisProperties: YAxisConstraintParametersProvider 22 | } 23 | 24 | extension NSLayoutLocation: LocationConstraintParametersProvider { 25 | public var xAxisProperties: XAxisConstraintParametersProvider { return self.xAxisAnchor } 26 | public var yAxisProperties: YAxisConstraintParametersProvider { return self.yAxisAnchor } 27 | } 28 | 29 | 30 | 31 | extension LocationConstraintParametersProvider { 32 | func move(horizontally: CGFloat, vertically: CGFloat) -> LocationConstraintParameters { return .init(xAxisProperties: self.xAxisProperties.move(by: horizontally), yAxisProperties: self.yAxisProperties.move(by: vertically)) } 33 | func move(by value: CGFloat) -> LocationConstraintParameters { return self.move(horizontally: value, vertically: value) } 34 | } 35 | 36 | @MainActor 37 | public func + (lhs: LocationConstraintParametersProvider, rhs: CGFloat) -> LocationConstraintParameters { return lhs.move(by: rhs) } 38 | @MainActor 39 | public func - (lhs: LocationConstraintParametersProvider, rhs: CGFloat) -> LocationConstraintParameters { return lhs.move(by: -rhs) } 40 | @MainActor 41 | public func + (lhs: LocationConstraintParametersProvider, rhs: (CGFloat,CGFloat)) -> LocationConstraintParameters { return lhs.move(horizontally: rhs.0, vertically: rhs.1) } 42 | @MainActor 43 | public func - (lhs: LocationConstraintParametersProvider, rhs: (CGFloat,CGFloat)) -> LocationConstraintParameters { return lhs.move(horizontally: -rhs.0, vertically: -rhs.1) } 44 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraint Paramters/SizeConstraintParametersProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SizeConstraintParametersProvider.swift 3 | // FlooidLayout 4 | // 5 | // Created by Martin Lalev on 12.07.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | 13 | @MainActor 14 | public protocol SizeConstraintParametersProvider { 15 | var widthProperties: DimensionConstraintParametersProvider { get } 16 | var heightProperties: DimensionConstraintParametersProvider { get } 17 | } 18 | 19 | public struct SizeConstraintParameters: SizeConstraintParametersProvider { 20 | public let widthProperties: DimensionConstraintParametersProvider 21 | public let heightProperties: DimensionConstraintParametersProvider 22 | } 23 | 24 | extension NSLayoutSize: SizeConstraintParametersProvider { 25 | public var widthProperties: DimensionConstraintParametersProvider { return self.widthAnchor } 26 | public var heightProperties: DimensionConstraintParametersProvider { return self.heightAnchor } 27 | } 28 | 29 | 30 | 31 | extension SizeConstraintParametersProvider { 32 | func expand(width: CGFloat, height: CGFloat) -> SizeConstraintParameters { return .init(widthProperties: self.widthProperties.expand(by: width), heightProperties: self.heightProperties.expand(by: height)) } 33 | func scale(width: CGFloat, height: CGFloat) -> SizeConstraintParameters { return .init(widthProperties: self.widthProperties.scale(by: width), heightProperties: self.heightProperties.scale(by: height)) } 34 | func expand(by value: CGFloat) -> SizeConstraintParameters { return self.expand(width: value, height: value) } 35 | func scale(by value: CGFloat) -> SizeConstraintParameters { return self.scale(width: value, height: value) } 36 | } 37 | 38 | @MainActor 39 | public func + (lhs: SizeConstraintParametersProvider, rhs: CGFloat) -> SizeConstraintParameters { return lhs.expand(by: rhs) } 40 | @MainActor 41 | public func - (lhs: SizeConstraintParametersProvider, rhs: CGFloat) -> SizeConstraintParameters { return lhs.expand(by: -rhs) } 42 | @MainActor 43 | public func ++ (lhs: SizeConstraintParametersProvider, rhs: CGFloat) -> SizeConstraintParameters { return lhs.expand(by: 2*rhs) } 44 | @MainActor 45 | public func -- (lhs: SizeConstraintParametersProvider, rhs: CGFloat) -> SizeConstraintParameters { return lhs.expand(by: -2*rhs) } 46 | @MainActor 47 | public func * (lhs: SizeConstraintParametersProvider, rhs: CGFloat) -> SizeConstraintParameters { return lhs.scale(by: rhs) } 48 | @MainActor 49 | public func + (lhs: SizeConstraintParametersProvider, rhs: (CGFloat,CGFloat)) -> SizeConstraintParameters { return lhs.expand(width: rhs.0, height: rhs.1) } 50 | @MainActor 51 | public func - (lhs: SizeConstraintParametersProvider, rhs: (CGFloat,CGFloat)) -> SizeConstraintParameters { return lhs.expand(width: -rhs.0, height: -rhs.1) } 52 | @MainActor 53 | public func ++ (lhs: SizeConstraintParametersProvider, rhs: (CGFloat,CGFloat)) -> SizeConstraintParameters { return lhs.expand(width: 2*rhs.0, height: 2*rhs.1) } 54 | @MainActor 55 | public func -- (lhs: SizeConstraintParametersProvider, rhs: (CGFloat,CGFloat)) -> SizeConstraintParameters { return lhs.expand(width: -2*rhs.0, height: -2*rhs.1) } 56 | @MainActor 57 | public func * (lhs: SizeConstraintParametersProvider, rhs: (CGFloat,CGFloat)) -> SizeConstraintParameters { return lhs.scale(width: rhs.0, height: rhs.1) } 58 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraint Paramters/XAxisConstraintParametersProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XAxisConstraintParametersProvider.swift 3 | // FlooidLayout 4 | // 5 | // Created by Martin Lalev on 12.07.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | 13 | @MainActor 14 | public protocol XAxisConstraintParametersProvider { 15 | var anchor: NSLayoutXAxisAnchor { get } 16 | var constant: CGFloat { get } 17 | } 18 | 19 | public struct XAxisConstraintParameters: XAxisConstraintParametersProvider { 20 | public let anchor: NSLayoutXAxisAnchor 21 | public let constant: CGFloat 22 | } 23 | 24 | extension NSLayoutXAxisAnchor: XAxisConstraintParametersProvider { 25 | public var anchor: NSLayoutXAxisAnchor { return self } 26 | public var constant: CGFloat { return 0 } 27 | } 28 | 29 | 30 | 31 | extension XAxisConstraintParametersProvider { 32 | func move(by value: CGFloat) -> XAxisConstraintParameters { return .init(anchor: self.anchor, constant: value) } 33 | } 34 | 35 | @MainActor 36 | public func + (lhs: XAxisConstraintParametersProvider, rhs: CGFloat) -> XAxisConstraintParameters { return lhs.move(by: rhs) } 37 | @MainActor 38 | public func - (lhs: XAxisConstraintParametersProvider, rhs: CGFloat) -> XAxisConstraintParameters { return lhs.move(by: -rhs) } 39 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraint Paramters/XAxisEdgesConstraintParametersProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XAxisEdgesConstraintParametersProvider.swift 3 | // FlooidLayout 4 | // 5 | // Created by Martin Lalev on 12.07.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | 13 | @MainActor 14 | public protocol XAxisEdgesConstraintParametersProvider { 15 | var leadingProperties: XAxisConstraintParametersProvider { get } 16 | var trailingProperties: XAxisConstraintParametersProvider { get } 17 | } 18 | 19 | public struct XAxisEdgesConstraintParameters: XAxisEdgesConstraintParametersProvider { 20 | public let leadingProperties: XAxisConstraintParametersProvider 21 | public let trailingProperties: XAxisConstraintParametersProvider 22 | } 23 | 24 | extension NSLayoutXAxisEdges: XAxisEdgesConstraintParametersProvider { 25 | public var leadingProperties: XAxisConstraintParametersProvider { return self.leadingAxisAnchor } 26 | public var trailingProperties: XAxisConstraintParametersProvider { return self.trailingAxisAnchor } 27 | } 28 | 29 | 30 | 31 | extension XAxisEdgesConstraintParametersProvider { 32 | func expand(leading: CGFloat, trailing: CGFloat) -> XAxisEdgesConstraintParameters { return .init(leadingProperties: self.leadingProperties.move(by: -leading), trailingProperties: self.trailingProperties.move(by: trailing)) } 33 | func expand(by value: CGFloat) -> XAxisEdgesConstraintParameters { return self.expand(leading: value, trailing: value) } 34 | } 35 | 36 | @MainActor 37 | public func ++ (lhs: XAxisEdgesConstraintParametersProvider, rhs: CGFloat) -> XAxisEdgesConstraintParameters { return lhs.expand(by: rhs) } 38 | @MainActor 39 | public func -- (lhs: XAxisEdgesConstraintParametersProvider, rhs: CGFloat) -> XAxisEdgesConstraintParameters { return lhs.expand(by: -rhs) } 40 | @MainActor 41 | public func ++ (lhs: XAxisEdgesConstraintParametersProvider, rhs: (CGFloat,CGFloat)) -> XAxisEdgesConstraintParameters { return lhs.expand(leading: rhs.0, trailing: rhs.1) } 42 | @MainActor 43 | public func -- (lhs: XAxisEdgesConstraintParametersProvider, rhs: (CGFloat,CGFloat)) -> XAxisEdgesConstraintParameters { return lhs.expand(leading: -rhs.0, trailing: -rhs.1) } 44 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraint Paramters/YAxisConstraintParametersProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YAxisConstraintParametersProvider.swift 3 | // FlooidLayout 4 | // 5 | // Created by Martin Lalev on 12.07.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | 13 | @MainActor 14 | public protocol YAxisConstraintParametersProvider { 15 | var anchor: NSLayoutYAxisAnchor { get } 16 | var constant: CGFloat { get } 17 | } 18 | 19 | public struct YAxisConstraintParameters: YAxisConstraintParametersProvider { 20 | public let anchor: NSLayoutYAxisAnchor 21 | public let constant: CGFloat 22 | } 23 | 24 | extension NSLayoutYAxisAnchor: YAxisConstraintParametersProvider { 25 | public var anchor: NSLayoutYAxisAnchor { return self } 26 | public var constant: CGFloat { return 0 } 27 | } 28 | 29 | 30 | 31 | extension YAxisConstraintParametersProvider { 32 | func move(by value: CGFloat) -> YAxisConstraintParameters { return .init(anchor: self.anchor, constant: value) } 33 | } 34 | 35 | @MainActor 36 | public func + (lhs: YAxisConstraintParametersProvider, rhs: CGFloat) -> YAxisConstraintParameters { return lhs.move(by: rhs) } 37 | @MainActor 38 | public func - (lhs: YAxisConstraintParametersProvider, rhs: CGFloat) -> YAxisConstraintParameters { return lhs.move(by: -rhs) } 39 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraint Paramters/YAxisEdgesConstraintParametersProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YAxisEdgesConstraintParametersProvider.swift 3 | // FlooidLayout 4 | // 5 | // Created by Martin Lalev on 12.07.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | 13 | @MainActor 14 | public protocol YAxisEdgesConstraintParametersProvider { 15 | var topProperties: YAxisConstraintParametersProvider { get } 16 | var bottomProperties: YAxisConstraintParametersProvider { get } 17 | } 18 | 19 | public struct YAxisEdgesConstraintParameters: YAxisEdgesConstraintParametersProvider { 20 | public let topProperties: YAxisConstraintParametersProvider 21 | public let bottomProperties: YAxisConstraintParametersProvider 22 | } 23 | 24 | extension NSLayoutYAxisEdges: YAxisEdgesConstraintParametersProvider { 25 | public var topProperties: YAxisConstraintParametersProvider { return self.topAxisAnchor } 26 | public var bottomProperties: YAxisConstraintParametersProvider { return self.bottomAxisAnchor } 27 | } 28 | 29 | 30 | 31 | extension YAxisEdgesConstraintParametersProvider { 32 | func expand(top: CGFloat, bottom: CGFloat) -> YAxisEdgesConstraintParameters { return .init(topProperties: self.topProperties.move(by: -top), bottomProperties: self.bottomProperties.move(by: bottom)) } 33 | func expand(by value: CGFloat) -> YAxisEdgesConstraintParameters { return self.expand(top: value, bottom: value) } 34 | } 35 | 36 | @MainActor 37 | public func ++ (lhs: YAxisEdgesConstraintParametersProvider, rhs: CGFloat) -> YAxisEdgesConstraintParameters { return lhs.expand(by: rhs) } 38 | @MainActor 39 | public func -- (lhs: YAxisEdgesConstraintParametersProvider, rhs: CGFloat) -> YAxisEdgesConstraintParameters { return lhs.expand(by: -rhs) } 40 | @MainActor 41 | public func ++ (lhs: YAxisEdgesConstraintParametersProvider, rhs: (CGFloat,CGFloat)) -> YAxisEdgesConstraintParameters { return lhs.expand(top: rhs.0, bottom: rhs.1) } 42 | @MainActor 43 | public func -- (lhs: YAxisEdgesConstraintParametersProvider, rhs: (CGFloat,CGFloat)) -> YAxisEdgesConstraintParameters { return lhs.expand(top: -rhs.0, bottom: -rhs.1) } 44 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraints Creation/LayoutConstraintDimensionBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayoutConstraintDimensionBuilder.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 30.06.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public extension NSLayoutDimension { 13 | 14 | @discardableResult 15 | static func == (lhs: NSLayoutDimension, rhs: NSLayoutDimension) -> NSLayoutConstraint { 16 | return lhs.constraint(equalTo: rhs.anchor, multiplier: rhs.multiplyer, constant: rhs.constant) 17 | } 18 | 19 | @discardableResult 20 | static func <= (lhs: NSLayoutDimension, rhs: NSLayoutDimension) -> NSLayoutConstraint { 21 | return lhs.constraint(lessThanOrEqualTo: rhs.anchor, multiplier: rhs.multiplyer, constant: rhs.constant) 22 | } 23 | 24 | @discardableResult 25 | static func >= (lhs: NSLayoutDimension, rhs: NSLayoutDimension) -> NSLayoutConstraint { 26 | return lhs.constraint(greaterThanOrEqualTo: rhs.anchor, multiplier: rhs.multiplyer, constant: rhs.constant) 27 | } 28 | 29 | @discardableResult 30 | static func == (lhs: NSLayoutDimension, rhs: DimensionConstraintParametersProvider) -> NSLayoutConstraint { 31 | return lhs.constraint(equalTo: rhs.anchor, multiplier: rhs.multiplyer, constant: rhs.constant) 32 | } 33 | 34 | @discardableResult 35 | static func <= (lhs: NSLayoutDimension, rhs: DimensionConstraintParametersProvider) -> NSLayoutConstraint { 36 | return lhs.constraint(lessThanOrEqualTo: rhs.anchor, multiplier: rhs.multiplyer, constant: rhs.constant) 37 | } 38 | 39 | @discardableResult 40 | static func >= (lhs: NSLayoutDimension, rhs: DimensionConstraintParametersProvider) -> NSLayoutConstraint { 41 | return lhs.constraint(greaterThanOrEqualTo: rhs.anchor, multiplier: rhs.multiplyer, constant: rhs.constant) 42 | } 43 | 44 | @discardableResult 45 | static func == (lhs: NSLayoutDimension, rhs: CGFloat) -> NSLayoutConstraint { 46 | return lhs.constraint(equalToConstant: rhs) 47 | } 48 | 49 | @discardableResult 50 | static func <= (lhs: NSLayoutDimension, rhs: CGFloat) -> NSLayoutConstraint { 51 | return lhs.constraint(lessThanOrEqualToConstant: rhs) 52 | } 53 | 54 | @discardableResult 55 | static func >= (lhs: NSLayoutDimension, rhs: CGFloat) -> NSLayoutConstraint { 56 | return lhs.constraint(greaterThanOrEqualToConstant: rhs) 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraints Creation/LayoutConstraintEdgesBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayoutConstraintEdgesBuilder.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 30.06.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public struct NSLayoutEdgeConstraints { 13 | public let leading: NSLayoutConstraint 14 | public let trailing: NSLayoutConstraint 15 | public let top: NSLayoutConstraint 16 | public let bottom: NSLayoutConstraint 17 | } 18 | 19 | public extension NSLayoutEdges { 20 | 21 | @discardableResult 22 | static func == (lhs: NSLayoutEdges, rhs: NSLayoutEdges) -> NSLayoutEdgeConstraints { 23 | return .init(leading: lhs.leadingAxisAnchor == rhs.leadingProperties, trailing: lhs.trailingAxisAnchor == rhs.trailingProperties, top: lhs.topAxisAnchor == rhs.topProperties, bottom: lhs.bottomAxisAnchor == rhs.bottomProperties) 24 | } 25 | 26 | @discardableResult 27 | static func == (lhs: NSLayoutEdges, rhs: EdgesConstraintParametersProvider) -> NSLayoutEdgeConstraints { 28 | return .init(leading: lhs.leadingAxisAnchor == rhs.leadingProperties, trailing: lhs.trailingAxisAnchor == rhs.trailingProperties, top: lhs.topAxisAnchor == rhs.topProperties, bottom: lhs.bottomAxisAnchor == rhs.bottomProperties) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraints Creation/LayoutConstraintLocationBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayoutConstraintLocationBuilder.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 30.06.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public struct NSLayoutLocationConstraints { 13 | public let x: NSLayoutConstraint 14 | public let y: NSLayoutConstraint 15 | } 16 | 17 | public extension NSLayoutLocation { 18 | 19 | @discardableResult 20 | static func == (lhs: NSLayoutLocation, rhs: NSLayoutLocation) -> NSLayoutLocationConstraints { 21 | return .init(x: lhs.xAxisAnchor == rhs.xAxisProperties, y: lhs.yAxisAnchor == rhs.yAxisProperties) 22 | } 23 | 24 | @discardableResult 25 | static func <= (lhs: NSLayoutLocation, rhs: NSLayoutLocation) -> NSLayoutLocationConstraints { 26 | return .init(x: lhs.xAxisAnchor <= rhs.xAxisProperties, y: lhs.yAxisAnchor <= rhs.yAxisProperties) 27 | } 28 | 29 | @discardableResult 30 | static func >= (lhs: NSLayoutLocation, rhs: NSLayoutLocation) -> NSLayoutLocationConstraints { 31 | return .init(x: lhs.xAxisAnchor >= rhs.xAxisProperties, y: lhs.yAxisAnchor >= rhs.yAxisProperties) 32 | } 33 | 34 | @discardableResult 35 | static func == (lhs: NSLayoutLocation, rhs: LocationConstraintParametersProvider) -> NSLayoutLocationConstraints { 36 | return .init(x: lhs.xAxisAnchor == rhs.xAxisProperties, y: lhs.yAxisAnchor == rhs.yAxisProperties) 37 | } 38 | 39 | @discardableResult 40 | static func <= (lhs: NSLayoutLocation, rhs: LocationConstraintParametersProvider) -> NSLayoutLocationConstraints { 41 | return .init(x: lhs.xAxisAnchor <= rhs.xAxisProperties, y: lhs.yAxisAnchor <= rhs.yAxisProperties) 42 | } 43 | 44 | @discardableResult 45 | static func >= (lhs: NSLayoutLocation, rhs: LocationConstraintParametersProvider) -> NSLayoutLocationConstraints { 46 | return .init(x: lhs.xAxisAnchor >= rhs.xAxisProperties, y: lhs.yAxisAnchor >= rhs.yAxisProperties) 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraints Creation/LayoutConstraintSizeBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayoutConstraintSizeBuilder.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 30.06.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public struct NSLayoutSizeConstraints { 13 | public let width: NSLayoutConstraint 14 | public let height: NSLayoutConstraint 15 | } 16 | 17 | public extension NSLayoutSize { 18 | 19 | @discardableResult 20 | static func == (lhs: NSLayoutSize, rhs: NSLayoutSize) -> NSLayoutSizeConstraints { 21 | return .init(width: lhs.widthAnchor == rhs.widthProperties, height: lhs.heightAnchor == rhs.heightProperties) 22 | } 23 | 24 | @discardableResult 25 | static func <= (lhs: NSLayoutSize, rhs: NSLayoutSize) -> NSLayoutSizeConstraints { 26 | return .init(width: lhs.widthAnchor <= rhs.widthProperties, height: lhs.heightAnchor <= rhs.heightProperties) 27 | } 28 | 29 | @discardableResult 30 | static func >= (lhs: NSLayoutSize, rhs: NSLayoutSize) -> NSLayoutSizeConstraints { 31 | return .init(width: lhs.widthAnchor >= rhs.widthProperties, height: lhs.heightAnchor >= rhs.heightProperties) 32 | } 33 | 34 | @discardableResult 35 | static func == (lhs: NSLayoutSize, rhs: SizeConstraintParametersProvider) -> NSLayoutSizeConstraints { 36 | return .init(width: lhs.widthAnchor == rhs.widthProperties, height: lhs.heightAnchor == rhs.heightProperties) 37 | } 38 | 39 | @discardableResult 40 | static func <= (lhs: NSLayoutSize, rhs: SizeConstraintParametersProvider) -> NSLayoutSizeConstraints { 41 | return .init(width: lhs.widthAnchor <= rhs.widthProperties, height: lhs.heightAnchor <= rhs.heightProperties) 42 | } 43 | 44 | @discardableResult 45 | static func >= (lhs: NSLayoutSize, rhs: SizeConstraintParametersProvider) -> NSLayoutSizeConstraints { 46 | return .init(width: lhs.widthAnchor >= rhs.widthProperties, height: lhs.heightAnchor >= rhs.heightProperties) 47 | } 48 | 49 | @discardableResult 50 | static func == (lhs: NSLayoutSize, rhs: CGSize) -> NSLayoutSizeConstraints { 51 | return .init(width: lhs.widthAnchor == rhs.width, height: lhs.heightAnchor == rhs.height) 52 | } 53 | 54 | @discardableResult 55 | static func <= (lhs: NSLayoutSize, rhs: CGSize) -> NSLayoutSizeConstraints { 56 | return .init(width: lhs.widthAnchor <= rhs.width, height: lhs.heightAnchor <= rhs.height) 57 | } 58 | 59 | @discardableResult 60 | static func >= (lhs: NSLayoutSize, rhs: CGSize) -> NSLayoutSizeConstraints { 61 | return .init(width: lhs.widthAnchor >= rhs.width, height: lhs.heightAnchor >= rhs.height) 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraints Creation/LayoutConstraintXAxisBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayoutConstraintXAxisBuilder.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 30.06.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public extension NSLayoutXAxisAnchor { 13 | 14 | @discardableResult 15 | static func == (lhs: NSLayoutXAxisAnchor, rhs: NSLayoutXAxisAnchor) -> NSLayoutConstraint { 16 | return lhs.constraint(equalTo: rhs.anchor, constant: rhs.constant) 17 | } 18 | 19 | @discardableResult 20 | static func <= (lhs: NSLayoutXAxisAnchor, rhs: NSLayoutXAxisAnchor) -> NSLayoutConstraint { 21 | return lhs.constraint(lessThanOrEqualTo: rhs.anchor, constant: rhs.constant) 22 | } 23 | 24 | @discardableResult 25 | static func >= (lhs: NSLayoutXAxisAnchor, rhs: NSLayoutXAxisAnchor) -> NSLayoutConstraint { 26 | return lhs.constraint(greaterThanOrEqualTo: rhs.anchor, constant: rhs.constant) 27 | } 28 | 29 | @discardableResult 30 | static func == (lhs: NSLayoutXAxisAnchor, rhs: XAxisConstraintParametersProvider) -> NSLayoutConstraint { 31 | return lhs.constraint(equalTo: rhs.anchor, constant: rhs.constant) 32 | } 33 | 34 | @discardableResult 35 | static func <= (lhs: NSLayoutXAxisAnchor, rhs: XAxisConstraintParametersProvider) -> NSLayoutConstraint { 36 | return lhs.constraint(lessThanOrEqualTo: rhs.anchor, constant: rhs.constant) 37 | } 38 | 39 | @discardableResult 40 | static func >= (lhs: NSLayoutXAxisAnchor, rhs: XAxisConstraintParametersProvider) -> NSLayoutConstraint { 41 | return lhs.constraint(greaterThanOrEqualTo: rhs.anchor, constant: rhs.constant) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraints Creation/LayoutConstraintXAxisEdgesBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayoutConstraintXAxisEdgesBuilder.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 30.06.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public struct NSLayoutXAxisEdgesConstraints { 13 | public let leading: NSLayoutConstraint 14 | public let trailing: NSLayoutConstraint 15 | } 16 | 17 | public extension NSLayoutXAxisEdges { 18 | 19 | @discardableResult 20 | static func == (lhs: NSLayoutXAxisEdges, rhs: NSLayoutXAxisEdges) -> NSLayoutXAxisEdgesConstraints { 21 | return .init(leading: lhs.leadingAxisAnchor == rhs.leadingProperties, trailing: lhs.trailingAxisAnchor == rhs.trailingProperties) 22 | } 23 | 24 | @discardableResult 25 | static func == (lhs: NSLayoutXAxisEdges, rhs: XAxisEdgesConstraintParametersProvider) -> NSLayoutXAxisEdgesConstraints { 26 | return .init(leading: lhs.leadingAxisAnchor == rhs.leadingProperties, trailing: lhs.trailingAxisAnchor == rhs.trailingProperties) 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraints Creation/LayoutConstraintYAxisBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayoutConstraintYAxisBuilder.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 30.06.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public extension NSLayoutYAxisAnchor { 13 | 14 | @discardableResult 15 | static func == (lhs: NSLayoutYAxisAnchor, rhs: NSLayoutYAxisAnchor) -> NSLayoutConstraint { 16 | return lhs.constraint(equalTo: rhs.anchor, constant: rhs.constant) 17 | } 18 | 19 | @discardableResult 20 | static func <= (lhs: NSLayoutYAxisAnchor, rhs: NSLayoutYAxisAnchor) -> NSLayoutConstraint { 21 | return lhs.constraint(lessThanOrEqualTo: rhs.anchor, constant: rhs.constant) 22 | } 23 | 24 | @discardableResult 25 | static func >= (lhs: NSLayoutYAxisAnchor, rhs: NSLayoutYAxisAnchor) -> NSLayoutConstraint { 26 | return lhs.constraint(greaterThanOrEqualTo: rhs.anchor, constant: rhs.constant) 27 | } 28 | 29 | @discardableResult 30 | static func == (lhs: NSLayoutYAxisAnchor, rhs: YAxisConstraintParametersProvider) -> NSLayoutConstraint { 31 | return lhs.constraint(equalTo: rhs.anchor, constant: rhs.constant) 32 | } 33 | 34 | @discardableResult 35 | static func <= (lhs: NSLayoutYAxisAnchor, rhs: YAxisConstraintParametersProvider) -> NSLayoutConstraint { 36 | return lhs.constraint(lessThanOrEqualTo: rhs.anchor, constant: rhs.constant) 37 | } 38 | 39 | @discardableResult 40 | static func >= (lhs: NSLayoutYAxisAnchor, rhs: YAxisConstraintParametersProvider) -> NSLayoutConstraint { 41 | return lhs.constraint(greaterThanOrEqualTo: rhs.anchor, constant: rhs.constant) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /FlooidLayout/Source/Constraints Creation/LayoutConstraintYAxisEdgesBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayoutConstraintYAxisEdgesBuilder.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 30.06.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public struct NSLayoutYAxisEdgesConstraints { 13 | public let top: NSLayoutConstraint 14 | public let bottom: NSLayoutConstraint 15 | } 16 | 17 | public extension NSLayoutYAxisEdges { 18 | 19 | @discardableResult 20 | static func == (lhs: NSLayoutYAxisEdges, rhs: NSLayoutYAxisEdges) -> NSLayoutYAxisEdgesConstraints { 21 | return .init(top: lhs.topAxisAnchor == rhs.topProperties, bottom: lhs.bottomAxisAnchor == rhs.bottomProperties) 22 | } 23 | 24 | @discardableResult 25 | static func == (lhs: NSLayoutYAxisEdges, rhs: YAxisEdgesConstraintParametersProvider) -> NSLayoutYAxisEdgesConstraints { 26 | return .init(top: lhs.topAxisAnchor == rhs.topProperties, bottom: lhs.bottomAxisAnchor == rhs.bottomProperties) 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /FlooidLayout/Source/NSLayoutConstraintAssignment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSLayoutConstraintAssignment.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 30.06.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | 13 | precedencegroup ConstraintAssignmentPrecedence { 14 | lowerThan: ComparisonPrecedence 15 | higherThan: LogicalConjunctionPrecedence 16 | associativity: left 17 | } 18 | 19 | infix operator -->: ConstraintAssignmentPrecedence 20 | 21 | public protocol InlineAssignable {} 22 | 23 | extension InlineAssignable { 24 | 25 | @discardableResult 26 | public static func --> (lpred: Self, rpred: inout Self) -> Self { 27 | rpred = lpred 28 | return lpred 29 | } 30 | 31 | @discardableResult 32 | public static func --> (lpred: Self, rpred: inout Self?) -> Self { 33 | rpred = lpred 34 | return lpred 35 | } 36 | 37 | } 38 | 39 | extension NSLayoutConstraint: InlineAssignable {} 40 | extension NSLayoutSizeConstraints: InlineAssignable {} 41 | extension NSLayoutLocationConstraints: InlineAssignable {} 42 | extension NSLayoutEdgeConstraints: InlineAssignable {} 43 | extension NSLayoutXAxisEdgesConstraints: InlineAssignable {} 44 | extension NSLayoutYAxisEdgesConstraints: InlineAssignable {} 45 | -------------------------------------------------------------------------------- /FlooidLayout/Source/NSLayoutConstraintPropertyModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSLayoutConstraintPropertyModifier.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 1.07.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | 13 | precedencegroup ConstraintPropertyModifierPrecedence { 14 | lowerThan: ComparisonPrecedence 15 | higherThan: ConstraintAssignmentPrecedence 16 | associativity: left 17 | } 18 | 19 | infix operator --!: ConstraintPropertyModifierPrecedence 20 | 21 | public enum NSLayoutConstraintProperty { 22 | case priority(UILayoutPriority) 23 | case identifier(String) 24 | case activated 25 | } 26 | 27 | extension NSLayoutConstraint { 28 | 29 | @discardableResult 30 | public static func --! (lpred: NSLayoutConstraint, rpred: NSLayoutConstraintProperty) -> NSLayoutConstraint { 31 | switch rpred { 32 | 33 | case .priority(let priority): 34 | lpred.priority = priority 35 | 36 | case .identifier(let identifier): 37 | lpred.identifier = identifier 38 | 39 | case .activated: 40 | lpred.isActive = true 41 | 42 | } 43 | return lpred 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /FlooidLayout/Source/NSLayoutConstraintsBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSLayoutConstraintsBuilder.swift 3 | // FlooidUI 4 | // 5 | // Created by Martin Lalev on 1.07.19. 6 | // Copyright © 2019 Martin Lalev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | 13 | 14 | // Function builder helper type 15 | 16 | @MainActor 17 | public protocol NSLayoutConstraintsAccumulation { 18 | var asMultipleConstraints: [NSLayoutConstraint] { get } 19 | } 20 | 21 | extension NSLayoutConstraint: NSLayoutConstraintsAccumulation { 22 | public var asMultipleConstraints: [NSLayoutConstraint] { [self] } 23 | } 24 | extension Array: NSLayoutConstraintsAccumulation where Element: NSLayoutConstraint { 25 | public var asMultipleConstraints: [NSLayoutConstraint] { self } 26 | } 27 | 28 | extension NSLayoutEdgeConstraints: NSLayoutConstraintsAccumulation { 29 | public var asMultipleConstraints: [NSLayoutConstraint] { [self.leading, self.trailing, self.top, self.bottom] } 30 | } 31 | extension NSLayoutSizeConstraints: NSLayoutConstraintsAccumulation { 32 | public var asMultipleConstraints: [NSLayoutConstraint] { [self.width, self.height] } 33 | } 34 | extension NSLayoutLocationConstraints: NSLayoutConstraintsAccumulation { 35 | public var asMultipleConstraints: [NSLayoutConstraint] { [self.x, self.y] } 36 | } 37 | extension NSLayoutXAxisEdgesConstraints: NSLayoutConstraintsAccumulation { 38 | public var asMultipleConstraints: [NSLayoutConstraint] { [self.leading, self.trailing] } 39 | } 40 | extension NSLayoutYAxisEdgesConstraints: NSLayoutConstraintsAccumulation { 41 | public var asMultipleConstraints: [NSLayoutConstraint] { [self.top, self.bottom] } 42 | } 43 | 44 | 45 | 46 | // Function builder 47 | 48 | @MainActor 49 | @resultBuilder 50 | public struct NSLayoutConstraintsBuilder { 51 | 52 | public static func buildBlock(_ components: NSLayoutConstraintsAccumulation ...) -> NSLayoutConstraintsAccumulation { 53 | return components.flatMap { $0.asMultipleConstraints } 54 | } 55 | 56 | public static func buildIf(_ component: NSLayoutConstraintsAccumulation?) -> NSLayoutConstraintsAccumulation { 57 | return component ?? [NSLayoutConstraint]() 58 | } 59 | 60 | public static func buildEither(first: NSLayoutConstraintsAccumulation) -> NSLayoutConstraintsAccumulation { 61 | return first 62 | } 63 | 64 | public static func buildEither(second: NSLayoutConstraintsAccumulation) -> NSLayoutConstraintsAccumulation { 65 | return second 66 | } 67 | 68 | } 69 | 70 | 71 | 72 | // Function builder usage 73 | 74 | public extension NSLayoutConstraint { 75 | 76 | static func activate(@NSLayoutConstraintsBuilder constraintsBuilder: () -> NSLayoutConstraintsAccumulation) { 77 | NSLayoutConstraint.activate(constraintsBuilder().asMultipleConstraints) 78 | } 79 | 80 | static func make(@NSLayoutConstraintsBuilder constraintsBuilder: () -> NSLayoutConstraintsAccumulation) -> [NSLayoutConstraint] { 81 | return constraintsBuilder().asMultipleConstraints 82 | } 83 | 84 | } 85 | 86 | public extension UIView { 87 | 88 | func constraints(@NSLayoutConstraintsBuilder constraintsBuilder: (UIView) -> NSLayoutConstraintsAccumulation) { 89 | self.translatesAutoresizingMaskIntoConstraints = false 90 | NSLayoutConstraint.activate(constraintsBuilder(self).asMultipleConstraints) 91 | } 92 | 93 | } 94 | 95 | public extension UILayoutGuide { 96 | 97 | func constraints(@NSLayoutConstraintsBuilder constraintsBuilder: (UILayoutGuide) -> NSLayoutConstraintsAccumulation) { 98 | NSLayoutConstraint.activate(constraintsBuilder(self).asMultipleConstraints) 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Martin Lalev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "FlooidLayout", 7 | platforms: [.iOS(.v13)], 8 | products: [ 9 | .library( 10 | name: "FlooidLayout", 11 | targets: ["FlooidLayout"]), 12 | ], 13 | dependencies: [ 14 | ], 15 | targets: [ 16 | .target( 17 | name: "FlooidLayout", 18 | path: "FlooidLayout/Source" 19 | ), 20 | ], 21 | swiftLanguageVersions: [.v6] 22 | ) 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlooidLayout 2 | 3 | Turn this autolayout code: 4 | 5 | ```swift 6 | NSLayoutConstraint.activate([ 7 | view.centerXAnchor.constraint(equalTo: container.centerXAnchor), 8 | view.topAnchor.constraint(equalTo: container.topAnchor), 9 | view.widthAnchor.constraint(equalTo: container.widthAnchor, constant: -2*20), 10 | view.heightAnchor.constraint(equalTo: container.heightAnchor, constant: -2*20) 11 | ]) 12 | ``` 13 | 14 | into this: 15 | 16 | ```swift 17 | image.constraints { view in 18 | view.centerTopAnchor == container.centerTopAnchor 19 | view.sizeAnchor == container.sizeAnchor -- 20 20 | } 21 | ``` 22 | ## Installation 23 | 24 | ### Cocoapods 25 | 26 | ``` 27 | pod 'FlooidLayout', '~> 1.0.8' 28 | ``` 29 | 30 | ### Swift Package Manager 31 | 32 | ``` 33 | .package(url: "https://github.com/martin-lalev/FlooidLayout", from: "1.0.8") 34 | ``` 35 | 36 | ## How it works 37 | 38 | FlooidLayout defines custom operators on `NSLayoutAnchor` (e.g. `==`, `<=`, `>=`, etc.), which produce `NSLayoutConstraint`. Examples: 39 | * simple constants for width and height anchors: `view.heightAnchor == 50` 40 | * anchor reference: `view.leadingAnchor == container.centerXAnchor` 41 | * anchor reference with multiplier: `view.widthAnchor <= container.widthAnchor * 0.8` 42 | * anchor reference with constant: `view.leadingAnchor == sibilingView.trailingAnchor + 15` 43 | * anchor reference with multiplier and constant: `view.heightAnchor == container.heightAnchor * 0.8 - 10` 44 | > Width and height anchors can also have their constants assigned with `++` or `--` which simply multiplies the value by 2. This is useful when you want to express paddings or margins on a view, e.g: `view.widthAnchor == container.widthAnchor -- 10` is equivalent to `view.widthAnchor == container.widthAnchor - 20`. 45 | 46 | You can also call `constraints` on a view, which will provide you with a result builder, expecting a list of `NSLayoutConstraint`, which are then activated. 47 | 48 | ## Modifications and variable assignment 49 | 50 | Sometimes you may need to change the priority of a constraint or assign an identifier. You can do this inline with the `--!` operator: 51 | 52 | #### Assigning custom priority 53 | 54 | ```swift 55 | view.heightAnchor == view.widthAnchor * 0.6 --! .priority(.defaultHigh) 56 | ``` 57 | 58 | #### Assigning an identifier 59 | 60 | ```swift 61 | view.centerXAnchor == container.centerXAnchor --! .identifier("iconCenterX") 62 | ``` 63 | 64 | #### Activation 65 | 66 | ```swift 67 | view.centerXAnchor == container.centerXAnchor --! .activated 68 | ``` 69 | > Should be used only if you are declaring a constraint outside of the `constraints` method. `constraints` will automatically activate all constraints listed in the result builder. 70 | 71 | #### Store in a vairable 72 | 73 | ```swift 74 | view.topAnchor == container.topAnchor + 20 --> imageTopConstraint 75 | ``` 76 | 77 | #### Chaining 78 | 79 | All of the modifiers can be chained together with the only rule being that the assignment is at the end: 80 | 81 | ```swift 82 | view.heightAnchor == view.widthAnchor * 0.6 --! .priority(.defaultHigh) --! .identifier("iconHeight") --> iconHeightConstraint 83 | ``` 84 | 85 | ## Additional anchors 86 | 87 | FlooidLayout provides a few additional "anchors", which will make your anchor declarations even simpler: 88 | 89 | #### `sizeAnchor` 90 | 91 | Modifies both the width and height anchors 92 | 93 | ```swift 94 | // Constant assignment 95 | view.sizeAnchor == CGSize(width: 30, height: 30) 96 | 97 | // Another sizeAnchor assignment 98 | view.sizeAnchor == container.sizeAnchor 99 | 100 | // Expanding both dimensions 101 | view.sizeAnchor == container.sizeAnchor + 20 102 | 103 | // Shrinking both dimensions (the constant is multipled by two in this example) 104 | view.sizeAnchor == container.sizeAnchor -- 20 105 | 106 | // Scaling both dimensions 107 | view.sizeAnchor == container.sizeAnchor * 0.5 108 | 109 | // Expanding each dimensions separately 110 | view.sizeAnchor == container.sizeAnchor + (width: 20, height: 40) 111 | 112 | // Shrinking each dimensions separately (the constants are multipled by two in this example) 113 | view.sizeAnchor == container.sizeAnchor -- (width: 20, height: 10) 114 | 115 | // Scaling each dimensions separately 116 | view.sizeAnchor == container.sizeAnchor * (width: 0.5, height: 0.4) 117 | ``` 118 | 119 | #### `centerAnchor` 120 | 121 | Modifies both the centerX centerY anchors. Similarly other location anchors are available, like `leadingTopAnchor`, `centerBottomAnchor`, `rightCenterAnchor`, etc. 122 | 123 | ```swift 124 | 125 | // Another locationAnchor assignment 126 | view.centerAnchor == container.centerAnchor 127 | 128 | // Moving both axis 129 | view.leadingTopAnchor == container.leadingTopAnchor + 20 130 | 131 | // Moving each axis separately 132 | view.leadingCenterAnchor == container.trailingCenterAnchor + (x: 10, y: 0) 133 | ``` 134 | 135 | #### `edgesAnchor` 136 | 137 | Modifies the top, bottom, leading and trailing anchors 138 | 139 | ```swift 140 | 141 | // Another locationAnchor assignment 142 | view.edgesAnchor == container.edgesAnchor 143 | 144 | // Insetting all edges equally 145 | view.edgesAnchor == container.edgesAnchor -- 10 146 | 147 | // Insetting edges separately per axis 148 | view.edgesAnchor == container.edgesAnchor -- (horizontally: 20, vertically: 10) 149 | 150 | // Insetting each edge separately 151 | view.edgesAnchor == container.edgesAnchor -- (leading: 5, trailing: 15, top: 10, bottom: 20) 152 | ``` 153 | 154 | #### `horizontalEdgesAnchor` 155 | 156 | Modifies both the leading and trailing anchors 157 | 158 | ```swift 159 | 160 | // Another horizontalEdgesAnchor assignment 161 | view.horizontalEdgesAnchor == container.horizontalEdgesAnchor 162 | 163 | // Insetting both edges equally 164 | view.horizontalEdgesAnchor == container.horizontalEdgesAnchor -- 10 165 | 166 | // Insetting each edge separately 167 | view.horizontalEdgesAnchor == container.horizontalEdgesAnchor -- (leading: 5, trailing: 15) 168 | ``` 169 | 170 | #### `verticalEdgesAnchor` 171 | 172 | Modifies both the top and bottom anchors 173 | 174 | ```swift 175 | 176 | // Another verticalEdgesAnchor assignment 177 | view.verticalEdgesAnchor == container.verticalEdgesAnchor 178 | 179 | // Insetting all edges equally 180 | view.verticalEdgesAnchor == container.verticalEdgesAnchor -- 10 181 | 182 | // Insetting each edge separately 183 | view.verticalEdgesAnchor == container.verticalEdgesAnchor -- (top: 10, bottom: 20) 184 | ``` 185 | --------------------------------------------------------------------------------