diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..60ddca0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,43 @@ +name: "Align CI" + +on: + push: + branches: + - master + pull_request: + branches: + - '*' + +jobs: + macos-run-tests: + name: Unit Tests (macOS, Xcode 13.3.1) + runs-on: macOS-12 + env: + DEVELOPER_DIR: /Applications/Xcode_13.3.1.app/Contents/Developer + steps: + - uses: actions/checkout@v2 + - name: Run Tests + run: swift test + + ios-run-tests: + name: Unit Tests (iOS 15.4, Xcode 13.3.1) + runs-on: macOS-12 + env: + DEVELOPER_DIR: /Applications/Xcode_13.3.1.app/Contents/Developer + steps: + - uses: actions/checkout@v2 + - name: Run Tests + run: xcodebuild test -scheme "Align" -destination "OS=15.4,name=iPhone 13 Pro" + + discover-typos: + name: Discover Typos + runs-on: macOS-12 + env: + DEVELOPER_DIR: /Applications/Xcode_13.4.1.app/Contents/Developer + steps: + - uses: actions/checkout@v2 + - name: Discover typos + run: | + python3 -m pip install --upgrade pip + python3 -m pip install codespell + codespell --ignore-words-list="inout" diff --git a/.gitignore b/.gitignore index d534044..6297722 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ ## Build generated build/ DerivedData/ +.swiftpm/ ## Various settings *.pbxuser diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 305d40e..0000000 --- a/.travis.yml +++ /dev/null @@ -1,39 +0,0 @@ -language: swift -os: osx - -git: - depth: 1 - -stages: - - name: test - -branches: - only: - - master - -jobs: - include: - - stage: test - name: Run Unit Tests (iOS, Xcode 11.3) - osx_image: xcode11.3 - script: Scripts/test.sh -d "OS=13.3,name=iPhone 11" -d "OS=12.4,name=iPhone X" -d "OS=11.4,name=iPhone SE" - - - stage: test - name: Run Unit Tests (macOS, Xcode 11.3) - osx_image: xcode11.3 - script: Scripts/test.sh -d "arch=x86_64" - - - stage: test - name: Run Unit Tests (tvOS, Xcode 11.3) - osx_image: xcode11.3 - script: Scripts/test.sh -d "OS=13.3,name=Apple TV 4K" -d "OS=11.4,name=Apple TV 4K" - - - stage: test - name: Run Unit Tests (iOS, Xcode 11) - osx_image: xcode11 - script: Scripts/test.sh -d "OS=13.0,name=iPhone 11" - - - stage: test - name: Swift Build (Swift Package Manager) - osx_image: xcode11.3 - script: swift build diff --git a/Align.playground/Pages/Alignments.xcplaygroundpage/Contents.swift b/Align.playground/Pages/Alignments.xcplaygroundpage/Contents.swift index bbd515c..cd6217a 100644 --- a/Align.playground/Pages/Alignments.xcplaygroundpage/Contents.swift +++ b/Align.playground/Pages/Alignments.xcplaygroundpage/Contents.swift @@ -26,7 +26,7 @@ class MyViewController : UIViewController { subview.anchors.edges.pin( insets: EdgeInsets(top: 10, left: 10, bottom: 10, right: 10), - alignment: Alignmment(horizontal: .leading, vertical: .center) + alignment: Alignment(horizontal: .leading, vertical: .center) ) view.addSubview(container) diff --git a/Align.playground/Pages/Demo.xcplaygroundpage/Contents.swift b/Align.playground/Pages/Demo.xcplaygroundpage/Contents.swift index 62b4915..5b9e6b8 100644 --- a/Align.playground/Pages/Demo.xcplaygroundpage/Contents.swift +++ b/Align.playground/Pages/Demo.xcplaygroundpage/Contents.swift @@ -4,43 +4,26 @@ import UIKit import PlaygroundSupport import Align -typealias Stack = UIStackView - -extension Stack { - @nonobjc convenience init(style: ((UIStackView) -> Void)..., views: [UIView]) { - self.init(arrangedSubviews: views) - style.forEach { $0(self) } - } -} - -extension UILabel { - @nonobjc convenience init(style: ((UILabel) -> Void)...) { - self.init() - style.forEach { $0(self) } - } -} - class MyViewController : UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = UIColor.white - let logo = UILabel(style: { $0.font = UIFont.systemFont(ofSize: 30) }) - let title = UILabel(style: { $0.font = UIFont.systemFont(ofSize: 20, weight: .bold) }) - let subtitle = UILabel(style: { - $0.font = UIFont.systemFont(ofSize: 15) - $0.numberOfLines = 0 - }) - - let stack = Stack( - style: { $0.spacing = 10; $0.alignment = .top }, - views: [ - logo, - Stack(style: { $0.axis = .vertical }, views: [title, subtitle]) - ] - ) + let logo = UILabel() + logo.font = UIFont.systemFont(ofSize: 30) + + let title = UILabel() + title.font = UIFont.systemFont(ofSize: 20, weight: .bold) + let subtitle = UILabel() + subtitle.font = UIFont.systemFont(ofSize: 15) + subtitle.numberOfLines = 0 + + let stack = UIStackView(spacing: 10, alignment: .top, [ + logo, + UIStackView(axis: .vertical, [title, subtitle]) + ]) view.addSubview(stack) /// Here's code written using Align @@ -54,4 +37,16 @@ class MyViewController : UIViewController { } } +extension UIStackView { + @nonobjc convenience init(spacing: CGFloat = 10, + axis: NSLayoutConstraint.Axis = .horizontal, + alignment: UIStackView.Alignment = .fill, + _ views: [UIView]) { + self.init(arrangedSubviews: views) + self.spacing = spacing + self.axis = axis + self.alignment = alignment + } +} + PlaygroundPage.current.liveView = MyViewController() diff --git a/Align.podspec b/Align.podspec deleted file mode 100644 index b25098b..0000000 --- a/Align.podspec +++ /dev/null @@ -1,19 +0,0 @@ -Pod::Spec.new do |s| - s.name = "Align" - s.version = "2.4.1" - s.summary = "An intuitive and powerful Auto Layout library" - - s.homepage = "https://github.com/kean/Align" - s.license = "MIT" - s.author = "Alexander Grebenyuk" - s.social_media_url = "https://twitter.com/a_grebenyuk" - s.source = { :git => "https://github.com/kean/Align.git", :tag => s.version.to_s } - - s.swift_versions = ['5.1', '5.2'] - - s.ios.deployment_target = '11.0' - s.osx.deployment_target = '10.13' - s.tvos.deployment_target = '11.0' - - s.source_files = "Sources/**/*" -end diff --git a/Align.xcodeproj/project.pbxproj b/Align.xcodeproj/project.pbxproj index 4c6610c..a1babf3 100644 --- a/Align.xcodeproj/project.pbxproj +++ b/Align.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 0C8FD79D1FB88D2100A20E3D /* AnchorCollectionSizeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8FD79C1FB88D2100A20E3D /* AnchorCollectionSizeTests.swift */; }; 0CBC06B8249E9F790055D1B1 /* AnchorPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CBC06B7249E9F790055D1B1 /* AnchorPerformanceTests.swift */; }; 0CC3A06C1D7476A700754E59 /* Align.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0CE361181C86077B00EA70CF /* Align.framework */; }; + 0CCC2073287E3D26001FC3CE /* Align.docc in Sources */ = {isa = PBXBuildFile; fileRef = 0CCC2072287E3D26001FC3CE /* Align.docc */; }; 0CF93195249834DB00E1016A /* Align+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CF93193249834DB00E1016A /* Align+Extensions.swift */; }; 0CF931972499039500E1016A /* AnchorAPIsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CF931962499039500E1016A /* AnchorAPIsTests.swift */; }; /* End PBXBuildFile section */ @@ -36,10 +37,9 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 0C2EE5C3225A700E00743F47 /* Align.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Align.xcconfig; path = Supporting/Align.xcconfig; sourceTree = ""; }; 0C62A9A81FD28B5900C6EA6E /* Align.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Align.playground; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 0C683A0F1FBA4624002ACEB9 /* AnchorCollectionEdgesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnchorCollectionEdgesTests.swift; sourceTree = ""; }; - 0C72B1CB249AB71D006DCEDA /* MigrationGuide2.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = MigrationGuide2.md; path = Docs/MigrationGuide2.md; sourceTree = ""; }; + 0C6C05D2287F102F009CFDE6 /* ci.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; name = ci.yml; path = .github/workflows/ci.yml; sourceTree = ""; }; 0C81ABB61C889C100036DFD4 /* Align.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Align.swift; sourceTree = ""; }; 0C8FD7781FB85E8B00A20E3D /* XCTestExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTestExtensions.swift; sourceTree = ""; }; 0C8FD77E1FB85EA500A20E3D /* Diff.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Diff.swift; sourceTree = ""; }; @@ -53,16 +53,12 @@ 0C8FD79C1FB88D2100A20E3D /* AnchorCollectionSizeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnchorCollectionSizeTests.swift; sourceTree = ""; }; 0CBC06B7249E9F790055D1B1 /* AnchorPerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnchorPerformanceTests.swift; sourceTree = ""; }; 0CC3A0671D7476A700754E59 /* Align Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Align Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 0CC3A0751D7476B300754E59 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 0CD220BC22BBECE70095AD2A /* Align.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Align.podspec; sourceTree = ""; }; + 0CCC2072287E3D26001FC3CE /* Align.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = Align.docc; sourceTree = ""; }; 0CD220BF22BBED040095AD2A /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 0CD220C022BBED0A0095AD2A /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; - 0CD220C222BBED1C0095AD2A /* InstallationGuide.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = InstallationGuide.md; path = Docs/InstallationGuide.md; sourceTree = ""; }; 0CD220C322BBED250095AD2A /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 0CD220C422BBED2C0095AD2A /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; - 0CD220C522BBED340095AD2A /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = ""; }; 0CE361181C86077B00EA70CF /* Align.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Align.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 0CE361241C8607BB00EA70CF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Supporting/Info.plist; sourceTree = ""; }; 0CF93193249834DB00E1016A /* Align+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Align+Extensions.swift"; sourceTree = ""; }; 0CF931962499039500E1016A /* AnchorAPIsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnchorAPIsTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -99,7 +95,6 @@ 0CC3A0721D7476B300754E59 /* Tests */ = { isa = PBXGroup; children = ( - 0CC3A0751D7476B300754E59 /* Info.plist */, 0C8FD7811FB85F5600A20E3D /* AnchorTests.swift */, 0CF931962499039500E1016A /* AnchorAPIsTests.swift */, 0C8FD78D1FB88B9B00A20E3D /* AnchorAlignmentTests.swift */, @@ -119,14 +114,11 @@ 0CD220BB22BBECD70095AD2A /* Metadata */ = { isa = PBXGroup; children = ( - 0CD220BC22BBECE70095AD2A /* Align.podspec */, 0CD220BF22BBED040095AD2A /* README.md */, 0CD220C022BBED0A0095AD2A /* Package.swift */, - 0C72B1CB249AB71D006DCEDA /* MigrationGuide2.md */, - 0CD220C222BBED1C0095AD2A /* InstallationGuide.md */, 0CD220C322BBED250095AD2A /* LICENSE */, 0CD220C422BBED2C0095AD2A /* CHANGELOG.md */, - 0CD220C522BBED340095AD2A /* .travis.yml */, + 0C6C05D2287F102F009CFDE6 /* ci.yml */, ); name = Metadata; sourceTree = ""; @@ -137,7 +129,6 @@ 0C62A9A81FD28B5900C6EA6E /* Align.playground */, 0CE361261C8607D600EA70CF /* Sources */, 0CC3A0721D7476B300754E59 /* Tests */, - 0CE361231C8607A900EA70CF /* Supporting */, 0CD220BB22BBECD70095AD2A /* Metadata */, 0CE361191C86077B00EA70CF /* Products */, ); @@ -152,19 +143,11 @@ name = Products; sourceTree = ""; }; - 0CE361231C8607A900EA70CF /* Supporting */ = { - isa = PBXGroup; - children = ( - 0CE361241C8607BB00EA70CF /* Info.plist */, - 0C2EE5C3225A700E00743F47 /* Align.xcconfig */, - ); - name = Supporting; - sourceTree = ""; - }; 0CE361261C8607D600EA70CF /* Sources */ = { isa = PBXGroup; children = ( 0C81ABB61C889C100036DFD4 /* Align.swift */, + 0CCC2072287E3D26001FC3CE /* Align.docc */, ); path = Sources; sourceTree = ""; @@ -225,7 +208,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0800; - LastUpgradeCheck = 1230; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = "Alexander Grebenyuk"; TargetAttributes = { 0CC3A0661D7476A700754E59 = { @@ -302,6 +285,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0CCC2073287E3D26001FC3CE /* Align.docc in Sources */, 0C81ABB71C889C100036DFD4 /* Align.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -320,11 +304,7 @@ 0CC3A0701D7476A700754E59 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ENABLE_MODULES = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_SUSPICIOUS_MOVES = YES; - INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.github.kean.Align-iOS-Tests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -335,11 +315,7 @@ 0CC3A0711D7476A700754E59 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ENABLE_MODULES = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_SUSPICIOUS_MOVES = YES; - INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.github.kean.Align-iOS-Tests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -348,56 +324,24 @@ }; 0CE3611E1C86077B00EA70CF /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0C2EE5C3225A700E00743F47 /* Align.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MTL_ENABLE_DEBUG_INFO = YES; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; + SUPPORTED_PLATFORMS = "iphoneos appletvos iphonesimulator appletvsimulator macosx"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TVOS_DEPLOYMENT_TARGET = 12.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -405,49 +349,18 @@ }; 0CE3611F1C86077B00EA70CF /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0C2EE5C3225A700E00743F47 /* Align.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MTL_ENABLE_DEBUG_INFO = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MACOSX_DEPLOYMENT_TARGET = 10.13; + SUPPORTED_PLATFORMS = "iphoneos appletvos iphonesimulator appletvsimulator macosx"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TVOS_DEPLOYMENT_TARGET = 12.0; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -458,20 +371,16 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; - CLANG_ENABLE_MODULES = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - DEFINES_MODULE = YES; + DOCC_HOSTING_BASE_PATH = /align/; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "$(SRCROOT)/Supporting/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MARKETING_VERSION = 2.4.1; PRODUCT_BUNDLE_IDENTIFIER = com.github.kean.Align; PRODUCT_NAME = Align; SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; @@ -479,13 +388,10 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; - CLANG_ENABLE_MODULES = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - DEFINES_MODULE = YES; + DOCC_HOSTING_BASE_PATH = /align/; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "$(SRCROOT)/Supporting/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MARKETING_VERSION = 2.4.1; diff --git a/Align.xcodeproj/xcshareddata/xcschemes/Align.xcscheme b/Align.xcodeproj/xcshareddata/xcschemes/Align.xcscheme index 032463c..d979f33 100644 --- a/Align.xcodeproj/xcshareddata/xcschemes/Align.xcscheme +++ b/Align.xcodeproj/xcshareddata/xcschemes/Align.xcscheme @@ -1,6 +1,6 @@ Use [Migraiton Guide](https://github.com/kean/Align/blob/master/Docs/MigrationGuide2.md) included in the repo to ease migration. -- Remove all deprecated APIs. If you are migrating from the previous version, consider migrating to version 2.0.0 first. It is going to guide you throught the migration. +- Remove all deprecated APIs. If you are migrating from the previous version, consider migrating to version 2.0.0 first. It is going to guide you thought the migration. ## Align 2.0.0 @@ -65,7 +79,7 @@ - Add `constraints` property to `Constraints` type to allow access to all of the constraints created using it - Add `activate` parameter to `Constraints` initiliazer to optionally disable automatic activation of constraints - Deprecated `func edges(_ edges: LayoutEdge...)`, use `pin(axis:)` insteads -- `pin()` methods now use `.leading` and `.trailing` anchors intead of absolute `.left` and `.right` anchors. To switch to absolute anchors, use `absolute()`: `view.anchors.edges.absolute()` +- `pin()` methods now use `.leading` and `.trailing` anchors instead of absolute `.left` and `.right` anchors. To switch to absolute anchors, use `absolute()`: `view.anchors.edges.absolute()` - Remove `addSubview` family of APIs - Migrate to Swift 5.1 - Increase minimum required platform versions diff --git a/Docs/InstallationGuide.md b/Docs/InstallationGuide.md deleted file mode 100644 index b18d427..0000000 --- a/Docs/InstallationGuide.md +++ /dev/null @@ -1,84 +0,0 @@ -# Installation Guide - -- [Installation](#installation)] - * [Swift Package Manager](#swift-package-manager) - * [Manually](#manually) - * [CocoaPods](#cocoapods) - * [Carthage](#carthage) -- [License](#license) - -## Requirements - -| Align | Swift | Xcode | Platforms | -|------------------|-----------------------|----------------------|------------------------| -| Align 2.0 | Swift 5.1 | Xcode 11.0 | iOS 11.0 / tvOS 11.0 / macOS 10.13 | -| Align 1.1-1.2 | Swift 4.2 – 5.0 | Xcode 10.1 – 10.2 | iOS 10.0 / tvOS 10.0 | -| Align 1.0 | Swift 4.0 – 4.2 | Xcode 9.2 – 10.1 | iOS 9.0 / tvOS 9.0 | - -## Installation - -### Swift Package Manager - -[Swift Package Manager](https://swift.org/package-manager/) is a dependency manager built into Xcode. - -If you are using Xcode 11 or higher, go to **File / Swift Packages / Add Package Dependency...** and enter package repository URL **https://github.com/kean/Align.git**, then follow the instructions. - -To remove the dependency, select the project and open **Swift Packages** (which is next to **Build Settings**). You can add and remove packages from this tab. - -> Swift Package Manager can also be used [from the command line](https://swift.org/package-manager/). - -### Manually - -The entire library fits in a single file with under 250 lines of code which you can just drag-n-drop into your app. This way you won't have to manually `import` it in your source files. - - -### CocoaPods - -[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command: - -```bash -$ gem install cocoapods -``` - -> CocoaPods 1.1+ is required to build Align - -To integrate Align into your Xcode project using CocoaPods, specify it in your `Podfile`: - -```ruby -source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '11.0' -use_frameworks! - -target '' do - pod 'Align' -end -``` - -Then, run the following command: - -```bash -$ pod install -``` - -### Carthage - -[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. - -You can install Carthage with [Homebrew](http://brew.sh/) using the following command: - -```bash -$ brew update -$ brew install carthage -``` - -To integrate Align into your Xcode project using Carthage, specify it in your `Cartfile`: - -```ogdl -github "kean/Align" -``` - -Run `carthage update` to build the framework and drag the built `Align.framework` into your Xcode project. - -## License - -Align is available under the MIT license. See the LICENSE file for more info. diff --git a/Docs/MigrationGuide2.md b/Docs/MigrationGuide2.md deleted file mode 100644 index a88a863..0000000 --- a/Docs/MigrationGuide2.md +++ /dev/null @@ -1,110 +0,0 @@ -# Align 2 Migration Guide - -This guide is provided in order to ease the transition of existing applications using Align 1.x to the latest version, as well as explain the design and structure of new and changed functionality. - -**Note** If you are migrating from Align 1, upgrade to Align 2.0.0 first. This version contains deprecated APIs that were removed in Align 2.1.0. - -## `.al` extensions deprecated, replaced by `.anchor` - -```swift -// Align 1 -view.al.top.align(with: superview.al.top) - -// Align 2 -view.anchors.top.align(with: superview.anchors.top) -``` - -## Reworked `pin()` methods - -There are major changes in `pin()` family of methods. - -- All of the previous methods were deprecated and replaced with only two new methods: - -```swift -// Align 2 -view.pin( - to item2: LayoutItem? = nil, - axis: NSLayoutConstraint.Axis? = nil, - insets: EdgeInsets = .zero, - alignment: Alignmment = .fill -) - -view.pin( - to item2: LayoutItem? = nil, - axis: NSLayoutConstraint.Axis? = nil, - insets: CGFloat = .zero, - alignment: Alignmment = .fill -) -``` - -- The `relation: NSLayoutRelation` option which was initially copied from PureLayout, but never really made sense, was replaced with a much more clear and powerful `alignment: Alignment` option - -You can find more information about the new `pin()` method in the README, with clear illustrations, and more. But here are some example of how you code might change. - -```swift -// Align 1 -view.anchors.edges.pinToSuperview() - -// Align 2 -view.anchors.edges.pin() -``` - -```swift -// Align 1 -view.anchors.edges.pinToSuperview(insets: UIEdgeInsets(top: 10, bottom: 10, right: 10, top: 10)) - -// Align 2 -view.anchors.edges.pin(insets: 10) -``` - -```swift -// Align 1 -view.anchors.edges.pinToSuperview(relation: .greaterThanOrEqual) -view.anchors.center.alignWithSuperview() - -// Align 2 -view.anchors.edges.pin(alignment: .center) -``` - -```swift -// Align 1 -view.anchors.edges(.top, .bottom).pinToSuperview() - -// Align 2 -view.anchors.edges.pin(axis: vertical) -``` - -```swift -// Align 1 -view.anchors.edges.pinToSuperviewMargins() - -// Align 2 -view.anchors.edges.pin(to: container.layoutMarginsGuide) -``` - -```swift -// Align 1 -view.anchors.edges.pinToSafeArea(of: self) - -// Align 2 -view.anchors.edges.pin(to: view.safeAreaLayoutGuide) -``` - -## `leading` instead of `left`, etc - -`pin()` methods now use `.leading` and `.trailing` anchors intead of absolute `.left` and `.right` anchors. To switch to absolute anchors, use `absolute()`: - -```swift -// Align 2 -view.anchors.edges.absolute() -``` - -## `func edges(_ edges: LayoutEdge...)` deprecated - -```swift -// Align 1 -view.anchors.edges(.top, .bottom).pinToSuperview() - -// Align 2 -view.anchors.edges.pin(axis: vertical) -``` diff --git a/Package.swift b/Package.swift index d81092b..afb0d69 100755 --- a/Package.swift +++ b/Package.swift @@ -1,17 +1,18 @@ -// swift-tools-version:5.1 +// swift-tools-version:5.6 import PackageDescription let package = Package( name: "Align", platforms: [ - .iOS(.v11), - .tvOS(.v11), - .macOS(.v10_13), + .iOS(.v12), + .tvOS(.v12), + .macOS(.v10_14), ], products: [ - .library(name: "Align", targets: ["Align"]), + .library(name: "Align", type: .dynamic, targets: ["Align"]), ], targets: [ - .target(name: "Align", path: "Sources") + .target(name: "Align", path: "Sources"), + .testTarget(name: "AlignTests", dependencies: ["Align"], path: "Tests") ] ) diff --git a/README.md b/README.md index 1f9373a..60d3317 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,10 @@ Screen Shot 2020-06-15 at 23 06 32

- - - - +

-Align introduces a better alternative to Auto Layout [anchors](https://developer.apple.com/documentation/uikit/nslayoutanchor). +Align introduces a better alternative to Auto Layout anchors. - **Semantic**. Align APIs focus on your goals, not the math behind Auto Layout constraints. - **Powerful**. Create multiple constraints with a single line of code. @@ -21,219 +18,22 @@ To give you a taste of what the *semantic* APIs look like, here is an example: view.anchors.edges.pin(insets: 20, alignment: .center) ``` -pin edges with center alignmnet +pin edges with center alignment -> You can choose to add convenience extensions that you prefer to make it even more concise for select use cases, e.g. `view.pin(insets: 20)`. +## Documentation -## Getting Started +The [**documentation**](https://kean-docs.github.io/align/documentation/align/) for Align is created using DocC and covers all of its APIs in a clear visual way. There is also a [**cheat sheet**](https://github.com/kean/Align/blob/master/Sources/Align.docc/Resources/align-cheat-sheet.pdf) available that lists all of the available APIs. -The entire library fits in a single file with around 300 lines of code. You can simply drag-n-drop it into your app if you'd like. For more installation options, see [**Installation Guide**](https://github.com/kean/Align/blob/master/Docs/InstallationGuide.md). - -- **Anchors** ‣ [Introduction](#introduction) · [Core API](#core-api) · [Semantic API](#semantic-api) -- **Anchor Collections** ‣ [Edges](#edges) · [Center](#center) · [Size](#size) -- [**Advanced**](#advanced) · [**Requirements**](#requirements) · [**Why Align**](#why-align) - -## Introduction - -Anchors (`Anchor - -**Core API** allows you to create constraints by setting relations between one or more anchors, there APIs are similar to what `NSLayoutAnchor` provides. **Semantic API** is a high-level API that focus on your goals, such as pinning edges to the container, aligning the view, setting spacing between views, etc. - -Both of these types of APIs are designed to be easily discoverable using Xcode code completions. There is also a [**cheat sheet**](https://github.com/kean/Align/blob/master/Docs/align-cheat-sheet.pdf) available, as well as an illustrated guide located in this README. - - -Align Cheat Sheet + +Screen Shot 2022-07-13 at 10 08 57 AM -## Anchors - -### Core API - -```swift -// Align two views along one of the edges -a.anchors.leading.equal(b.anchors.leading) - -// Other options are available: -// a.anchors.leading.greaterThanOrEqual(b.anchors.leading) -// a.anchors.leading.greaterThanOrEqual(b.anchors.leading, constant: 10) - -// Set height to the given value (CGFloat) -a.anchors.height.equal(30) -``` - -pin edges - -Align automatically sets `translatesAutoresizingMaskIntoConstraints` to `false` for every view that you manipulate using it, so you no longer have to worry about it. - -Align also automatically activates the created constraints. Using [`(NSLayoutConstraint.active(_:)`](https://developer.apple.com/documentation/uikit/nslayoutconstraint/1526955-activate) is typically slightly more efficient than activating each constraint individually. To take advantage of this method, wrap your code into `Constrains` initializer. - -```swift -Constraints { - // Create constraints in this closure -} -``` - -> Align has full test coverage. If you'd like to learn about which constraints (`NSLayoutConstraint`) Align creates each time you call one of its methods, test cases are a great place to start. - -Align also allows you to offset and multiply anchors. This is a lightweight operation with no allocations involved. - -```swift -// Offset one of the anchors, creating a "virtual" anchor -b.anchors.leading.equal(a.anchors.trailing + 20) - -// Set aspect ratio for a view -b.anchors.height.equal(a.anchors.width * 2) -``` - -pin edges - -### Semantic API - -```swift -// Set spacing between two views -a.anchors.bottom.spacing(20, to: b.anchors.top) - -// Pin an edge to the superview -a.anchors.trailing.pin(inset: 20) -``` - -pin edges - -```swift -Clamps the dimension of a view to the given limiting range. -a.anchors.width.clamp(to: 40...100) -``` - -pin edges - - -## Anchor Collections - -With Align, you can manipulate multiple edges at the same time, creating more than one constraint at a time. - -### Edges - -`pin()` is probably the most powerful and flexible API in Align. - -```swift -view.anchors.edges.pin(insets: 20) - -// Same as the following: -view.anchors.edges.pin( - to: view.superview! - insets: EdgeInsets(top: 20, left: 20, bottom: 20, trailing: 20), - alignment: .fill -) -``` - -pin edges - -By default, `pin()` method pin the edges to the superview of the current view. However, you can select any target view or layout guide: - -```swift -// Pin to superview -view.anchors.edges.pin() - -// Pin to layout margins guide -view.anchors.edges.pin(to: container.layoutMarginsGuide) - -// Pin to safe area -view.anchors.edges.pin(to: container.safeAreaLayoutGuide) -``` - -> Align also provides a convenient way to access anchors of the layout guide: `view.anchors.safeArea.top`. - -By default, `pin()` users `.fill` alignment. There are variety of other alignments available (81 if you combine all possible options). - -```swift -view.anchors.edges.pin(insets: 20, alignment: .center) -``` - -pin edges with center alignmnet - -You can create constraint along the given axis. - -```swift -view.anchors.edges.pin(insets: 20, axis: .horizontal, alignment: .center) -``` - -pin edges with center alignmnet for horizontal axis - - -Or pin the view to to a corner. - -```swift -view.anchors.edges.pin(insets: 20, alignment: .topLeading) -``` - -pin edges with center alignmnet for horizontal axis - -You can create custom alignments (see `Alignment` type) by providing a vertical and horizontal component. - -```swift -anchors.edges.pin(insets: 20, alignment: Alignment(vertical: .center, horizontal: .leading)) -``` - -pin edges with center alignmnet for horizontal axis - -### Center - -```swift -a.anchors.center.align() -``` - -size equal - -### Size - -```swift -a.anchors.size.equal(CGSize(width: 120, height: 40)) -``` - -> `greaterThanEqual` and `lessThanOrEqual` options are also available - -size equal - -```swift -a.anchors.size.equal(b) -``` - -size equal other view - -## Advanced - -By default, Align automatically activates created constraints. Using `Constraints` API, constraints are activated all of the same time when you exit from the closure. It gives you a chance to change the `priority` of the constraints. - -```swift -Constraints(for: title, subtitle) { title, subtitle in - // Align one anchor with another - subtitle.top.spacing(10, to: title.bottom + 10) - - // Manipulate dimensions - title.width.equal(100) - - // Change a priority of constraints inside a group: - subtitle.bottom.pin().priority = UILayoutPriority(999) -} -``` - -`Constraints` also give you easy access to Align anchors (notice, there is no `.anchors` call in the example). And if you want to not activate the constraints, there is an option for that: - -```swift -Constraints(activate: false) { - // Create your constraints here -} -``` - ## Requirements -| Align | Swift | Xcode | Platforms | -|------------------|-----------------------|----------------------|------------------------| -| Align 2.0 | Swift 5.1 | Xcode 11.0 | iOS 11.0 / tvOS 11.0 / macOS 10.13 | -| Align 1.1-1.2 | Swift 4.2 – 5.0 | Xcode 10.1 – 10.2 | iOS 10.0 / tvOS 10.0 | -| Align 1.0 | Swift 4.0 – 4.2 | Xcode 9.2 – 10.1 | iOS 9.0 / tvOS 9.0 | +| Align | Swift | Xcode | Platforms | +|----------------|-------------|-------------------|------------------------------------| +| Align 3.0 | Swift 5.6 | Xcode 13.3 | iOS 12.0 / tvOS 12.0 / macOS 10.14 | +| Align 2.0 | Swift 5.1 | Xcode 11.0 | iOS 11.0 / tvOS 11.0 / macOS 10.13 | ## Why Align @@ -241,8 +41,8 @@ Align strives for clarity and simplicity by following [Swift API Design Guidelin Align is for someone who: -- Prefers fluent API that follows [Swift API Design Guidelines](https://swift.org/documentation/api-design-guidelines/) -- [Doesn't want](http://chris.eidhof.nl/post/micro-autolayout-dsl/) to depend on big, complex libraries -- Prefers to have as little extensions for native classes as possible, `Align` only adds one property +- Prefers fluent high-level APIs +- Doesn't want to depend on big, complex libraries – Align has only ~330 lines of code +- Prefers to have as little extensions for native classes as possible – Align adds a single property: `anchors` - Doesn't overuse operator overloads, prefers [fast compile times](https://github.com/robb/Cartography/issues/215) -- Likes [NSLayoutAnchor](https://developer.apple.com/library/ios/documentation/AppKit/Reference/NSLayoutAnchor_ClassReference/index.html) but wishes it had simpler, more fluent API which didn't require manually activating constraints +- Likes [NSLayoutAnchor](https://developer.apple.com/library/ios/documentation/AppKit/Reference/NSLayoutAnchor_ClassReference/index.html) but wishes it had simpler API which didn't require manually activating constraints diff --git a/Scripts/build.sh b/Scripts/build.sh deleted file mode 100755 index 7379928..0000000 --- a/Scripts/build.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -scheme="Nuke" - -while getopts "d:" opt; do - case $opt in - d) destinations+=("$OPTARG");; - #... - esac -done -shift $((OPTIND -1)) - -echo "destinations = ${destinations[@]}" - -set -o pipefail -xcodebuild -version - -for dest in "${destinations[@]}"; do - echo "Building for destination: $dest" - xcodebuild build -scheme $scheme -destination "$dest" | xcpretty; - if [ $? -ne 0 ]; then - exit $? - fi -done - -exit $? diff --git a/Scripts/test.sh b/Scripts/test.sh deleted file mode 100755 index d63eaa3..0000000 --- a/Scripts/test.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh - -scheme="Nuke" - -while getopts "s:d:" opt; do - case $opt in - s) scheme=${OPTARG};; - d) destinations+=("$OPTARG");; - #... - esac -done -shift $((OPTIND -1)) - -echo "scheme = ${scheme}" -echo "destinations = ${destinations[@]}" - - -set -o pipefail -xcodebuild -version - - -xcodebuild build-for-testing -scheme "$scheme" -destination "${destinations[0]}" | xcpretty; -if [ $? -ne 0 ]; then - exit $? -fi - -for destination in "${destinations[@]}"; do - echo "\nRunning tests for destination: $destination" - - # passing multiple destinations to `test` command results in Travis hanging - xcodebuild test-without-building -scheme "$scheme" -destination "$destination" | xcpretty; - - if [ $? -ne 0 ]; then - exit $? - fi -done - -exit $? diff --git a/Scripts/validate.sh b/Scripts/validate.sh deleted file mode 100755 index 203fee7..0000000 --- a/Scripts/validate.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -./temp/swiftlint lint --strict diff --git a/Sources/Align.docc/Align.md b/Sources/Align.docc/Align.md new file mode 100644 index 0000000..3ae6bfa --- /dev/null +++ b/Sources/Align.docc/Align.md @@ -0,0 +1,68 @@ +# ``Align`` + +Align introduces a better alternative to Auto Layout anchors. + +![Logo](logo.png) + +- **Semantic**. Align APIs focus on your goals, not the math behind Auto Layout constraints. +- **Powerful**. Create multiple constraints with a single line of code. +- **Type Safe**. Makes it impossible to create invalid constraints, at compile time. +- **Fluent**. Concise and clear API that follows [Swift API Design Guidelines](https://swift.org/documentation/api-design-guidelines/). +- **Simple**. Stop worrying about `translatesAutoresizingMaskIntoConstraints` and constraints activation. + +To give you a taste of what the *semantic* APIs look like, here is an example: + +```swift +view.anchors.edges.pin(insets: 20, alignment: .center) +``` + +![01](01.png) + +## Getting Started + +The entire library fits in a single file with around 300 lines of code. The best way to install it is by using Swift Package Manager, buy you can also simply drag-n-drop it into your app. If you install it as a package, you can avoid having to import it in every file by re-exposing its APIs in your app target: + +```swift +import Align + +extension LayoutItem { + var anchors: Align.LayoutAnchors { Align.LayoutAnchors(self) } +} +``` + +## Overview + +The Align APIs for creating constraints in the following quadrant: + +![02](02.png) + +**Core API** allows you to create constraints by setting relations between one or more anchors, there APIs are similar to what `NSLayoutAnchor` provides. **Semantic API** is a high-level API that focus on your goals, such as pinning edges to the container, aligning the view, setting spacing between views, etc. + +Both of these types of APIs are designed to be easily discoverable using Xcode code completions. There is also a [**cheat sheet**](https://github.com/kean/Align/blob/master/Docs/align-cheat-sheet.pdf) available listing all APIs in one place. + +## Requirements + +| Align | Swift | Xcode | Platforms | +|----------------|-------------|-------------------|------------------------------------| +| Align 3.0 | Swift 5.6 | Xcode 13.3 | iOS 12.0 / tvOS 12.0 / macOS 10.14 | +| Align 2.0 | Swift 5.1 | Xcode 11.0 | iOS 11.0 / tvOS 11.0 / macOS 10.13 | +| Align 1.1 | Swift 4.2 | Xcode 10.1 | iOS 10.0 / tvOS 10.0 | +| Align 1.0 | Swift 4.0 | Xcode 9.2 | iOS 9.0 / tvOS 9.0 | + +## Topics + +### Essentials + +- ``LayoutItem`` +- ``LayoutAnchors`` +- ``Anchor`` + +### Manipulating Multiple Anchors + +- ``AnchorCollectionEdges`` +- ``AnchorCollectionCenter`` +- ``AnchorCollectionSize`` + +### Managing Constraints + +- ``Constraints`` diff --git a/Sources/Align.docc/Alignment+Extension.md b/Sources/Align.docc/Alignment+Extension.md new file mode 100644 index 0000000..26ce26f --- /dev/null +++ b/Sources/Align.docc/Alignment+Extension.md @@ -0,0 +1,20 @@ +# ``Align/Alignment`` + +## Topics + +### Initializers + +- ``init(horizontal:vertical:)`` + +### Predefined Alignments + +- ``fill`` +- ``center`` +- ``topLeading`` +- ``top`` +- ``topTrailing`` +- ``trailing`` +- ``bottomTrailing`` +- ``bottom`` +- ``bottomLeading`` +- ``leading`` diff --git a/Sources/Align.docc/Anchor+Extensions.md b/Sources/Align.docc/Anchor+Extensions.md new file mode 100644 index 0000000..2687677 --- /dev/null +++ b/Sources/Align.docc/Anchor+Extensions.md @@ -0,0 +1,94 @@ +# ``Align/Anchor`` + +### Core Constraints + +```swift +// Align two views along one of the edges +a.anchors.leading.equal(b.anchors.leading) + +// Other options are available: +// a.anchors.leading.greaterThanOrEqual(b.anchors.leading) +// a.anchors.leading.greaterThanOrEqual(b.anchors.leading, constant: 10) + +// Set height to the given value (CGFloat) +a.anchors.height.equal(30) +``` + +![03](03.png) + +> tip: Align automatically sets `translatesAutoresizingMaskIntoConstraints` to `false` for every view that you manipulate using it, so you no longer have to worry about it. + +Align also allows you to offset and multiply anchors. This is a lightweight operation with no allocations involved. + +```swift +// Offset one of the anchors, creating a "virtual" anchor +b.anchors.leading.equal(a.anchors.trailing + 20) + +// Set aspect ratio for a view +b.anchors.height.equal(a.anchors.width * 2) +``` + +![04](04.png) + +### Semantic Constraints + +```swift +// Set spacing between two views +a.anchors.bottom.spacing(20, to: b.anchors.top) + +// Pin an edge to the superview +a.anchors.trailing.pin(inset: 20) +``` + + +![05](05.png) + +```swift +// Clamps the dimension of a view to the given limiting range. +a.anchors.width.clamp(to: 40...100) +``` + +![06](06.png) + +## Topics + +### Core Constraints for Edges and Center + +These constraints can be added between anchors that represent view edges and center, but only if they operate in the same axis. + +- ``equal(_:constant:)-9a08m`` +- ``greaterThanOrEqual(_:constant:)-3ths2`` +- ``lessThanOrEqual(_:constant:)-9nco3`` + +### Core Constraints for Dimensions + +- ``equal(_:)`` +- ``greaterThanOrEqual(_:)`` +- ``lessThanOrEqual(_:)`` + +- ``equal(_:constant:)-88kx7`` +- ``greaterThanOrEqual(_:constant:)-6c6e9`` +- ``lessThanOrEqual(_:constant:)-9dk3k`` + +### Core Constraints + +- ``offsetting(by:)`` +- ``multiplied(by:)`` + +### Semantic Constraints for Edges + +- ``pin(to:inset:)`` +- ``spacing(_:to:relation:)`` + +### Semantic Constraints for Dimensions + +- ``clamp(to:)`` + +### Semantic Constraints for Center + +- ``align(offset:)`` + +### Anchor Parameters + +- ``AnchorAxis`` +- ``AnchorType`` diff --git a/Sources/Align.docc/AnchorCollectionCenter+Extension.md b/Sources/Align.docc/AnchorCollectionCenter+Extension.md new file mode 100644 index 0000000..e87adea --- /dev/null +++ b/Sources/Align.docc/AnchorCollectionCenter+Extension.md @@ -0,0 +1,20 @@ +# ``Align/AnchorCollectionCenter`` + +```swift +a.anchors.center.align() +``` + +![Center](center-01.png) + +## Topics + +### Core Constraints + +- ``equal(_:offset:)`` +- ``lessThanOrEqual(_:offset:)`` +- ``greaterThanOrEqual(_:offset:)`` + +### Semantic Constraints + +- ``align()`` +- ``align(with:)`` diff --git a/Sources/Align.docc/AnchorCollectionEdges+Extension.md b/Sources/Align.docc/AnchorCollectionEdges+Extension.md new file mode 100644 index 0000000..7dbb11b --- /dev/null +++ b/Sources/Align.docc/AnchorCollectionEdges+Extension.md @@ -0,0 +1,95 @@ +# ``Align/AnchorCollectionEdges`` + +### Pin Edges + +The main API available for edges is ``pin(to:insets:axis:alignment:)-2uvy8`` that pins the edges to the container. + +```swift +// A convenience method: +view.anchors.edges.pin(insets: 20) + +// Same output as the following: +view.anchors.edges.pin( + to: view.superview! + insets: EdgeInsets(top: 20, left: 20, bottom: 20, trailing: 20), + alignment: .fill +) +``` + +![Edges](edges-01.png) + +By default, it pins the edges to the superview of the current view. However, you can select any view as a container or even a layout guide. + +```swift +// Pin to superview +view.anchors.edges.pin() + +// Pin to layout margins guide +view.anchors.edges.pin(to: container.layoutMarginsGuide) + +// Pin to safe area +view.anchors.edges.pin(to: container.safeAreaLayoutGuide) +``` + +### Pin with Alignment + +By default, ``pin(to:insets:axis:alignment:)-2uvy8`` uses alignment ``Alignment/fill``, but there are many other alignments available. For example, with ``Alignment/center``, the view is centered in the container and the edges are pinned with "less than or equal" constraints making sure it doesn't overflow the container. + +```swift +view.anchors.edges.pin(insets: 20, alignment: .center) +``` + +![Edges](edges-02.png) + +Another useful alignment is ``Alignment/topLeading`` that pins the view to the corner. + +```swift +view.anchors.edges.pin(insets: 20, alignment: .topLeading) +``` + +![Edges](edges-04.png) + +In addition to the 9 predefined such as ``Alignment/fill``, ``Alignment/topLeading``, you can also create custom alignments by providing a vertical and horizontal component separately. + +```swift +anchors.edges.pin( + insets: 20, + alignment: Alignment(vertical: .center, horizontal: .leading) +) +``` + +![Edges](edges-05.png) + +### Constraints Along the Axis + +Sometimes, you just need to create constraints along the given axis and you can do that using the `axis` parameter. + +```swift +view.anchors.edges.pin(insets: 20, axis: .horizontal, alignment: .center) +``` + +![Edges](edges-03.png) + + +## Topics + +### Core Constraints + +- ``equal(_:insets:)-69yv2`` +- ``equal(_:insets:)-789w8`` +- ``lessThanOrEqual(_:insets:)-a9x4`` +- ``lessThanOrEqual(_:insets:)-ty0q`` + +### Semantic Constraints + +- ``pin(to:insets:axis:alignment:)-74nhn`` +- ``pin(to:insets:axis:alignment:)-2uvy8`` + +### Instance Methods + +- ``absolute()`` + +### Nested Types + +- ``Alignment`` +- ``Axis`` diff --git a/Sources/Align.docc/AnchorCollectionSize+Extension.md b/Sources/Align.docc/AnchorCollectionSize+Extension.md new file mode 100644 index 0000000..dde2e10 --- /dev/null +++ b/Sources/Align.docc/AnchorCollectionSize+Extension.md @@ -0,0 +1,24 @@ +# ``Align/AnchorCollectionSize`` + +```swift +a.anchors.size.equal(CGSize(width: 120, height: 40)) +``` + +![size](size-01.png) + +```swift +a.anchors.size.equal(b) +``` + +![size](size-02.png) + +## Topics + +### Core Constraints + +- ``equal(_:)`` +- ``equal(_:insets:multiplier:)`` +- ``lessThanOrEqual(_:)`` +- ``lessThanOrEqual(_:insets:multiplier:)`` +- ``greaterThanOrEqul(_:)`` +- ``greaterThanOrEqual(_:insets:multiplier:)`` diff --git a/Sources/Align.docc/Constraints+Extension.md b/Sources/Align.docc/Constraints+Extension.md new file mode 100644 index 0000000..3b339d9 --- /dev/null +++ b/Sources/Align.docc/Constraints+Extension.md @@ -0,0 +1,51 @@ +# ``Align/Constraints`` + +By default, Align automatically activates created constraints. Using `Constraints` API, constraints are activated all of the same time when you exit from the closure. It gives you a chance to change the `priority` of the constraints. + +```swift +Constraints(for: title, subtitle) { title, subtitle in + // Align one anchor with another + subtitle.top.spacing(10, to: title.bottom + 10) + + // Manipulate dimensions + title.width.equal(100) + + // Change a priority of constraints inside a group: + subtitle.bottom.pin().priority = UILayoutPriority(999) +} +``` + +`Constraints` also give you easy access to Align anchors (notice, there is no `.anchors` call in the example). And if you want to not activate the constraints, there is an option for that: + +```swift +Constraints(activate: false) { + // Create your constraints here +} +``` + +## Topics + +### Initializers + +- ``init(activate:_:)`` + +### Variadic Initializers + +The following initializers provide convenient access to the views and their anchors. + +- ``init(for:_:)`` +- ``init(for:_:_:)`` +- ``init(for:_:_:_:)`` +- ``init(for:_:_:_:_:)`` + +### Accessing Underlying Constraints + +- ``constraints`` +- ``activate()`` +- ``deactivate()`` + +### Collection Conformance + +- ``startIndex`` +- ``endIndex`` +- ``index(after:)`` diff --git a/Sources/Align.docc/LayoutAnchors+Extension.md b/Sources/Align.docc/LayoutAnchors+Extension.md new file mode 100644 index 0000000..726a766 --- /dev/null +++ b/Sources/Align.docc/LayoutAnchors+Extension.md @@ -0,0 +1,41 @@ +# ``Align/LayoutAnchors`` + +## Topics + +### Edges + +- ``top`` +- ``bottom`` +- ``left`` +- ``right`` +- ``leading`` +- ``trailing`` + +### Center + +- ``centerX`` +- ``centerY`` + +### Baselines + +- ``firstBaseline`` +- ``lastBaseline`` + +### Dimensions + +- ``width`` +- ``height`` + +### Collections + +- ``edges`` +- ``center`` +- ``size`` + +### Accessing Underlying View + +- ``item`` + +### Deprecated + +- ``base`` diff --git a/Sources/Align.docc/Resources/01.png b/Sources/Align.docc/Resources/01.png new file mode 100644 index 0000000..e1dfd2d Binary files /dev/null and b/Sources/Align.docc/Resources/01.png differ diff --git a/Sources/Align.docc/Resources/02.png b/Sources/Align.docc/Resources/02.png new file mode 100644 index 0000000..cd737ae Binary files /dev/null and b/Sources/Align.docc/Resources/02.png differ diff --git a/Sources/Align.docc/Resources/03.png b/Sources/Align.docc/Resources/03.png new file mode 100644 index 0000000..2623f3e Binary files /dev/null and b/Sources/Align.docc/Resources/03.png differ diff --git a/Sources/Align.docc/Resources/04.png b/Sources/Align.docc/Resources/04.png new file mode 100644 index 0000000..903708d Binary files /dev/null and b/Sources/Align.docc/Resources/04.png differ diff --git a/Sources/Align.docc/Resources/05.png b/Sources/Align.docc/Resources/05.png new file mode 100644 index 0000000..fabdc7c Binary files /dev/null and b/Sources/Align.docc/Resources/05.png differ diff --git a/Sources/Align.docc/Resources/06.png b/Sources/Align.docc/Resources/06.png new file mode 100644 index 0000000..8a5fccd Binary files /dev/null and b/Sources/Align.docc/Resources/06.png differ diff --git a/Docs/align-cheat-sheet.pdf b/Sources/Align.docc/Resources/align-cheat-sheet.pdf similarity index 100% rename from Docs/align-cheat-sheet.pdf rename to Sources/Align.docc/Resources/align-cheat-sheet.pdf diff --git a/Sources/Align.docc/Resources/center-01.png b/Sources/Align.docc/Resources/center-01.png new file mode 100644 index 0000000..1def4b8 Binary files /dev/null and b/Sources/Align.docc/Resources/center-01.png differ diff --git a/Sources/Align.docc/Resources/edges-01.png b/Sources/Align.docc/Resources/edges-01.png new file mode 100644 index 0000000..8d25741 Binary files /dev/null and b/Sources/Align.docc/Resources/edges-01.png differ diff --git a/Sources/Align.docc/Resources/edges-02.png b/Sources/Align.docc/Resources/edges-02.png new file mode 100644 index 0000000..ab10bb3 Binary files /dev/null and b/Sources/Align.docc/Resources/edges-02.png differ diff --git a/Sources/Align.docc/Resources/edges-03.png b/Sources/Align.docc/Resources/edges-03.png new file mode 100644 index 0000000..151fcb9 Binary files /dev/null and b/Sources/Align.docc/Resources/edges-03.png differ diff --git a/Sources/Align.docc/Resources/edges-04.png b/Sources/Align.docc/Resources/edges-04.png new file mode 100644 index 0000000..59b858f Binary files /dev/null and b/Sources/Align.docc/Resources/edges-04.png differ diff --git a/Sources/Align.docc/Resources/edges-05.png b/Sources/Align.docc/Resources/edges-05.png new file mode 100644 index 0000000..f3ff1fe Binary files /dev/null and b/Sources/Align.docc/Resources/edges-05.png differ diff --git a/Sources/Align.docc/Resources/logo.png b/Sources/Align.docc/Resources/logo.png new file mode 100644 index 0000000..1c0a890 Binary files /dev/null and b/Sources/Align.docc/Resources/logo.png differ diff --git a/Sources/Align.docc/Resources/size-01.png b/Sources/Align.docc/Resources/size-01.png new file mode 100644 index 0000000..fc31fe4 Binary files /dev/null and b/Sources/Align.docc/Resources/size-01.png differ diff --git a/Sources/Align.docc/Resources/size-02.png b/Sources/Align.docc/Resources/size-02.png new file mode 100644 index 0000000..0bef153 Binary files /dev/null and b/Sources/Align.docc/Resources/size-02.png differ diff --git a/Sources/Align.swift b/Sources/Align.swift index 53fdb7a..b042894 100644 --- a/Sources/Align.swift +++ b/Sources/Align.swift @@ -1,76 +1,86 @@ // The MIT License (MIT) // -// Copyright (c) 2017-2020 Alexander Grebenyuk (github.com/kean). +// Copyright (c) 2017-2022 Alexander Grebenyuk (github.com/kean). #if os(iOS) || os(tvOS) import UIKit +#elseif os(macOS) +import AppKit +#endif -public protocol LayoutItem { // `UIView`, `UILayoutGuide` +/// A type that has layout anchors: either a view or a layout guide. +public protocol LayoutItem { +#if os(iOS) || os(tvOS) var superview: UIView? { get } +#else + var superview: NSView? { get } +#endif } +#if os(iOS) || os(tvOS) extension UIView: LayoutItem {} extension UILayoutGuide: LayoutItem { public var superview: UIView? { owningView } } #elseif os(macOS) -import AppKit - -public protocol LayoutItem { // `NSView`, `NSLayoutGuide` - var superview: NSView? { get } -} - extension NSView: LayoutItem {} extension NSLayoutGuide: LayoutItem { public var superview: NSView? { owningView } } #endif -public extension LayoutItem { // Align methods are available via `LayoutAnchors` - @nonobjc var anchors: LayoutAnchors { LayoutAnchors(base: self) } +extension LayoutItem { // Align methods are available via `LayoutAnchors` + /// Provides access to the layout anchors and anchor collections. + @nonobjc public var anchors: LayoutAnchors { LayoutAnchors(self) } } // MARK: - LayoutAnchors -public struct LayoutAnchors { - public let base: Base -} +/// Provides access to the layout anchors and anchor collections. +public struct LayoutAnchors { + /// The underlying item. + public let item: T -public extension LayoutAnchors where Base: LayoutItem { + public init(_ item: T) { self.item = item } + + // Deprecated in Align 3.0 + @available(*, deprecated, message: "Please use `view` to `layoutGuide`.") + public var base: T { item } // MARK: Anchors - var top: Anchor { Anchor(base, .top) } - var bottom: Anchor { Anchor(base, .bottom) } - var left: Anchor { Anchor(base, .left) } - var right: Anchor { Anchor(base, .right) } - var leading: Anchor { Anchor(base, .leading) } - var trailing: Anchor { Anchor(base, .trailing) } + public var top: Anchor { Anchor(item, .top) } + public var bottom: Anchor { Anchor(item, .bottom) } + public var left: Anchor { Anchor(item, .left) } + public var right: Anchor { Anchor(item, .right) } + public var leading: Anchor { Anchor(item, .leading) } + public var trailing: Anchor { Anchor(item, .trailing) } - var centerX: Anchor { Anchor(base, .centerX) } - var centerY: Anchor { Anchor(base, .centerY) } + public var centerX: Anchor { Anchor(item, .centerX) } + public var centerY: Anchor { Anchor(item, .centerY) } - var firstBaseline: Anchor { Anchor(base, .firstBaseline) } - var lastBaseline: Anchor { Anchor(base, .lastBaseline) } + public var firstBaseline: Anchor { Anchor(item, .firstBaseline) } + public var lastBaseline: Anchor { Anchor(item, .lastBaseline) } - var width: Anchor { Anchor(base, .width) } - var height: Anchor { Anchor(base, .height) } + public var width: Anchor { Anchor(item, .width) } + public var height: Anchor { Anchor(item, .height) } // MARK: Anchor Collections - var edges: AnchorCollectionEdges { AnchorCollectionEdges(item: base) } - var center: AnchorCollectionCenter { AnchorCollectionCenter(x: centerX, y: centerY) } - var size: AnchorCollectionSize { AnchorCollectionSize(width: width, height: height) } + public var edges: AnchorCollectionEdges { AnchorCollectionEdges(item: item) } + public var center: AnchorCollectionCenter { AnchorCollectionCenter(x: centerX, y: centerY) } + public var size: AnchorCollectionSize { AnchorCollectionSize(width: width, height: height) } } // MARK: - Anchors -// phantom types +/// A type that represents a layout axis. public enum AnchorAxis { public class Horizontal {} public class Vertical {} } +/// Represents an anchor type. public enum AnchorType { public class Dimension {} public class Alignment {} @@ -79,25 +89,25 @@ public enum AnchorType { public class Baseline: Alignment {} } -/// An anchor represents one of the view's layout attributes (e.g. `left`, -/// `centerX`, `width`, etc). +/// An anchor represents one of the view's layout attributes. /// -/// Instead of creating `NSLayoutConstraint` objects directly, start with a `UIView`, -/// `NSView`, or `UILayoutGuide` object you wish to constrain, and select one of -/// that object’s anchor properties. These properties correspond to the main -/// `NSLayoutConstraint.Attribute` values used in Auto Layout, and provide an -/// appropriate `Anchor` type for creating constraints to that attribute. For -/// example, `view.anchors.top` is represted by `Anchor`. -/// Use the anchor’s methods to construct your constraint. +/// Instead of creating `NSLayoutConstraint` objects directly, start with a `UIView` +/// or `UILayoutGuide` and select one of its anchors. For example, `view.anchors.top` +/// is represted by `Anchor`. Then use the +/// anchor’s methods to construct your constraint. /// -/// - note: `UIView` does not provide anchor properties for the layout margin attributes. -/// Instead, the `layoutMarginsGuide` property provides a `UILayoutGuide` object that -/// represents these margins. Use the guide’s anchor properties to create your constraints. +/// ```swift +/// // Align two views along one of the edges +/// a.anchors.leading.equal(b.anchors.leading) +/// ``` /// /// When you create constraints using `Anchor` APIs, the constraints are activated /// automatically and the target view has `translatesAutoresizingMaskIntoConstraints` /// set to `false`. If you want to activate all the constraints at the same or /// create them without activation, use `Constraints` type. +/// +/// - tip: `UIView` does not provide anchor properties for the layout margin attributes. +/// Instead, call `view.layoutMarginsGuide.anchors`. public struct Anchor { // type and axis are phantom types let item: LayoutItem let attribute: NSLayoutConstraint.Attribute @@ -110,27 +120,30 @@ public struct Anchor { // type and axis are phantom types /// Returns a new anchor offset by a given amount. /// - /// - note: Consider using a convenience operator instead: `view.anchors.top + 10`. + /// - tip: Consider using a convenience operator instead: `view.anchors.top + 10`. public func offsetting(by offset: CGFloat) -> Anchor { Anchor(item, attribute, offset: self.offset + offset, multiplier: self.multiplier) } - /// Returns a new anchor with a given multiplier. + /// Returns a new anchor with an constant multiplied by the given amount. /// - /// - note: Consider using a convenience operator instead: `view.anchors.height * 2`. + /// - tip: Consider using a convenience operator instead: `view.anchors.height * 2`. public func multiplied(by multiplier: CGFloat) -> Anchor { Anchor(item, attribute, offset: self.offset * multiplier, multiplier: self.multiplier * multiplier) } } +/// Returns a new anchor offset by a given amount. public func + (anchor: Anchor, offset: CGFloat) -> Anchor { anchor.offsetting(by: offset) } +/// Returns a new anchor offset by a given amount. public func - (anchor: Anchor, offset: CGFloat) -> Anchor { anchor.offsetting(by: -offset) } +/// Returns a new anchor with an constant multiplied by the given amount. public func * (anchor: Anchor, multiplier: CGFloat) -> Anchor { anchor.multiplied(by: multiplier) } @@ -138,7 +151,6 @@ public func * (anchor: Anchor, multiplier: CGFloat) -> A // MARK: - Anchors (AnchorType.Alignment) public extension Anchor where Type: AnchorType.Alignment { - /// Adds a constraint that defines the anchors' attributes as equal to each other. @discardableResult func equal(_ anchor: Anchor, constant: CGFloat = 0) -> NSLayoutConstraint { Constraints.add(self, anchor, constant: constant, relation: .equal) } @@ -155,7 +167,6 @@ public extension Anchor where Type: AnchorType.Alignment { // MARK: - Anchors (AnchorType.Dimension) public extension Anchor where Type: AnchorType.Dimension { - /// Adds a constraint that defines the anchors' attributes as equal to each other. @discardableResult func equal(_ anchor: Anchor, constant: CGFloat = 0) -> NSLayoutConstraint { Constraints.add(self, anchor, constant: constant, relation: .equal) } @@ -219,47 +230,21 @@ public extension Anchor where Type: AnchorType.Center { // MARK: - AnchorCollectionEdges -public struct Alignment { - public enum Horizontal { - case fill, center, leading, trailing - } - public enum Vertical { - case fill, center, top, bottom - } - - public let horizontal: Horizontal - public let vertical: Vertical - - public init(horizontal: Horizontal, vertical: Vertical) { - (self.horizontal, self.vertical) = (horizontal, vertical) - } - - public static let fill = Alignment(horizontal: .fill, vertical: .fill) - public static let center = Alignment(horizontal: .center, vertical: .center) - public static let topLeading = Alignment(horizontal: .leading, vertical: .top) - public static let leading = Alignment(horizontal: .leading, vertical: .fill) - public static let bottomLeading = Alignment(horizontal: .leading, vertical: .bottom) - public static let bottom = Alignment(horizontal: .fill, vertical: .bottom) - public static let bottomTrailing = Alignment(horizontal: .trailing, vertical: .bottom) - public static let trailing = Alignment(horizontal: .trailing, vertical: .fill) - public static let topTrailing = Alignment(horizontal: .trailing, vertical: .top) - public static let top = Alignment(horizontal: .fill, vertical: .top) -} - +/// Create multiple constraints at once by operating more than one edge at once. public struct AnchorCollectionEdges { let item: LayoutItem var isAbsolute = false - // By default, edges use locale-specific `.leading` and `.trailing` + /// Use `left` and `right` edges instead of `leading` and `trailing`. public func absolute() -> AnchorCollectionEdges { AnchorCollectionEdges(item: item, isAbsolute: true) } - #if os(iOS) || os(tvOS) +#if os(iOS) || os(tvOS) public typealias Axis = NSLayoutConstraint.Axis - #else +#else public typealias Axis = NSLayoutConstraint.Orientation - #endif +#endif // MARK: Core API @@ -285,7 +270,7 @@ public struct AnchorCollectionEdges { /// to the superview. /// /// - parameter target: The target view, by default, uses the superview. - /// - parameter insets: Insets the reciever's edges by the given insets. + /// - parameter insets: Insets the receiver's edges by the given insets. /// - parameter axis: If provided, creates constraints only along the given /// axis. For example, if you pass axis `.horizontal`, only the `.leading`, /// `.trailing` (and `.centerX` if needed) attributes are used. `nil` by default @@ -299,7 +284,7 @@ public struct AnchorCollectionEdges { /// to the superview. /// /// - parameter target: The target view, by default, uses the superview. - /// - parameter insets: Insets the reciever's edges by the given insets. + /// - parameter insets: Insets the receiver's edges by the given insets. /// - parameter axis: If provided, creates constraints only along the given /// axis. For example, if you pass axis `.horizontal`, only the `.leading`, /// `.trailing` (and `.centerX` if needed) attributes are used. `nil` by default @@ -335,10 +320,94 @@ public struct AnchorCollectionEdges { } return constraints } + + public struct Alignment { + + /// The alignment along the horizontal axis. + public enum Horizontal { + /// Pin both leading and trailing edges to the superview. + case fill + /// Center the view in the container along the vertical axis. + case center + /// Pin the leading edge to the superview and prevent the view from + /// overflowing the container by pinning the trailing edge using + /// "less than or equal" constraint. + case leading + /// Pin the trailing edge to the superview and prevent the view from + /// overflowing the container by pinning the leading edge using + /// "less than or equal" constraint. + case trailing + } + + /// The alignment along the vertical axis. + public enum Vertical { + /// Pin both top and bottom edges to the superview. + case fill + /// Center the view in the container along the vertical axis. + case center + /// Pin the top edge to the superview and prevent the view from + /// overflowing the container by pinning the bottom edge using + /// "less than or equal" constraint. + case top + /// Pin the bottom edge to the superview and prevent the view from + /// overflowing the container by pinning the top edge using + /// "less than or equal" constraint. + case bottom + } + /// The alignment along the horizontal axis. + public let horizontal: Horizontal + /// The alignment along the vertical axis. + public let vertical: Vertical + + /// Initializes the alignment. + public init(horizontal: Horizontal, vertical: Vertical) { + (self.horizontal, self.vertical) = (horizontal, vertical) + } + + /// The edges are pinned to the matching edges of the container with the + /// given edge insets. + public static let fill = Alignment(horizontal: .fill, vertical: .fill) + /// The view is centered in the container and the edges are pinned using + /// "less than or equal" constraints making sure it doesn't overflow the container. + public static let center = Alignment(horizontal: .center, vertical: .center) + /// The view is pinned to the top-leading corner of the container with the + /// given edge insets and the remaining edges are pinned using "less than or + /// equal" constraints making sure the view doesn't overflow the container. + public static let topLeading = Alignment(horizontal: .leading, vertical: .top) + /// The view is pinned to the top edge with the inset while the bottom + /// edge is pinned using "less than or equal" constraint making sure the view + /// doesn't overflow the container. The view is also centered horizontally. + public static let top = Alignment(horizontal: .center, vertical: .top) + /// The view is pinned to the bottom-trailing corner of the container with the + /// given edge insets and the remaining edges are pinned using "less than or + /// equal" constraints making sure the view doesn't overflow the container. + public static let topTrailing = Alignment(horizontal: .trailing, vertical: .top) + /// The view is pinned to the trailing edge with the inset while the leading + /// edge is pinned using "less than or equal" constraint making sure the view + /// doesn't overflow the container. The view is also centered vertically. + public static let trailing = Alignment(horizontal: .trailing, vertical: .center) + /// The view is pinned to the bottom-trailing corner of the container with the + /// given edge insets and the remaining edges are pinned using "less than or + /// equal" constraints making sure the view doesn't overflow the container. + public static let bottomTrailing = Alignment(horizontal: .trailing, vertical: .bottom) + /// The view is pinned to the bottom edge with the inset while the top + /// edge is pinned using "less than or equal" constraint making sure the view + /// doesn't overflow the container. The view is also centered horizontally. + public static let bottom = Alignment(horizontal: .center, vertical: .bottom) + /// The view is pinned to the bottom-leading corner of the container with the + /// given edge insets and the remaining edges are pinned using "less than or + /// equal" constraints making sure the view doesn't overflow the container. + public static let bottomLeading = Alignment(horizontal: .leading, vertical: .bottom) + /// The view is pinned to the leading edge with the inset while the trailing + /// edge is pinned using "less than or equal" constraint making sure the view + /// doesn't overflow the container. The view is also centered vertically. + public static let leading = Alignment(horizontal: .leading, vertical: .center) + } } // MARK: - AnchorCollectionCenter +/// Create multiple constraints at once by using both `centerX` and `centerY` anchors. public struct AnchorCollectionCenter { let x: Anchor let y: Anchor @@ -372,6 +441,7 @@ public struct AnchorCollectionCenter { // MARK: - AnchorCollectionSize +/// Create multiple constraints at once by using both `width` and `height` anchors. public struct AnchorCollectionSize { let width: Anchor let height: Anchor @@ -409,6 +479,35 @@ public struct AnchorCollectionSize { // MARK: - Constraints +/// Allows you to access the underlying constraints. +/// +/// By default, Align automatically activates created constraints. Using +/// ``Constraints`` API, constraints are activated all of the same time when you +/// exit from the closure. It gives you a chance to change the `priority` of +/// the created constraints. +/// +/// ```swift +/// Constraints(for: title, subtitle) { title, subtitle in +/// // Align one anchor with another +/// subtitle.top.spacing(10, to: title.bottom + 10) +/// +/// // Manipulate dimensions +/// title.width.equal(100) +/// +/// // Change a priority of constraints inside a group: +/// subtitle.bottom.pin().priority = UILayoutPriority(999) +/// } +/// ``` +/// +/// ``Constraints`` also give you easy access to Align anchors (notice, there +/// is no `.anchors` call in the example). And if you want to not activate the +/// constraints, there is an option for that: +/// +/// ```swift +/// Constraints(activate: false) { +/// // Create your constraints here +/// } +/// ``` public final class Constraints: Collection { public typealias Element = NSLayoutConstraint public typealias Index = Int @@ -439,12 +538,12 @@ public final class Constraints: Collection { // MARK: Activate - /// Activates each constraint in the reciever. + /// Activates each constraint in the receiver. public func activate() { NSLayoutConstraint.activate(constraints) } - /// Deactivates each constraint in the reciever. + /// Deactivates each constraint in the receiver. public func deactivate() { NSLayoutConstraint.deactivate(constraints) } diff --git a/Supporting/Align.xcconfig b/Supporting/Align.xcconfig deleted file mode 100644 index 86345ed..0000000 --- a/Supporting/Align.xcconfig +++ /dev/null @@ -1,10 +0,0 @@ -// Configuration settings file format documentation can be found at: -// https://help.apple.com/xcode/#/dev745c5c974 - -SUPPORTED_PLATFORMS = iphoneos appletvos iphonesimulator appletvsimulator macosx - -SWIFT_VERSION = 5.0 - -IPHONEOS_DEPLOYMENT_TARGET = 11.0 -TVOS_DEPLOYMENT_TARGET = 11.0 -MACOSX_DEPLOYMENT_TARGET = 10.13 diff --git a/Supporting/Info.plist b/Supporting/Info.plist deleted file mode 100644 index ca23c84..0000000 --- a/Supporting/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - $(MARKETING_VERSION) - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/Tests/AnchorAPIsTests.swift b/Tests/AnchorAPIsTests.swift index e406f36..e18a5f5 100644 --- a/Tests/AnchorAPIsTests.swift +++ b/Tests/AnchorAPIsTests.swift @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) 2017-2020 Alexander Grebenyuk (github.com/kean). +// Copyright (c) 2017-2022 Alexander Grebenyuk (github.com/kean). import Foundation import XCTest diff --git a/Tests/AnchorAlignmentTests.swift b/Tests/AnchorAlignmentTests.swift index 29b90cb..61d176b 100644 --- a/Tests/AnchorAlignmentTests.swift +++ b/Tests/AnchorAlignmentTests.swift @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) 2017-2020 Alexander Grebenyuk (github.com/kean). +// Copyright (c) 2017-2022 Alexander Grebenyuk (github.com/kean). import XCTest import Align diff --git a/Tests/AnchorCenterTests.swift b/Tests/AnchorCenterTests.swift index 0572739..8230981 100644 --- a/Tests/AnchorCenterTests.swift +++ b/Tests/AnchorCenterTests.swift @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) 2017-2020 Alexander Grebenyuk (github.com/kean). +// Copyright (c) 2017-2022 Alexander Grebenyuk (github.com/kean). import XCTest import Align diff --git a/Tests/AnchorCollectionCenterTests.swift b/Tests/AnchorCollectionCenterTests.swift index 284f370..3077275 100644 --- a/Tests/AnchorCollectionCenterTests.swift +++ b/Tests/AnchorCollectionCenterTests.swift @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) 2017-2020 Alexander Grebenyuk (github.com/kean). +// Copyright (c) 2017-2022 Alexander Grebenyuk (github.com/kean). import XCTest import Align diff --git a/Tests/AnchorCollectionEdgesTests.swift b/Tests/AnchorCollectionEdgesTests.swift index c964f31..c7f3d29 100644 --- a/Tests/AnchorCollectionEdgesTests.swift +++ b/Tests/AnchorCollectionEdgesTests.swift @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) 2017-2020 Alexander Grebenyuk (github.com/kean). +// Copyright (c) 2017-2022 Alexander Grebenyuk (github.com/kean). import XCTest import Align diff --git a/Tests/AnchorCollectionSizeTests.swift b/Tests/AnchorCollectionSizeTests.swift index 1b71494..74723a6 100644 --- a/Tests/AnchorCollectionSizeTests.swift +++ b/Tests/AnchorCollectionSizeTests.swift @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) 2017-2020 Alexander Grebenyuk (github.com/kean). +// Copyright (c) 2017-2022 Alexander Grebenyuk (github.com/kean). import XCTest import Align diff --git a/Tests/AnchorDimensionTests.swift b/Tests/AnchorDimensionTests.swift index b4c0572..df70143 100644 --- a/Tests/AnchorDimensionTests.swift +++ b/Tests/AnchorDimensionTests.swift @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) 2017-2020 Alexander Grebenyuk (github.com/kean). +// Copyright (c) 2017-2022 Alexander Grebenyuk (github.com/kean). import XCTest import Align diff --git a/Tests/AnchorEdgeTests.swift b/Tests/AnchorEdgeTests.swift index 8b83b19..45a6798 100644 --- a/Tests/AnchorEdgeTests.swift +++ b/Tests/AnchorEdgeTests.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2017-2020 Alexander Grebenyuk (github.com/kean). +// Copyright (c) 2017-2022 Alexander Grebenyuk (github.com/kean). import XCTest import Align diff --git a/Tests/AnchorPerformanceTests.swift b/Tests/AnchorPerformanceTests.swift index c4d3fa8..01a576c 100644 --- a/Tests/AnchorPerformanceTests.swift +++ b/Tests/AnchorPerformanceTests.swift @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) 2017-2020 Alexander Grebenyuk (github.com/kean). +// Copyright (c) 2017-2022 Alexander Grebenyuk (github.com/kean). import XCTest import Align diff --git a/Tests/AnchorTests.swift b/Tests/AnchorTests.swift index c7ca714..020f604 100644 --- a/Tests/AnchorTests.swift +++ b/Tests/AnchorTests.swift @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) 2017-2020 Alexander Grebenyuk (github.com/kean). +// Copyright (c) 2017-2022 Alexander Grebenyuk (github.com/kean). import XCTest import Align diff --git a/Tests/ConstraintsTests.swift b/Tests/ConstraintsTests.swift index 3f3e74b..d3eeeb9 100644 --- a/Tests/ConstraintsTests.swift +++ b/Tests/ConstraintsTests.swift @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) 2017-2020 Alexander Grebenyuk (github.com/kean). +// Copyright (c) 2017-2022 Alexander Grebenyuk (github.com/kean). import XCTest import Align @@ -124,32 +124,32 @@ class ConstraintsArityTests: XCTestCase { func testOne() { Constraints(for: a) { - XCTAssertTrue($0.base === a) + XCTAssertTrue($0.item === a) return } } func testTwo() { Constraints(for: a, b) { - XCTAssertTrue($0.base === a) - XCTAssertTrue($1.base === b) + XCTAssertTrue($0.item === a) + XCTAssertTrue($1.item === b) } } func testThree() { Constraints(for: a, b, c) { - XCTAssertTrue($0.base === a) - XCTAssertTrue($1.base === b) - XCTAssertTrue($2.base === c) + XCTAssertTrue($0.item === a) + XCTAssertTrue($1.item === b) + XCTAssertTrue($2.item === c) } } func testFour() { Constraints(for: a, b, c, d) { - XCTAssertTrue($0.base === a) - XCTAssertTrue($1.base === b) - XCTAssertTrue($2.base === c) - XCTAssertTrue($3.base === d) + XCTAssertTrue($0.item === a) + XCTAssertTrue($1.item === b) + XCTAssertTrue($2.item === c) + XCTAssertTrue($3.item === d) } } } @@ -164,42 +164,42 @@ class AddingSubviewsTests: XCTestCase { func testOne() { container.addSubview(a) { - XCTAssertTrue($0.base.superview === container) - XCTAssertTrue($0.base === a) + XCTAssertTrue($0.item.superview === container) + XCTAssertTrue($0.item === a) return } } func testTwo() { container.addSubview(a, b) { - XCTAssertTrue($0.base.superview === container) - XCTAssertTrue($1.base.superview === container) - XCTAssertTrue($0.base === a) - XCTAssertTrue($1.base === b) + XCTAssertTrue($0.item.superview === container) + XCTAssertTrue($1.item.superview === container) + XCTAssertTrue($0.item === a) + XCTAssertTrue($1.item === b) } } func testThree() { container.addSubview(a, b, c) { - XCTAssertTrue($0.base.superview === container) - XCTAssertTrue($1.base.superview === container) - XCTAssertTrue($2.base.superview === container) - XCTAssertTrue($0.base === a) - XCTAssertTrue($1.base === b) - XCTAssertTrue($2.base === c) + XCTAssertTrue($0.item.superview === container) + XCTAssertTrue($1.item.superview === container) + XCTAssertTrue($2.item.superview === container) + XCTAssertTrue($0.item === a) + XCTAssertTrue($1.item === b) + XCTAssertTrue($2.item === c) } } func testFour() { container.addSubview(a, b, c, d) { - XCTAssertTrue($0.base.superview === container) - XCTAssertTrue($1.base.superview === container) - XCTAssertTrue($2.base.superview === container) - XCTAssertTrue($3.base.superview === container) - XCTAssertTrue($0.base === a) - XCTAssertTrue($1.base === b) - XCTAssertTrue($2.base === c) - XCTAssertTrue($3.base === d) + XCTAssertTrue($0.item.superview === container) + XCTAssertTrue($1.item.superview === container) + XCTAssertTrue($2.item.superview === container) + XCTAssertTrue($3.item.superview === container) + XCTAssertTrue($0.item === a) + XCTAssertTrue($1.item === b) + XCTAssertTrue($2.item === c) + XCTAssertTrue($3.item === d) } } } diff --git a/Tests/Extensions/Align+Extensions.swift b/Tests/Extensions/Align+Extensions.swift index 3ebd825..6b7f8c2 100644 --- a/Tests/Extensions/Align+Extensions.swift +++ b/Tests/Extensions/Align+Extensions.swift @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) 2017-2020 Alexander Grebenyuk (github.com/kean). +// Copyright (c) 2017-2022 Alexander Grebenyuk (github.com/kean). #if os(iOS) || os(tvOS) diff --git a/Tests/Extensions/XCTestExtensions.swift b/Tests/Extensions/XCTestExtensions.swift index 6e2c4a9..2187607 100644 --- a/Tests/Extensions/XCTestExtensions.swift +++ b/Tests/Extensions/XCTestExtensions.swift @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) 2017-2020 Alexander Grebenyuk (github.com/kean). +// Copyright (c) 2017-2022 Alexander Grebenyuk (github.com/kean). import XCTest @@ -112,7 +112,7 @@ extension NSLayoutConstraint.Attribute { case .centerXWithinMargins: return "centerXWithinMargins" case .centerYWithinMargins: return "centerYWithinMargins" #endif - @unknown default: return "unexpacted" + @unknown default: return "unexpected" } } } diff --git a/Tests/Info.plist b/Tests/Info.plist deleted file mode 100644 index ba72822..0000000 --- a/Tests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - -