diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 92e5b5e8..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,44 +0,0 @@ -# iOS CircleCI 2.0 configuration file -# -# Check https://circleci.com/docs/2.0/ios-migrating-from-1-2/ for more details -# -version: 2 -jobs: - build: - - # Specify the Xcode version to use - macos: - xcode: "9.2.0" - - dependencies: - pre: - - rvm install rubygems 2.4.8 --force - - gem install bundler -v 1.11.2 - - steps: - - checkout - - # Install CocoaPods - - run: - name: Install CocoaPods - command: pod install - - # Build the app and run tests - - run: - name: Build and run tests - command: bundle exec fastlane travis - environment: - SCAN_DEVICE: iPhone 6 - SCAN_SCHEME: WebTests - - # Collect XML test results data to show in the UI, - # and save the same XML files under test-results folder - # in the Artifacts tab - - store_test_results: - path: test_output/report.xml - - store_artifacts: - path: /tmp/test-results - destination: scan-test-results - - store_artifacts: - path: ~/Library/Logs/scan - destination: scan-logs diff --git a/.codeclimate.yml b/.codeclimate.yml deleted file mode 100644 index 43f5d15a..00000000 --- a/.codeclimate.yml +++ /dev/null @@ -1,14 +0,0 @@ -engines: - tailor: - enabled: true - -ratings: - paths: - - "**.swift" - -exclude_paths: - - Tests - - Example - - Pods - - data - - scripts diff --git a/.github/workflows/github-actions-ci.yml b/.github/workflows/github-actions-ci.yml new file mode 100644 index 00000000..5507b3d8 --- /dev/null +++ b/.github/workflows/github-actions-ci.yml @@ -0,0 +1,160 @@ +name: Run CI +on: [push, pull_request] + +env: + XCODE_VERSION: "16.4.0" + IOS_SIMULATOR_DEVICE: "iPhone 16" + IOS_SIMULATOR_OS: "18.4" + TVOS_SIMULATOR_DEVICE: "Apple TV 4K (3rd generation)" + TVOS_SIMULATOR_OS: "18.4" + +jobs: + CI: + runs-on: macos-15 + + # ℹ️ Available GitHub Actions Runner Images + # https://github.com/actions/runner-images + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup CocoaPods + run: pod install --repo-update + + - name: Set Xcode Version + run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app + + - name: Build PinLayout-iOS + run: | + set -o pipefail && xcodebuild build \ + -project PinLayout.xcodeproj \ + -scheme PinLayout-iOS \ + -sdk iphonesimulator \ + -destination "platform=iOS Simulator,name=${{ env.IOS_SIMULATOR_DEVICE }},OS=${{ env.IOS_SIMULATOR_OS }}" \ + | xcpretty + + - name: Build PinLayout-tvOS + run: | + set -o pipefail && xcodebuild build \ + -project PinLayout.xcodeproj \ + -scheme PinLayout-tvOS \ + -sdk appletvsimulator \ + -destination "platform=tvOS Simulator,name=${{ env.TVOS_SIMULATOR_DEVICE }},OS=${{ env.TVOS_SIMULATOR_OS }}" \ + | xcpretty + + - name: Build PinLayout-macOS + run: | + set -o pipefail && xcodebuild build \ + -project PinLayout.xcodeproj \ + -scheme PinLayout-macOS \ + -sdk macosx \ + | xcpretty + + - name: Build PinLayoutSample + run: | + set -o pipefail && xcodebuild build \ + -workspace PinLayout.xcworkspace \ + -scheme PinLayoutSample \ + -sdk iphonesimulator \ + -destination "platform=iOS Simulator,name=${{ env.IOS_SIMULATOR_DEVICE }},OS=${{ env.IOS_SIMULATOR_OS }}" \ + | xcpretty + + - name: iOS Unit Tests + run: | + set -o pipefail && xcodebuild build test \ + -workspace PinLayout.xcworkspace \ + -scheme PinLayout-iOS \ + -sdk iphonesimulator \ + -destination "platform=iOS Simulator,name=${{ env.IOS_SIMULATOR_DEVICE }},OS=${{ env.IOS_SIMULATOR_OS }}" \ + | xcpretty + + # - name: tvOS Unit Tests + # run: | + # set -o pipefail && xcodebuild build test \ + # -workspace PinLayout.xcworkspace \ + # -scheme PinLayout-tvOS \ + # -sdk appletvsimulator \ + # -destination "platform=tvOS Simulator,name=${{ env.TVOS_SIMULATOR_DEVICE }},OS=${{ env.TVOS_SIMULATOR_OS }}" \ + # | xcpretty + + # - name: macOS Unit Tests + # run: | + # set -o pipefail && xcodebuild test \ + # -workspace PinLayout.xcworkspace \ + # -scheme PinLayout-macOS \ + # -sdk macosx \ + # -destination "platform=macOS,name=Any Mac" \ + # | xcpretty + + - name: Test CocoaPods Integration + run: | + echo "Testing CocoaPods integration..." + cd TestProjects/cocoapods/ios + pod install + set -o pipefail && xcodebuild build \ + -workspace PinLayout-iOS.xcworkspace \ + -scheme PinLayout-iOS \ + -sdk iphonesimulator \ + -destination "platform=iOS Simulator,name=${{ env.IOS_SIMULATOR_DEVICE }},OS=${{ env.IOS_SIMULATOR_OS }}" \ + | xcpretty + cd ../../.. + + # - name: Test CocoaPods macOS Integration + # run: | + # cd TestProjects/cocoapods/macos + # pod install + # set -o pipefail && xcodebuild build \ + # -workspace PinLayout-macOS.xcworkspace \ + # -scheme PinLayout-macOS \ + # -sdk macosx \ + # | xcpretty + # cd ../../.. + + # - name: Test CocoaPods tvOS Integration + # run: | + # cd TestProjects/cocoapods/tvos + # pod install + # set -o pipefail && xcodebuild build \ + # -workspace PinLayout-tvOS.xcworkspace \ + # -scheme PinLayout-tvOS \ + # -sdk appletvsimulator \ + # -destination "platform=tvOS Simulator,name=${{ env.TVOS_SIMULATOR_DEVICE }},OS=${{ env.TVOS_SIMULATOR_OS }}" \ + # | xcpretty + # cd ../../.. + + # - name: Test Carthage Integration + # run: | + # cd TestProjects/carthage/ios + # rm Cartfile + # echo "git \"file:///$GITHUB_WORKSPACE\"" > Cartfile + # carthage update --use-ssh --platform iOS --use-xcframeworks + # set -o pipefail && xcodebuild build \ + # -project PinLayout-Carthage-iOS.xcodeproj \ + # -scheme PinLayout-Carthage-iOS \ + # -sdk iphonesimulator \ + # -destination "platform=iOS Simulator,name=${{ env.IOS_SIMULATOR_DEVICE }},OS=${{ env.IOS_SIMULATOR_OS }}" \ + # | xcpretty + # rm Cartfile.resolved + # cd ../../.. + + - name: Swift Package Manager - iOS Empty project + run: | + cd TestProjects/swift-package-manager/ios + rm -rf .build + set -o pipefail && xcodebuild build \ + -project PinLayout-SPM-iOS.xcodeproj \ + -scheme PinLayout-SPM-iOS \ + -sdk iphonesimulator \ + -destination "platform=iOS Simulator,name=${{ env.IOS_SIMULATOR_DEVICE }},OS=${{ env.IOS_SIMULATOR_OS }}" \ + | xcpretty + cd ../../.. + + - name: Pod Lib Lint + run: set -o pipefail && pod lib lint --allow-warnings + + - name: Codecov + run: bash <(curl -s https://codecov.io/bash) -D /tmp/PinLayout + + - name: Job Status + run: echo "🍏 This job's status is ${{ job.status }}." \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0c4e7350..c375b2bf 100644 --- a/.gitignore +++ b/.gitignore @@ -8,17 +8,11 @@ data/ Pods/ -docs/1.2 -fastlane/README.md -fastlane/report.xml -fastlane/test_output - TestProjects/cocoapods/ios/PinLayout-iOS.xcworkspace/ -TestProjects/cocoapods/ios/Podfile.lock TestProjects/cocoapods/macos/PinLayout-macOS.xcworkspace/ -TestProjects/cocoapods/macos/Podfile.lock TestProjects/cocoapods/tvos/PinLayout-tvOS.xcworkspace/ -TestProjects/cocoapods/tvos/Podfile.lock TestProjects/carthage/ios/Cartfile.resolved -TestProjects/carthage/ios/Carthage/ \ No newline at end of file +TestProjects/carthage/ios/Carthage/ + +Example/PinLayoutExampleMacOS diff --git a/.swiftlint.yml b/.swiftlint.yml index 0df8a810..5f739b58 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -21,6 +21,10 @@ disabled_rules: # rule identifiers to exclude from running - identifier_name - line_length - empty_count + - todo + - private_over_fileprivate excluded: # paths to ignore during linting. overridden by `included`. - Pods + - Example/PinLayoutExampleMacOS + - TestProjects diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/.tailor.yml b/.tailor.yml deleted file mode 100644 index 51330aa6..00000000 --- a/.tailor.yml +++ /dev/null @@ -1,19 +0,0 @@ -include: - - PinLayout - -exclude: - - PinLayoutTests - - PinLayoutSample - - Carthage - - Source/Pods - - data - - scripts - -except: - - trailing-whitespace - - brace-style - - function-whitespace - - terminating-semicolon - - lower-camel-case - - trailing-closure - - forced-type-cast diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 12370fd0..00000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: objective-c -osx_image: xcode9.3 -cache: bundler - -install: - - bundle install # --deployment # to cache vendor/bundle - - pod install --repo-update; - -script: - - sh build-ci.sh /tmp/PinLayout - -after_success: - - bash <(curl -s https://codecov.io/bash) -D /tmp/PinLayout - # bash <(curl -s https://codecov.io/bash) -J 'PinLayout' -J 'PinLayoutTests' diff --git a/CHANGELOG.md b/CHANGELOG.md index fe0a9bb2..52e67e91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,398 @@ # Change Log -## [1.7.3](https://github.com/layoutBox/FlexLayout/releases/tag/1.7.2) +## [1.10.6](https://github.com/layoutBox/PinLayout/releases/tag/1.10.6) + +#### Fix CI Failures and Improve Test Code + +Added by [heoblitz](https://github.com/heoblitz) in Pull Request [#283](https://github.com/layoutBox/PinLayout/pull/283) + + +## [1.10.5](https://github.com/layoutBox/PinLayout/releases/tag/1.10.5) +Released on 2023-11-03 + +#### Replace UIScreen.main to get display scale on iOS 13.0 and later + +UIScree.main will be deprecated in a future version of iOS. + +Added by [Hyungyu Kim](https://github.com/hyun99999) in Pull Request [#275](https://github.com/layoutBox/PinLayout/pull/275) + + +## [1.10.3](https://github.com/layoutBox/PinLayout/releases/tag/1.10.2) +Released on 2022-06-07 + +#### Fix Xcode Live Preview + +Fix an error while using Xcode live preview with PinLayout. An internal class has been renamed. + +Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#251](https://github.com/layoutBox/PinLayout/pull/251) + + +## [1.10.2](https://github.com/layoutBox/PinLayout/releases/tag/1.10.2) +Released on 2022-02-01 + +#### Renamed property `pin.keyboardMargins` -> `pin.keyboardArea` + +This new name better represent what `UIKit`'s `UIView.keyboardLayoutGuide` is + +Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#243](https://github.com/layoutBox/PinLayout/pull/243) + +## [1.10.1](https://github.com/layoutBox/PinLayout/releases/tag/1.10.1) +Released on 2022-02-01 + +#### New property `pin.keyboardMargins` + +* `UIView.pin.keyboardMargins`: property expose directly the value of UIKit [`UIView.keyboardLayoutGuide`](https://developer.apple.com/documentation/uikit/keyboards_and_input/adjusting_your_layout_with_keyboard_layout_guide). This is really useful when layout adjustment due to the keyboard is required. iOS 15+ + +Added by [baegteun](https://github.com/baekteun) in Pull Request [#238](https://github.com/layoutBox/PinLayout/pull/238) + + +## [1.10.0](https://github.com/layoutBox/PinLayout/releases/tag/1.10.0) +Released on 2021-05-18 + +#### New Objective-C interface +Instead of using verbose Objective-C with all brackets (`[ ]`): +``` +[[[[[[logo.pinObjc top] left] width:100] aspectRatio] marginWithTop:topLayoutGuide + 10 horizontal:10 bottom:10] layout]; +``` + +It now use a function chaining: +``` +logo.pinObjc.topInsets(safeArea).leftInsets(safeArea).width(100).aspectRatio().margin(margin).layout(); +``` + +Added by [protosse](https://github.com/protosse) in Pull Request [#229](https://github.com/layoutBox/PinLayout/pull/229) + +## [1.9.4](https://github.com/layoutBox/PinLayout/releases/tag/1.9.4) +Released on 2021-05-17 + +* Update to Xcode 12.5 + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#223](https://github.com/layoutBox/PinLayout/pull/223) + + +## [1.9.3](https://github.com/layoutBox/PinLayout/releases/tag/1.9.3) +Released on 2020-12-17 + +#### Fix an issue with Automatic Sizing + +* Fix autoSizeThatFits wrong calculations. There was a problem with size calculations of inner views while using `autoSizeThatFits`. + * Added by [Igor Bulyga](https://github.com/IgorBulyga) in Pull Request [#221](https://github.com/layoutBox/PinLayout/pull/221) + + + +## [1.9.0](https://github.com/layoutBox/PinLayout/releases/tag/1.9.0) +Released on 2019-10-03 + +#### Automatic Sizing (UIView only) + +* By calling `autoSizeThatFits` with the given available size and a layout closure, any layouting performed by PinLayout in that closure will be computed without affecting any subview's `frame` in the view hierarchy. On the other hand, any non PinLayout related code will also be executed. For that reason, it is really important to separate your layout code in it's own function to avoid any side effect during sizing, like setting the scroll view's content size in the above exemple or perhaps assigning `itemSize` in a collection view layout. That kind of code that depends on the layout should only be executed when `layoutSubviews()` is called as part of a normal layout pass. The resulting size also takes into account the margins applied on subviews, even on the bottom and trailing sides. Automatic sizing makes it really easy to write your layout logic once and add proper sizing behavior with virtually no additional effort. See https://github.com/layoutBox/PinLayout#automatic_sizing for more documentation. + * Added by [Antoine Lamy](https://github.com/antoinelamy) in Pull Request [#216](https://github.com/layoutBox/PinLayout/pull/216) + + +## [1.8.11](https://github.com/layoutBox/PinLayout/releases/tag/1.8.11) +Released on 2019-10-03 + +#### Improve method that validates `width` and `height` values + +* Method that set the `width` and `height` now validates even more parameter, by checking NaN and Infinity values. + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#206](https://github.com/layoutBox/PinLayout/pull/206) + + +## [1.8.10](https://github.com/layoutBox/PinLayout/releases/tag/1.8.10) +Released on 2019-09-16 + +#### Usage `UIView.effectiveUserInterfaceLayoutDirection` to detect RTL + +* Use `UIView.effectiveUserInterfaceLayoutDirection` to detect RTL on iOS 10 and above. This is recommended approach to detect layout direction taking into account view's semantic content attribute, trait environment and UIApplication layout direction. + * Added by [MontakOleg](https://github.com/MontakOleg) in Pull Request [#200](https://github.com/layoutBox/PinLayout/pull/200) +* Update Travis to Xcode 11. + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#202](https://github.com/layoutBox/PinLayout/pull/202) + + +## [1.8.9](https://github.com/layoutBox/PinLayout/releases/tag/1.8.9) +Released on 2019-08-16 + +#### Upgrade to Swift 5 + +* Upgrade project to Swift 5 +* Update Pods +* Apply xcodeproj migration + +Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#195](https://github.com/layoutBox/PinLayout/pull/195) + + +## [1.8.8](https://github.com/layoutBox/PinLayout/releases/tag/1.8.8) +Released on 2019-06-25 + +#### Update Swift Package Manager support for Xcode 11 + +* Updated PinLayout to be used with Xcode 11's Swift Package Manager. + * Added by [Hal Lee](https://github.com/hallee) in Pull Request [#192](https://github.com/layoutBox/PinLayout/pull/192) + +* Fix Warnings: `public' modifier is redundant for instance method declared in a public extension`. + * Added by [MontakOleg](https://github.com/MontakOleg) in Pull Request [#193](https://github.com/layoutBox/PinLayout/pull/193) + +## [1.8.7](https://github.com/layoutBox/PinLayout/releases/tag/1.8.7) +Released on 2019-03-02 + +#### Add missing Objective-C API methods + +* wrapContent +* wrapContentWithPadding:(CGFloat) +* wrapContentWithInsets:(PEdgeInsets) +* wrapContentWithType:(WrapType) +* wrapContentWithType:(WrapType) padding:(CGFloat) +* wrapContentWithType:(WrapType) insets:(PEdgeInsets) + +## [1.8.6](https://github.com/layoutBox/PinLayout/releases/tag/1.8.6) +Released on 2018-09-29 + +#### Update support for Swift 4.2 +The PinLayout pod doesn't specify anymore the Swift language version. + +PinLayout supports Swift versions: +* Swift 4.2 / 4.1 / 4.0 +* Swift 3.* + +* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#178](https://github.com/layoutBox/PinLayout/pull/178) + + +## [1.8.5](https://github.com/layoutBox/PinLayout/releases/tag/1.8.5) +Released on 2018-09-27 + +#### Minor internal changes +Remove `sizeToFit()` from SizeCalculable protocol. +This change ensure that PinLayout `pin.sizeToFit()` method behave correctly. As per the iOS documentation, we should not directly override sizeToFit() but rather always only implement sizeThatFits(_:) for auto-sizing needs. This update aim to remove the sizeToFit() requirement in the SizeCalculable protocol. + +* Added by [Antoine Lamy](https://github.com/antoinelamy) in Pull Request [#164](https://github.com/layoutBox/PinLayout/pull/164) + + +## [1.8.4](https://github.com/layoutBox/PinLayout/releases/tag/1.8.4) +Released on 2018-09-25 + +#### Minor changes + +* Cleanup .xcodeproj +* Removed Swiftlint warnings +* Fix an issue with PinLayoutSample app related to IntroRTLView example + +* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#177](https://github.com/layoutBox/PinLayout/pull/177) + +## [1.8.3](https://github.com/layoutBox/PinLayout/releases/tag/1.8.3) +Released on 2018-08-28 + +#### Add methods to layout a view between two other views +Add methods to position a view between two other views, either horizontally or vertically. + +**New Methods:** + +* **`horizontallyBetween(:UIView, and: UIView)`** +Position the view between the two specified views horizontally. The method layout the view's left and right edges. The order of the reference views is irrelevant. +Note that the layout will be applied only if there is horizontal space between the specified views. + +* **`horizontallyBetween(:UIView, and: UIView, aligned: VerticalAlign)`** +Position the view between the two specified views horizontally and aligned it using the specified VerticalAlign. The view will be aligned related to the first specified reference view. Note that the layout will be applied only if there is horizontal space between the specified views. + +* **`verticallyBetween(:UIView, and: UIView)`** +Position the view between the two specified views vertically. The method layout the view's top and bottom edges. The order of the reference views is irrelevant. Note that the layout will be applied only if there is vertical space between the specified views. + +* **`verticallyBetween(:UIView, and: UIView, aligned: HorizontalAlign)`** +Position the view between the two specified views vertically and aligned it using the specified HorizontalAlign. The view will be aligned related to the first specified reference view. Note that the layout will be applied only if there is vertical space between the specified views. + +###### Example: + + + +```swift + view.pin.verticallyBetween(viewA, and: viewB, aligned: .center).marginVertical(10) +``` + +See [Readme for more information](https://github.com/layoutBox/PinLayout#layout_between) + + +* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#172](https://github.com/layoutBox/PinLayout/pull/172) + +## [1.8.2](https://github.com/layoutBox/PinLayout/releases/tag/1.8.2) +Released on 2018-08-25 + +#### Add `pin.readableMargins` and `pin.layoutmargins` +Add properties: + +* **`pin.readableMargins: UIEdgeInset`**: +PinLayout's `UIView.pin.readableMargins` property expose UIKit [`UIView.readableContentGuide`](https://developer.apple.com/documentation/uikit/uiview/1622644-readablecontentguide) as an UIEdgeInsets. This is really useful since UIKit only expose the readableContent area to Auto Layout using UILayoutGuide. + +* **`pin.layoutmargins: UIEdgeInset`** +PinLayout's `UIView.pin.layoutMargins` property expose directly the value of UIKit [`UIView.layoutMargins`](https://developer.apple.com/documentation/uikit/uiview/1622566-layoutmargins). The property exists only to be consistent with the other areas: `pin.safeArea`, `pin.readableMargins` and `pin.layoutmargins`. So its usage is not necessary. + +**Add examples using these properties:** +![pinlayout_example_layout_margins_all](https://user-images.githubusercontent.com/14981341/44617938-51475900-a83a-11e8-96eb-d24f5561dab2.png) + +![pinlayout_example_tableview_readable_content_all](https://user-images.githubusercontent.com/14981341/44617939-51475900-a83a-11e8-9cc6-fe2aac499ce8.png) + +* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#170](https://github.com/layoutBox/PinLayout/pull/170) + +## [1.8.1](https://github.com/layoutBox/PinLayout/releases/tag/1.8.1) +Released on 2018-08-23 + +#### PinLayout Swift 3 support +PinLayout supports Swift 3 and Swift 4 + +* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#169](https://github.com/layoutBox/PinLayout/pull/169) + + +## [1.8.0](https://github.com/layoutBox/PinLayout/releases/tag/1.8.0) +Released on 2018-08-21 + +#### Deprecated method `fitSize()` has been removed +`fitSize()` has been removed after being deprecated for 10 months. `sizeToFit(:FitType)` should now be used instead. See [Adjusting size](https://github.com/layoutBox/PinLayout#adjusting_size). + +Plus: + +* Refactor relative positioning methods source code (above(...), after(...), ...) using a default parameter value for the alignment parameter. +* Fix unit test screen density. +* Update few examples source code. + + +* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#167](https://github.com/layoutBox/PinLayout/pull/167) + + +## [1.7.12](https://github.com/layoutBox/PinLayout/releases/tag/1.7.12) +Released on 2018-08-16 + +#### Add Animations documentation and example +Add documentation that explains how PinLayout can handle view's animations. + +* Show few strategies that can be used to animate views. +* Add an Animation example in the Example app. +* Add an new "Examples" markdown page showing all PinLayout's examples. +* Convert `fileprivate` to `private` declarations + +* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#165](https://github.com/layoutBox/PinLayout/pull/165) + + +## [1.7.11](https://github.com/layoutBox/PinLayout/releases/tag/1.7.11) +Released on 2018-08-05 + +#### Method that position multiple edges now accept an `offset` parameter. +The `offset` parameter that specifies the distance from their superview's corresponding edges in pixels. + +**New methods:** + +* `topLeft(_ offset: CGFloat)` +* `topCenter(_ topOffset: CGFloat)` +* `topRight(_ offset: CGFloat)` + +* `centerLeft(_ leftOffset: CGFloat)` +* `center(_ offset: CGFloat)` +* `centerRight(_ rightOffset offset: CGFloat)` + +* `bottomLeft(_ offset: CGFloat)` +* `bottomCenter(_ bottomOffset: CGFloat)` +* `bottomRight(_ offset: CGFloat)` + +For example, to position a view at the top left corner with a top and left margin of 10 pixels: + +``` + view.pin.topLeft(10) +``` + +#### Other change +Cleanup the interface by using default value parameters. + +* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#163](https://github.com/layoutBox/PinLayout/pull/163) + + +## [1.7.10](https://github.com/layoutBox/PinLayout/releases/tag/1.7.10) +Released on 2018-07-17 + +#### Add `sizeToFit()` method. +The method adjust the view's size based on the result of the method `UIView.sizeToFit()`. Particularly useful for controls/views that have an intrinsic size (label, button, ...). + +* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#158](https://github.com/layoutBox/PinLayout/pull/158) + + +## [1.7.9](https://github.com/layoutBox/PinLayout/releases/tag/1.7.9) +Released on 2018-06-28 + +#### Fix a regression +The recent changes to PinLayout that enable the layout of CALayer has impacted the layout of UIViews. + +* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#152](https://github.com/layoutBox/PinLayout/pull/152) + + +## [1.7.8](https://github.com/layoutBox/PinLayout/releases/tag/1.7.8) +Released on 2018-06-26 + +#### Add support for CALayer layout +PinLayout can now layouts **CALayer**'s. All PinLayout's properties and methods are available, with the following exceptions: + +* `sizeToFit(:FitType)` is not supported. Support for `sizeToFit(:FitType)` can be added to your custom CALayer subclasses, just make those layers conform to the `SizeCalculable` protocol and implement the two required functions. +* `CALayer.pin.safeArea` property is not available. +* `aspectRatio()` with no parameters + +See [CALayer Support documentation](https://github.com/layoutBox/PinLayout#calayer-support) for more information + +* Added by [Antoine Lamy](https://github.com/antoinelamy) in Pull Request [#151](https://github.com/layoutBox/PinLayout/pull/151) + + +## [1.7.7](https://github.com/layoutBox/PinLayout/releases/tag/1.7.6) +Released on 2018-06-19 + +#### Refactoring using generics +Refactoring to avoid having to deal directly with view types, making it easier to extend layouting to other APIs (e.g: CALayer) + +* Added by [Antoine Lamy](https://github.com/antoinelamy) in Pull Request [#148](https://github.com/layoutBox/PinLayout/pull/148) + + +## [1.7.6](https://github.com/layoutBox/PinLayout/releases/tag/1.7.6) +Released on 2018-06-12 + +### PinLayout has moved to the **layoutBox** organization +PinLayout is now part of the same organization as other open source projects related to layout using Swift. + +#### Refactor source code that handle size adjustment. +* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#143](https://github.com/layoutBox/PinLayout/pull/143) + +#### Add an example using `wrapContent()` methods +* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#145](https://github.com/layoutBox/PinLayout/pull/145) + +#### Refactor views frame/bounds access +* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#147](https://github.com/layoutBox/PinLayout/pull/147) + + + +## [1.7.5](https://github.com/layoutBox/PinLayout/releases/tag/1.7.5) +Released on 2018-06-05 + +### Add `wrapContent()` methods that adjust view's width & height to wrap all its subviews + +The following methods are useful to adjust view's width and/or height to wrap all its subviews. These methods also adjust subviews position to create a tight wrap. + +**Methods:** + +* **`wrapContent()`** +**`wrapContent(padding: CGFloat)`** +**`wrapContent(padding: UIEdgeInsets)`** +Adjust the view's width and height to wrap all its subviews. The method also adjusts subviews position to create a tight wrap. It is also possible to specify an optional padding around all subviews. +* **`wrapContent(:WrapType)`** +**`wrapContent(:WrapType, padding: CGFloat)`** +**`wrapContent(:WrapType, padding: UIEdgeInsets)`** +Adjust the view's width AND/OR height to wrap all its subviews. WrapType values are `.horizontally`/`.vertically`/`.all` It is also possible to specify an optional padding around all subviews. + +See [documentation](https://github.com/layoutBox/PinLayout#wrapContent) for more information + +Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#141](https://github.com/layoutBox/PinLayout/pull/141) + + +## [1.7.4](https://github.com/layoutBox/PinLayout/releases/tag/1.7.4) +Released on 2018-05-26 + +### Objective-C support for macOS and tvOS +Add the support of Objective-C to macOS and tvOS. + +Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#138](https://github.com/layoutBox/PinLayout/pull/138) + +## [1.7.3](https://github.com/layoutBox/PinLayout/releases/tag/1.7.3) Released on 2018-04-25 ### Add few missing Objective-C Interface properties and methods @@ -18,10 +409,10 @@ These methods and properties are now accessible from Objective-C: * `Pin.initPinLayout()` * `Pin.layoutDirection()` -Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#135](https://github.com/mirego/PinLayout/pull/135) +Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#135](https://github.com/layoutBox/PinLayout/pull/135) -## [1.7.2](https://github.com/layoutBox/FlexLayout/releases/tag/1.7.2) +## [1.7.2](https://github.com/layoutBox/PinLayout/releases/tag/1.7.2) Released on 2018-04-23 ### Fine tune UIView.pin.safeArea support for iOS 8 and "New Relic" framework @@ -31,11 +422,11 @@ Changes: * Fix issue with "New Relic" framework: Add a Pin.initPinLayout() that can be called to initialize PinLayout before the "New Relic" framework is initialized. "New Relic" is conflicting with other popular frameworks including Mixpanel, ReactiveCocoa, Aspect, ..., and PinLayout. To fix the issue, `Pin.initPinLayout()` must be called BEFORE initializing "New Relic" with `NewRelic.start(withApplicationToken:"APP_TOKEN")`. See here for more information regarding this issue #130 -Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#134](https://github.com/mirego/PinLayout/pull/134) +Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#134](https://github.com/layoutBox/PinLayout/pull/134) -## [1.7.0](https://github.com/layoutBox/FlexLayout/releases/tag/1.7.0) +## [1.7.0](https://github.com/layoutBox/PinLayout/releases/tag/1.7.0) Released on 2018-04-20 ### Add macOS support @@ -54,21 +445,21 @@ PinLayout **support of macOS is not complete at 100%**, see here the particulari All other PinLayout's methods and properties are available on macOS! -Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#131](https://github.com/mirego/PinLayout/pull/131) +Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#131](https://github.com/layoutBox/PinLayout/pull/131) ### PinLayout now use MIT license The PinLayout license has been changed from **BSD 3-clause "New"** to **MIT License**. -## [1.6.0](https://github.com/layoutBox/FlexLayout/releases/tag/1.6.0) +## [1.6.0](https://github.com/layoutBox/PinLayout/releases/tag/1.6.0) Released on 2018-03-22 ### UIView.pin.safeArea PinLayout can handle easily iOS 11 UIView.safeAreaInsets, but it goes further by supporting safeAreaInsets for previous iOS releases (including iOS 7/8/9/10) by adding a property UIView.pin.safeArea. PinLayout also extend the support of UIView.safeAreaInsetsDidChange() callback on iOS 7/8/9/10. -See [UIView.pin.safeArea Documentation](https://github.com/mirego/PinLayout#safeAreaInsets) for more details. +See [UIView.pin.safeArea Documentation](https://github.com/layoutBox/PinLayout#safeAreaInsets) for more details. -Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#125](https://github.com/mirego/PinLayout/pull/125) +Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#125](https://github.com/layoutBox/PinLayout/pull/125) ### Add methods taking UIEdgeInset as parameter @@ -80,9 +471,9 @@ Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#125](https://g * `left(_ insets: UIEdgeInsets)` * `right(_ insets: UIEdgeInsets)` - See [Layout using distances from superview’s edges](https://github.com/mirego/PinLayout#layout-using-distances-from-superviews-edges) for more details. + See [Layout using distances from superview’s edges](https://github.com/layoutBox/PinLayout#layout-using-distances-from-superviews-edges) for more details. - Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#125](https://github.com/mirego/PinLayout/pull/125) + Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#125](https://github.com/layoutBox/PinLayout/pull/125) ### Add margins method with percentage parameter @@ -99,9 +490,9 @@ Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#125](https://g * `margin(_ top: Percent, _ horizontal: Percent, _ bottom: Percent)` * `margin(_ top: Percent, _ left: Percent, _ bottom: Percent, _ right: Percent)` - Added by [vandyshev](https://github.com/vandyshev) in Pull Request [#126](https://github.com/mirego/PinLayout/pull/126) + Added by [vandyshev](https://github.com/vandyshev) in Pull Request [#126](https://github.com/layoutBox/PinLayout/pull/126) -## [1.5.9](https://github.com/layoutBox/FlexLayout/releases/tag/1.5.9) +## [1.5.9](https://github.com/layoutBox/PinLayout/releases/tag/1.5.9) Released on 2018-02-18 #### **`UIView.pin`** versus **`UIView.pinFrame`** @@ -111,27 +502,27 @@ Until now `UIView.pin` was used to layout views, but there's also another proper * `.pinFrame`: Set the position and the size on the **transformed view**. The size and position is applied **after the transform**. -See https://github.com/mirego/PinLayout#uiviews-transforms for more informations. +See https://github.com/layoutBox/PinLayout#uiviews-transforms for more informations. -Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#122](https://github.com/mirego/PinLayout/pull/122) +Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#122](https://github.com/layoutBox/PinLayout/pull/122) -## [1.5.8](https://github.com/layoutBox/FlexLayout/releases/tag/1.5.8) +## [1.5.8](https://github.com/layoutBox/PinLayout/releases/tag/1.5.8) Released on 2018-01-20 * Handle layout relative to a view with a transform and/or a modified anchorPoint. -Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#116](https://github.com/mirego/PinLayout/pull/116) +Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#116](https://github.com/layoutBox/PinLayout/pull/116) -## [1.5.7](https://github.com/layoutBox/FlexLayout/releases/tag/1.5.7) +## [1.5.7](https://github.com/layoutBox/PinLayout/releases/tag/1.5.7) Released on 2018-01-19 * Fix an issue that was affecting UIScrollViews. PinLayout now set only the bounds's size and keep the origin. -Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#115](https://github.com/mirego/PinLayout/pull/115) +Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#115](https://github.com/layoutBox/PinLayout/pull/115) * Handle correctly view's `layer.anchorPoint`. PinLayout now update correctly the view position when the view's layer.anchorPoint has been modified, i.e. when it is not its default value (0.5, 0.5). -Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#114](https://github.com/mirego/PinLayout/pull/114) +Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#114](https://github.com/layoutBox/PinLayout/pull/114) -## [1.5.5](https://github.com/layoutBox/FlexLayout/releases/tag/1.5.5) +## [1.5.5](https://github.com/layoutBox/PinLayout/releases/tag/1.5.5) Released on 2018-01-12 Add methods: @@ -147,23 +538,23 @@ Similar to calling `view.left(value).right(value)`. * **`vertically(_ value: CGFloat)`** / **`vertically(_ percent: Percent)`** The value specifies the ** top and bottom edges** on its superview's corresponding edges in pixels (or in percentage of its superview's height). Similar to calling `view.top(value).bottom(value)`. - * Added by [Olivier Pineau](https://github.com/OlivierPineau) in Pull Request [#111](https://github.com/mirego/PinLayout/pull/111) + * Added by [Olivier Pineau](https://github.com/OlivierPineau) in Pull Request [#111](https://github.com/layoutBox/PinLayout/pull/111) -## [1.5.4](https://github.com/layoutBox/FlexLayout/releases/tag/1.5.4) +## [1.5.4](https://github.com/layoutBox/PinLayout/releases/tag/1.5.4) Released on 2017-12-28 * PinLayout now handle correctly more situations with view with transforms. - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#110](https://github.com/mirego/PinLayout/pull/110) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#110](https://github.com/layoutBox/PinLayout/pull/110) -## [1.5.3](https://github.com/layoutBox/FlexLayout/releases/tag/1.5.3) +## [1.5.3](https://github.com/layoutBox/PinLayout/releases/tag/1.5.3) Released on 2017-12-28 * PinLayout now handle correctly parents (superviews) with transforms. - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#108](https://github.com/mirego/PinLayout/pull/108) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#108](https://github.com/layoutBox/PinLayout/pull/108) -## [1.5.2](https://github.com/layoutBox/FlexLayout/releases/tag/1.5.2) +## [1.5.2](https://github.com/layoutBox/PinLayout/releases/tag/1.5.2) Released on 2017-12-22 * POSSIBLE BREAKING CHANGE: PinLayout now keeps UIView's transform (scale, rotation, ...) @@ -171,22 +562,22 @@ Previously any view's transform was altered after layouting the view with PinLay For people not using transforms, this should be a non-breaking change. If someone is using transforms with PinLayout, this may change the behavior, although I think this will produce the expected results (ie, transforms not being affected/altered by layout). - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#107](https://github.com/mirego/PinLayout/pull/107) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#107](https://github.com/layoutBox/PinLayout/pull/107) -## [1.5.1](https://github.com/mirego/PinLayout/releases/tag/1.5.1) +## [1.5.1](https://github.com/layoutBox/PinLayout/releases/tag/1.5.1) #### Change * Add `layout()` method to support Xcode playgrounds PinLayout layouts views immediately after the line containing `.pin` has been fully executed, thanks to ARC (Automatic Reference Counting) this works perfectly on iOS/tvOS/macOS simulators and devices. But in Xcode Playgrounds, ARC doesn't work as expected, object references are kept much longer. This is a well-documented issue. The impact of this problem is that PinLayout doesn't layout views at the time and in the order required. To handle this situation in playgrounds it is possible to call the `layout()` method to complete the layout. -[See PinLayout in Xcode Playgrounds documentation for more information](https://github.com/mirego/PinLayout#playgrounds) +[See PinLayout in Xcode Playgrounds documentation for more information](https://github.com/layoutBox/PinLayout#playgrounds) -* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#101](https://github.com/mirego/PinLayout/pull/101) +* Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#101](https://github.com/layoutBox/PinLayout/pull/101) -## [1.5.0](https://github.com/mirego/PinLayout/releases/tag/1.5.0) +## [1.5.0](https://github.com/layoutBox/PinLayout/releases/tag/1.5.0) ### New method `sizeToFit(:FitType)` & `fitSize()` is now deprecated #### Changes @@ -216,14 +607,14 @@ PinLayout layouts views immediately after the line containing `.pin` has been fu * **`.heightFlexible`**: Similar to `.height`, except that PinLayout won't constrain the resulting height to match the reference height. The resulting height may be smaller of bigger depending on the view's sizeThatFits(..) method result. - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#103](https://github.com/mirego/PinLayout/pull/103) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#103](https://github.com/layoutBox/PinLayout/pull/103) -## [1.4.3](https://github.com/mirego/PinLayout/releases/tag/1.4.1) +## [1.4.3](https://github.com/layoutBox/PinLayout/releases/tag/1.4.1) Fix Carthage support * Fix an issue that occurs with the latest Carthage version. -## [1.4.2](https://github.com/mirego/PinLayout/releases/tag/1.4.1) +## [1.4.2](https://github.com/layoutBox/PinLayout/releases/tag/1.4.1) #### Change Add method that can pin multiples edges: @@ -233,10 +624,10 @@ Add method that can pin multiples edges: * `vertically()`: Pin the **top and bottom edges** on its superview's corresponding edges. Similar to calling `view.top().bottom()`. - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#93](https://github.com/mirego/PinLayout/pull/93) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#93](https://github.com/layoutBox/PinLayout/pull/93) -## [1.4.1](https://github.com/mirego/PinLayout/releases/tag/1.4.1) +## [1.4.1](https://github.com/layoutBox/PinLayout/releases/tag/1.4.1) #### Change * Add new method `margin(_ directionalInsets: NSDirectionalEdgeInsets)` @@ -244,12 +635,12 @@ Add method that can pin multiples edges: This method is particularly to set all margins using iOS 11 `UIView.directionalLayoutMargins`. Available only on iOS 11 and higher. - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#85](https://github.com/mirego/PinLayout/pull/85) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#85](https://github.com/layoutBox/PinLayout/pull/85) * Update all examples so they support iOS 11 and iPhoneX landscape mode. They use the new UIView.safeAreaInsets property. - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#85](https://github.com/mirego/PinLayout/pull/85) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#85](https://github.com/layoutBox/PinLayout/pull/85) -## [1.4.0](https://github.com/mirego/PinLayout/releases/tag/1.4.0) +## [1.4.0](https://github.com/layoutBox/PinLayout/releases/tag/1.4.0) #### Change * PinLayout now apply correctly margins when hCenter or vCenter have been set * hCenter: When the Horizontal Center is set, PinLayout now applies the left margin. @@ -257,10 +648,10 @@ Add method that can pin multiples edges: **BREAKING CHANGE**: This may be a breaking change if you are using hCenter(..), vCenter(...), center(...), centerRight(...), centerLeft(...), or any other method using the center position while also using a margin. - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#91](https://github.com/mirego/PinLayout/pull/91) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#91](https://github.com/layoutBox/PinLayout/pull/91) -## [1.3.2](https://github.com/mirego/PinLayout/releases/tag/1.3.2) +## [1.3.2](https://github.com/layoutBox/PinLayout/releases/tag/1.3.2) #### Change * Add **aspectRatio** methods: * **`aspectRatio(_ ratio: CGFloat)`**: @@ -285,16 +676,16 @@ This method is particularly useful to set all margins using iOS 11 UIView.safeAr the UIImageView's image dimension. For other types of views, this method as no impact. - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#84](https://github.com/mirego/PinLayout/pull/84) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#84](https://github.com/layoutBox/PinLayout/pull/84) -## [1.3.1](https://github.com/mirego/PinLayout/releases/tag/1.3.1) +## [1.3.1](https://github.com/layoutBox/PinLayout/releases/tag/1.3.1) #### Change * Add new margin method `margin(_ insets: UIEdgeInsets)` Set all margins using an UIEdgeInsets. This method is particularly useful to set all margins using iOS 11 UIView.safeAreaInsets - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#82](https://github.com/mirego/PinLayout/pull/82) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#82](https://github.com/layoutBox/PinLayout/pull/82) -## [1.3.0](https://github.com/mirego/PinLayout/releases/tag/1.3.0) +## [1.3.0](https://github.com/layoutBox/PinLayout/releases/tag/1.3.0) Released on 2017-08-18. #### Change @@ -306,7 +697,7 @@ The value specifies the distance horizontally of the view's center **related to Previously `hCenter(0)` wasn't equal to `hCenter()`, same thing for `vCenter(0)`. But this was an exception: `top(0)` == `top()`,` left(0)` == `left()`, `right(0)` == `right()`. Now thay all have the same logic. -## [1.2.4](https://github.com/mirego/PinLayout/releases/tag/1.2.4) +## [1.2.4](https://github.com/layoutBox/PinLayout/releases/tag/1.2.4) #### Change * Add methods to pin hCenter and vCenter to any other view's edges (including the new hCenter and vCenter edges) * **New methods**: @@ -317,9 +708,9 @@ Previously `hCenter(0)` wasn't equal to `hCenter()`, same thing for `vCenter(0)` * **New UIView's edges**: * **`UIView.edge.hCenter`** * **`UIView.edge.vCenter`** - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#80](https://github.com/mirego/PinLayout/pull/80) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#80](https://github.com/layoutBox/PinLayout/pull/80) -## [1.2.3](https://github.com/mirego/PinLayout/releases/tag/1.2.3) +## [1.2.3](https://github.com/layoutBox/PinLayout/releases/tag/1.2.3) #### Change * Warnings now display more context information * The class name of the view being layouted. @@ -338,18 +729,18 @@ Previously `hCenter(0)` wasn't equal to `hCenter()`, same thing for `vCenter(0)` * 👉 PinLayout Warning: topLeft(to: .topLeft, of: (UIView, Frame: (10.0, 10.0, 10.0, 10.0))) won't be applied, the reference view (UIView, Frame: (10.0, 10.0, 10.0, 10.0)) must be added as a sub-view before being used as a reference. (Layouted view info: Type: UIView, Frame: (140.0, 100.0, 100.0, 60.0), Superviews: UIView -> UIView, Tag: 0) - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#75](https://github.com/mirego/PinLayout/pull/75) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#75](https://github.com/layoutBox/PinLayout/pull/75) -## [1.2.2](https://github.com/mirego/PinLayout/releases/tag/1.2.2) +## [1.2.2](https://github.com/layoutBox/PinLayout/releases/tag/1.2.2) #### Change * Added a new method `fitSize()` that will replace the `sizeThatFit()` method. Its prior name was creating confusion with the already existingUIView.sizeToFit()` method. * `sizeThatFit()` method has been marked as deprecated. -## [1.2.1](https://github.com/mirego/PinLayout/releases/tag/1.2.1) +## [1.2.1](https://github.com/layoutBox/PinLayout/releases/tag/1.2.1) #### Change * Add Swift 4.0 support -## [1.2.0](https://github.com/mirego/PinLayout/releases/tag/1.2.0) +## [1.2.0](https://github.com/layoutBox/PinLayout/releases/tag/1.2.0) Released on 2017-08-18. #### Change @@ -384,19 +775,19 @@ Additions: * marginEnd(_ value: CGFloat) * HorizontalAlign.start * HorizontalAlign.end - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#56](https://github.com/mirego/PinLayout/pull/56) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#56](https://github.com/layoutBox/PinLayout/pull/56) -## [1.1.5](https://github.com/mirego/PinLayout/releases/tag/1.1.5) +## [1.1.5](https://github.com/layoutBox/PinLayout/releases/tag/1.1.5) Released on 2017-07-14. #### Change * Fix missing UIKit import. The problem was occuring while using Swift Package Manager. - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#67](https://github.com/mirego/PinLayout/pull/67) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#67](https://github.com/layoutBox/PinLayout/pull/67) -## [1.1.4](https://github.com/mirego/PinLayout/releases/tag/1.1.4) +## [1.1.4](https://github.com/layoutBox/PinLayout/releases/tag/1.1.4) Released on 2017-07-09. #### Change @@ -407,17 +798,17 @@ Released on 2017-07-09. * maxHeight * justify(:HorizontalAlign) * align(:VerticalAlign) - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#53](https://github.com/mirego/PinLayout/pull/53) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#53](https://github.com/layoutBox/PinLayout/pull/53) -## [1.1.1](https://github.com/mirego/PinLayout/releases/tag/1.1.1) +## [1.1.1](https://github.com/layoutBox/PinLayout/releases/tag/1.1.1) Released on 2017-06-27. #### Change * Support **Xcode 9 Beta 2** - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#52](https://github.com/mirego/PinLayout/pull/52) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#52](https://github.com/layoutBox/PinLayout/pull/52) * Add a Form example - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#51](https://github.com/mirego/PinLayout/pull/51) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#51](https://github.com/layoutBox/PinLayout/pull/51) * This example demonstrates: * Usage of filter method when using PinLayout's relative methods (above, below, left, right) * Adjusting a container's height to match all its children. @@ -425,30 +816,30 @@ Released on 2017-06-27. -## [1.1.0](https://github.com/mirego/PinLayout/releases/tag/1.1.0) +## [1.1.0](https://github.com/layoutBox/PinLayout/releases/tag/1.1.0) Released on 2017-06-18. #### Change * Update relative methods signatures when specifying multiple relative views. Update the minor version due to a small breaking change with methods above(of…), below(of…), left(of…) and right(of…). They now takes either a single UIView or an Array of UIViews. - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#48](https://github.com/mirego/PinLayout/pull/48) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#48](https://github.com/layoutBox/PinLayout/pull/48) -## [1.0.15](https://github.com/mirego/PinLayout/releases/tag/1.0.15) +## [1.0.15](https://github.com/layoutBox/PinLayout/releases/tag/1.0.15) Released on 2017-06-12. #### Change * Add **tvOS** support & set iOS target to 8.0 (instead of 10.2) - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#46](https://github.com/mirego/PinLayout/pull/46) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#46](https://github.com/layoutBox/PinLayout/pull/46) -## [1.0.14](https://github.com/mirego/PinLayout/releases/tag/1.0.14) +## [1.0.14](https://github.com/layoutBox/PinLayout/releases/tag/1.0.14) Released on 2017-06-12. #### Change * Implementation of **relative positioning using multiple relative views** - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#43](https://github.com/mirego/PinLayout/pull/43) + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#43](https://github.com/layoutBox/PinLayout/pull/43) * The following methods can now receives one or many relative views. Useful to position a view relative to many UIViews. * `above(of relativeViews: UIView...) ` * `above(of relativeViews: UIView..., aligned: HorizontalAlignment) ` @@ -459,13 +850,13 @@ Released on 2017-06-12. * `right(of relativeViews: UIView...) ` * `right(of relativeViews: UIView..., aligned: VerticalAlignment)` -## [1.0.11](https://github.com/mirego/PinLayout/releases/tag/1.0.11) +## [1.0.11](https://github.com/layoutBox/PinLayout/releases/tag/1.0.11) Released on 2017-06-08. #### Change * Add **Swift Package Manager** support - * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#38](https://github.com/mirego/PinLayout/pull/38 + * Added by [Luc Dion](https://github.com/lucdion) in Pull Request [#38](https://github.com/layoutBox/PinLayout/pull/38 * **`size(…)` methods** now tries to apply the width and the height individually Previously the size specified was applied only if both the width and height wasn’t specified. Now PinLayout will apply them individually, so if the width has been specified yet, the size’s width will be applied, else a warning will be displayed that indicate that the width won’t be applied. Same thing for the height. * Doesn’t display a warning anymore if the new specified width or height value is equal to the currently set value. This is coherent with other methods (top, left, hCenter, ….) @@ -476,13 +867,13 @@ Previously the size specified was applied only if both the width and height wasn ### Fixes - Fix an issue with pin.vCenter() and pin.hCenter() - Fixed by [Luc Dion](https://github.com/lucdion) in Pull Request - [#36](https://github.com/mirego/PinLayout/pull/36). + [#36](https://github.com/layoutBox/PinLayout/pull/36). -## [1.0.7](https://github.com/mirego/PinLayout/releases/tag/1.0.7) +## [1.0.7](https://github.com/layoutBox/PinLayout/releases/tag/1.0.7) Released on 2017-06-06. ### Fixes - Fix an issue with pin.vCenter() and pin.hCenter() - Fixed by [Luc Dion](https://github.com/lucdion) in Pull Request - [#36](https://github.com/mirego/PinLayout/pull/36). + [#36](https://github.com/layoutBox/PinLayout/pull/36). diff --git a/Example/.DS_Store b/Example/.DS_Store new file mode 100644 index 00000000..b066f015 Binary files /dev/null and b/Example/.DS_Store differ diff --git a/Example/.swiftlint.yml b/Example/.swiftlint.yml index 0df8a810..adee2693 100644 --- a/Example/.swiftlint.yml +++ b/Example/.swiftlint.yml @@ -24,3 +24,4 @@ disabled_rules: # rule identifiers to exclude from running excluded: # paths to ignore during linting. overridden by `included`. - Pods + - PinLayoutExampleMacOS \ No newline at end of file diff --git a/Example/PinLayoutSample.xcodeproj/project.pbxproj b/Example/PinLayoutSample.xcodeproj/project.pbxproj index 7fe03372..1db911c2 100644 --- a/Example/PinLayoutSample.xcodeproj/project.pbxproj +++ b/Example/PinLayoutSample.xcodeproj/project.pbxproj @@ -3,19 +3,17 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 241637741F8E4BC200EE703A /* IntroObjectiveCViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 241637711F8E4BC200EE703A /* IntroObjectiveCViewController.m */; }; 241637771F8E4F9100EE703A /* IntroObjectiveCView.m in Sources */ = {isa = PBXBuildFile; fileRef = 241637761F8E4F9100EE703A /* IntroObjectiveCView.m */; }; - 2439CC281E6658C3003326FB /* PinLayout.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 2439CC241E665858003326FB /* PinLayout.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 2439CC2B1E6658CC003326FB /* PinLayout.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2439CC241E665858003326FB /* PinLayout.framework */; }; 2439CC351E665BF6003326FB /* MenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2439CC331E665BF6003326FB /* MenuView.swift */; }; 2439CC361E665BF6003326FB /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2439CC341E665BF6003326FB /* MenuViewController.swift */; }; 2439CC4B1E665C6B003326FB /* BasicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2439CC381E665C6B003326FB /* BasicView.swift */; }; - 2439CC521E665C6B003326FB /* MultiRelativeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2439CC3F1E665C6B003326FB /* MultiRelativeView.swift */; }; - 2439CC531E665C6B003326FB /* MultiRelativeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2439CC401E665C6B003326FB /* MultiRelativeViewController.swift */; }; + 2439CC521E665C6B003326FB /* BetweenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2439CC3F1E665C6B003326FB /* BetweenView.swift */; }; + 2439CC531E665C6B003326FB /* BetweenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2439CC401E665C6B003326FB /* BetweenViewController.swift */; }; 2439CC541E665C6B003326FB /* RelativeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2439CC411E665C6B003326FB /* RelativeView.swift */; }; 2439CC551E665C6B003326FB /* RelativeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2439CC421E665C6B003326FB /* RelativeViewController.swift */; }; 247157941F87BD680003424F /* UIEdgeInsets+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 247157931F87BD680003424F /* UIEdgeInsets+PinLayout.swift */; }; @@ -43,12 +41,28 @@ 24F246141FA8D57100B6332E /* UIImageView+Download.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F246131FA8D57100B6332E /* UIImageView+Download.swift */; }; 24F75B5B1EE5644E008DB567 /* IntroView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F75B591EE5644E008DB567 /* IntroView.swift */; }; 24F75B5C1EE5644E008DB567 /* IntroViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F75B5A1EE5644E008DB567 /* IntroViewController.swift */; }; - DE6C3D736B571B80E207DF6A /* Pods_PinLayoutSample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AAD69688AA2A3F0994F3074E /* Pods_PinLayoutSample.framework */; }; + B30A278A4301304C7DD08E37 /* Pods_PinLayoutSample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 641BBAE7F09807BD466749F3 /* Pods_PinLayoutSample.framework */; }; + C892FA1924A5821E0086A75E /* AutoSizingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C892FA1824A5821E0086A75E /* AutoSizingViewController.swift */; }; + C892FA1B24A5822B0086A75E /* AutoSizingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C892FA1A24A5822B0086A75E /* AutoSizingView.swift */; }; + C892FA1D24A584010086A75E /* ContentService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C892FA1C24A584010086A75E /* ContentService.swift */; }; + C892FA1F24A597FA0086A75E /* AutoSizingContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C892FA1E24A597FA0086A75E /* AutoSizingContainerView.swift */; }; + C892FA2124A598170086A75E /* ProxyWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C892FA2024A598170086A75E /* ProxyWrapper.swift */; }; + DF390898211900320049FD56 /* AnimationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF390897211900320049FD56 /* AnimationsView.swift */; }; + DF39089A211900480049FD56 /* AnimationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF390899211900480049FD56 /* AnimationsViewController.swift */; }; DF4C1AA4205AEDFC00DED50B /* SafeAreaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF4C1AA0205AEDFC00DED50B /* SafeAreaView.swift */; }; DF4C1AA5205AEDFC00DED50B /* SafeAreaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF4C1AA1205AEDFC00DED50B /* SafeAreaViewController.swift */; }; DF4C1AAA205AF10900DED50B /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF4C1AA7205AF10900DED50B /* RoundedButton.swift */; }; - DF4C1AAE205AF78A00DED50B /* SafeAreaCornersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF4C1AAC205AF78A00DED50B /* SafeAreaCornersViewController.swift */; }; - DF4C1AAF205AF78A00DED50B /* SafeAreaCornersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF4C1AAD205AF78A00DED50B /* SafeAreaCornersView.swift */; }; + DF4C1AAE205AF78A00DED50B /* SafeAreaAndMarginsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF4C1AAC205AF78A00DED50B /* SafeAreaAndMarginsViewController.swift */; }; + DF4C1AAF205AF78A00DED50B /* SafeAreaAndMarginsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF4C1AAD205AF78A00DED50B /* SafeAreaAndMarginsView.swift */; }; + DFBCAEFB213012930025F7BF /* MethodReadableInsetsGroupHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFBCAEF9213012930025F7BF /* MethodReadableInsetsGroupHeader.swift */; }; + DFBCAEFC213012930025F7BF /* MethodReadableInsetsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFBCAEFA213012930025F7BF /* MethodReadableInsetsCell.swift */; }; + DFBCAEFE2130146C0025F7BF /* AreaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFBCAEFD2130146C0025F7BF /* AreaView.swift */; }; + DFD27841211B1A710056BD93 /* UINavigationController+Orientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD27840211B1A700056BD93 /* UINavigationController+Orientation.swift */; }; + DFD27848211B1D090056BD93 /* UITabBarController+Orientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD27847211B1D090056BD93 /* UITabBarController+Orientation.swift */; }; + DFD31BA0212EE4F200566CA4 /* TableViewReadableContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD31B9E212EE4F100566CA4 /* TableViewReadableContentView.swift */; }; + DFD31BA1212EE4F200566CA4 /* TableViewReadableContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD31B9F212EE4F200566CA4 /* TableViewReadableContentViewController.swift */; }; + DFEAF74A20C9648A00E33147 /* WrapContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFEAF74920C9648A00E33147 /* WrapContentView.swift */; }; + DFEAF74C20C9649F00E33147 /* WrapContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFEAF74B20C9649F00E33147 /* WrapContentViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -66,13 +80,6 @@ remoteGlobalIDString = 249EFE831E64FB4C00165E39; remoteInfo = PinLayoutTests; }; - 2439CC291E6658C3003326FB /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 2439CC1E1E665858003326FB /* PinLayout.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 249EFE791E64FB4C00165E39; - remoteInfo = PinLayout; - }; 24DA374E1EF7F90700D1AB2F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 2439CC1E1E665858003326FB /* PinLayout.xcodeproj */; @@ -80,31 +87,28 @@ remoteGlobalIDString = 244DF2F81EF46C500090508B; remoteInfo = PinLayoutTVOS; }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 2460ACCE1E64FD9D000BCAC5 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 2439CC281E6658C3003326FB /* PinLayout.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; + DFEAF71B20C840F300E33147 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 2439CC1E1E665858003326FB /* PinLayout.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = DF1A5D2C2084CF9700725EF5; + remoteInfo = "PinLayout-macOS"; }; - 2468130D1F8D013600462E53 /* Embed App Extensions */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 13; - files = ( - ); - name = "Embed App Extensions"; - runOnlyForDeploymentPostprocessing = 0; + DFEAF71D20C840F300E33147 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 2439CC1E1E665858003326FB /* PinLayout.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = DF1A5D1D2084C94700725EF5; + remoteInfo = "PinLayoutTests-macOS"; + }; + DFEAF71F20C840F300E33147 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 2439CC1E1E665858003326FB /* PinLayout.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = DFF6F9C22084DCD3004F5AED; + remoteInfo = "PinLayoutTests-tvOS"; }; -/* End PBXCopyFilesBuildPhase section */ +/* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 241637701F8E4BC200EE703A /* IntroObjectiveCViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntroObjectiveCViewController.h; sourceTree = ""; }; @@ -115,8 +119,8 @@ 2439CC331E665BF6003326FB /* MenuView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuView.swift; sourceTree = ""; }; 2439CC341E665BF6003326FB /* MenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuViewController.swift; sourceTree = ""; }; 2439CC381E665C6B003326FB /* BasicView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicView.swift; sourceTree = ""; }; - 2439CC3F1E665C6B003326FB /* MultiRelativeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultiRelativeView.swift; sourceTree = ""; }; - 2439CC401E665C6B003326FB /* MultiRelativeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultiRelativeViewController.swift; sourceTree = ""; }; + 2439CC3F1E665C6B003326FB /* BetweenView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BetweenView.swift; sourceTree = ""; }; + 2439CC401E665C6B003326FB /* BetweenViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BetweenViewController.swift; sourceTree = ""; }; 2439CC411E665C6B003326FB /* RelativeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelativeView.swift; sourceTree = ""; }; 2439CC421E665C6B003326FB /* RelativeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelativeViewController.swift; sourceTree = ""; }; 246812FC1F8D013500462E53 /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; }; @@ -148,14 +152,30 @@ 24F246131FA8D57100B6332E /* UIImageView+Download.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+Download.swift"; sourceTree = ""; }; 24F75B591EE5644E008DB567 /* IntroView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntroView.swift; sourceTree = ""; }; 24F75B5A1EE5644E008DB567 /* IntroViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntroViewController.swift; sourceTree = ""; }; + 641BBAE7F09807BD466749F3 /* Pods_PinLayoutSample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PinLayoutSample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A35A00E6536E49A548E763E6 /* Pods-PinLayoutSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutSample.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-PinLayoutSample/Pods-PinLayoutSample.debug.xcconfig"; sourceTree = ""; }; - AAD69688AA2A3F0994F3074E /* Pods_PinLayoutSample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PinLayoutSample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C589624E868FCB20F7C10918 /* Pods-PinLayoutSample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutSample.release.xcconfig"; path = "../Pods/Target Support Files/Pods-PinLayoutSample/Pods-PinLayoutSample.release.xcconfig"; sourceTree = ""; }; + C892FA1824A5821E0086A75E /* AutoSizingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoSizingViewController.swift; sourceTree = ""; }; + C892FA1A24A5822B0086A75E /* AutoSizingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoSizingView.swift; sourceTree = ""; }; + C892FA1C24A584010086A75E /* ContentService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentService.swift; sourceTree = ""; }; + C892FA1E24A597FA0086A75E /* AutoSizingContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoSizingContainerView.swift; sourceTree = ""; }; + C892FA2024A598170086A75E /* ProxyWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyWrapper.swift; sourceTree = ""; }; + DF390897211900320049FD56 /* AnimationsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AnimationsView.swift; path = PinLayoutSample/UI/Examples/Animations/AnimationsView.swift; sourceTree = SOURCE_ROOT; }; + DF390899211900480049FD56 /* AnimationsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimationsViewController.swift; sourceTree = ""; }; DF4C1AA0205AEDFC00DED50B /* SafeAreaView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SafeAreaView.swift; sourceTree = ""; }; DF4C1AA1205AEDFC00DED50B /* SafeAreaViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SafeAreaViewController.swift; sourceTree = ""; }; DF4C1AA7205AF10900DED50B /* RoundedButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; - DF4C1AAC205AF78A00DED50B /* SafeAreaCornersViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SafeAreaCornersViewController.swift; sourceTree = ""; }; - DF4C1AAD205AF78A00DED50B /* SafeAreaCornersView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SafeAreaCornersView.swift; sourceTree = ""; }; + DF4C1AAC205AF78A00DED50B /* SafeAreaAndMarginsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SafeAreaAndMarginsViewController.swift; sourceTree = ""; }; + DF4C1AAD205AF78A00DED50B /* SafeAreaAndMarginsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SafeAreaAndMarginsView.swift; sourceTree = ""; }; + DFBCAEF9213012930025F7BF /* MethodReadableInsetsGroupHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MethodReadableInsetsGroupHeader.swift; sourceTree = ""; }; + DFBCAEFA213012930025F7BF /* MethodReadableInsetsCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MethodReadableInsetsCell.swift; sourceTree = ""; }; + DFBCAEFD2130146C0025F7BF /* AreaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AreaView.swift; sourceTree = ""; }; + DFD27840211B1A700056BD93 /* UINavigationController+Orientation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Orientation.swift"; sourceTree = ""; }; + DFD27847211B1D090056BD93 /* UITabBarController+Orientation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITabBarController+Orientation.swift"; sourceTree = ""; }; + DFD31B9E212EE4F100566CA4 /* TableViewReadableContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewReadableContentView.swift; sourceTree = ""; }; + DFD31B9F212EE4F200566CA4 /* TableViewReadableContentViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewReadableContentViewController.swift; sourceTree = ""; }; + DFEAF74920C9648A00E33147 /* WrapContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WrapContentView.swift; sourceTree = ""; }; + DFEAF74B20C9649F00E33147 /* WrapContentViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WrapContentViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -163,8 +183,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 2439CC2B1E6658CC003326FB /* PinLayout.framework in Frameworks */, - DE6C3D736B571B80E207DF6A /* Pods_PinLayoutSample.framework in Frameworks */, + B30A278A4301304C7DD08E37 /* Pods_PinLayoutSample.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -174,8 +193,8 @@ 160FB83905049FCEDD18DC8A /* Frameworks */ = { isa = PBXGroup; children = ( - AAD69688AA2A3F0994F3074E /* Pods_PinLayoutSample.framework */, 246812FC1F8D013500462E53 /* NotificationCenter.framework */, + 641BBAE7F09807BD466749F3 /* Pods_PinLayoutSample.framework */, ); name = Frameworks; sourceTree = ""; @@ -195,8 +214,11 @@ isa = PBXGroup; children = ( 2439CC241E665858003326FB /* PinLayout.framework */, - 24DA374F1EF7F90700D1AB2F /* PinLayoutTVOS.framework */, - 2439CC261E665858003326FB /* PinLayoutTests.xctest */, + DFEAF71C20C840F300E33147 /* PinLayout.framework */, + 24DA374F1EF7F90700D1AB2F /* PinLayout.framework */, + 2439CC261E665858003326FB /* PinLayoutTests-iOS.xctest */, + DFEAF71E20C840F300E33147 /* PinLayoutTests-macOS.xctest */, + DFEAF72020C840F300E33147 /* PinLayoutTests-tvOS.xctest */, ); name = Products; sourceTree = ""; @@ -216,15 +238,19 @@ children = ( 24F75B581EE5642C008DB567 /* Intro */, 24CB99991F290591004EA7FB /* AdjustToContainer */, - DF66F9D61E8493D400ADB8D5 /* AutoAdjustingSize */, 24A9C1EF1EF0542500F2CF64 /* TableViewExample */, 24F2460C1FA8D4D100B6332E /* CollectionViewExample */, + DF3908912118FFF20049FD56 /* Animations */, + DF66F9D61E8493D400ADB8D5 /* AutoAdjustingSize */, DF4C1A9F205AEDFC00DED50B /* SafeArea */, + 2439CC631E66606D003326FB /* RelativeView */, + 2439CC5F1E665F66003326FB /* BetweenView */, 2497CFEA1EF40B8100DFD13B /* Form */, + DFEAF71520C840F300E33147 /* WrapContent */, + DFD31B9D212EE4D600566CA4 /* TableViewReadableContent */, 24D18D181F3DECD6008129EF /* IntroRTL */, 2416376F1F8E4BC200EE703A /* IntroObjectiveC */, - 2439CC5F1E665F66003326FB /* MultiRelativeView */, - 2439CC631E66606D003326FB /* RelativeView */, + C892FA1124A582050086A75E /* AutoSizing */, 24CD1E8D1F8E4B0A00C3A54D /* PinLayoutSample-Bridging-Header.h */, ); path = Examples; @@ -238,17 +264,21 @@ 249326881EEEEE3D00BCB814 /* Stylesheet.swift */, 247157931F87BD680003424F /* UIEdgeInsets+PinLayout.swift */, 24F246131FA8D57100B6332E /* UIImageView+Download.swift */, + DFD27840211B1A700056BD93 /* UINavigationController+Orientation.swift */, + DFD27847211B1D090056BD93 /* UITabBarController+Orientation.swift */, + C892FA1C24A584010086A75E /* ContentService.swift */, + C892FA2024A598170086A75E /* ProxyWrapper.swift */, ); path = Common; sourceTree = ""; }; - 2439CC5F1E665F66003326FB /* MultiRelativeView */ = { + 2439CC5F1E665F66003326FB /* BetweenView */ = { isa = PBXGroup; children = ( - 2439CC3F1E665C6B003326FB /* MultiRelativeView.swift */, - 2439CC401E665C6B003326FB /* MultiRelativeViewController.swift */, + 2439CC3F1E665C6B003326FB /* BetweenView.swift */, + 2439CC401E665C6B003326FB /* BetweenViewController.swift */, ); - path = MultiRelativeView; + path = BetweenView; sourceTree = ""; }; 2439CC631E66606D003326FB /* RelativeView */ = { @@ -382,14 +412,33 @@ path = Intro; sourceTree = ""; }; + C892FA1124A582050086A75E /* AutoSizing */ = { + isa = PBXGroup; + children = ( + C892FA1824A5821E0086A75E /* AutoSizingViewController.swift */, + C892FA1A24A5822B0086A75E /* AutoSizingView.swift */, + C892FA1E24A597FA0086A75E /* AutoSizingContainerView.swift */, + ); + path = AutoSizing; + sourceTree = ""; + }; + DF3908912118FFF20049FD56 /* Animations */ = { + isa = PBXGroup; + children = ( + DF390897211900320049FD56 /* AnimationsView.swift */, + DF390899211900480049FD56 /* AnimationsViewController.swift */, + ); + path = Animations; + sourceTree = ""; + }; DF4C1A9F205AEDFC00DED50B /* SafeArea */ = { isa = PBXGroup; children = ( DF4C1AAB205AF10C00DED50B /* Subviews */, DF4C1AA0205AEDFC00DED50B /* SafeAreaView.swift */, DF4C1AA1205AEDFC00DED50B /* SafeAreaViewController.swift */, - DF4C1AAD205AF78A00DED50B /* SafeAreaCornersView.swift */, - DF4C1AAC205AF78A00DED50B /* SafeAreaCornersViewController.swift */, + DF4C1AAD205AF78A00DED50B /* SafeAreaAndMarginsView.swift */, + DF4C1AAC205AF78A00DED50B /* SafeAreaAndMarginsViewController.swift */, ); path = SafeArea; sourceTree = ""; @@ -397,6 +446,7 @@ DF4C1AAB205AF10C00DED50B /* Subviews */ = { isa = PBXGroup; children = ( + DFBCAEFD2130146C0025F7BF /* AreaView.swift */, DF4C1AA7205AF10900DED50B /* RoundedButton.swift */, ); path = Subviews; @@ -411,6 +461,34 @@ path = AutoAdjustingSize; sourceTree = ""; }; + DFBCAEF82130127F0025F7BF /* Cells */ = { + isa = PBXGroup; + children = ( + DFBCAEF9213012930025F7BF /* MethodReadableInsetsGroupHeader.swift */, + DFBCAEFA213012930025F7BF /* MethodReadableInsetsCell.swift */, + ); + path = Cells; + sourceTree = ""; + }; + DFD31B9D212EE4D600566CA4 /* TableViewReadableContent */ = { + isa = PBXGroup; + children = ( + DFBCAEF82130127F0025F7BF /* Cells */, + DFD31B9E212EE4F100566CA4 /* TableViewReadableContentView.swift */, + DFD31B9F212EE4F200566CA4 /* TableViewReadableContentViewController.swift */, + ); + path = TableViewReadableContent; + sourceTree = ""; + }; + DFEAF71520C840F300E33147 /* WrapContent */ = { + isa = PBXGroup; + children = ( + DFEAF74920C9648A00E33147 /* WrapContentView.swift */, + DFEAF74B20C9649F00E33147 /* WrapContentViewController.swift */, + ); + path = WrapContent; + sourceTree = ""; + }; F143180314A617EFD07C5709 /* Pods */ = { isa = PBXGroup; children = ( @@ -431,15 +509,12 @@ 249EFE3B1E64FAFE00165E39 /* Sources */, 249EFE3C1E64FAFE00165E39 /* Frameworks */, 249EFE3D1E64FAFE00165E39 /* Resources */, - 2460ACCE1E64FD9D000BCAC5 /* Embed Frameworks */, - 5D3C4568AFC08267110D9971 /* [CP] Embed Pods Frameworks */, 24E6547E1E68F88D00A72A8B /* Run Swiftlint */, - 2468130D1F8D013600462E53 /* Embed App Extensions */, + A57F7C48B21998F5C851B138 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( - 2439CC2A1E6658C3003326FB /* PBXTargetDependency */, ); name = PinLayoutSample; productName = PinLayoutSample; @@ -453,18 +528,19 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0900; - LastUpgradeCheck = 0900; - ORGANIZATIONNAME = Mirego; + LastUpgradeCheck = 1420; + ORGANIZATIONNAME = layoutbox; TargetAttributes = { 249EFE3E1E64FAFE00165E39 = { CreatedOnToolsVersion = 8.2.1; + LastSwiftMigration = 1030; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 249EFE3A1E64FAFE00165E39 /* Build configuration list for PBXProject "PinLayoutSample" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -494,20 +570,41 @@ remoteRef = 2439CC231E665858003326FB /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 2439CC261E665858003326FB /* PinLayoutTests.xctest */ = { + 2439CC261E665858003326FB /* PinLayoutTests-iOS.xctest */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; - path = PinLayoutTests.xctest; + path = "PinLayoutTests-iOS.xctest"; remoteRef = 2439CC251E665858003326FB /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 24DA374F1EF7F90700D1AB2F /* PinLayoutTVOS.framework */ = { + 24DA374F1EF7F90700D1AB2F /* PinLayout.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; - path = PinLayoutTVOS.framework; + path = PinLayout.framework; remoteRef = 24DA374E1EF7F90700D1AB2F /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + DFEAF71C20C840F300E33147 /* PinLayout.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = PinLayout.framework; + remoteRef = DFEAF71B20C840F300E33147 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + DFEAF71E20C840F300E33147 /* PinLayoutTests-macOS.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "PinLayoutTests-macOS.xctest"; + remoteRef = DFEAF71D20C840F300E33147 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + DFEAF72020C840F300E33147 /* PinLayoutTests-tvOS.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "PinLayoutTests-tvOS.xctest"; + remoteRef = DFEAF71F20C840F300E33147 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ @@ -525,6 +622,7 @@ /* Begin PBXShellScriptBuildPhase section */ 24E6547E1E68F88D00A72A8B /* Run Swiftlint */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -535,26 +633,24 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = ../Pods/SwiftLint/swiftlint; + shellScript = "../Pods/SwiftLint/swiftlint\n"; }; - 5D3C4568AFC08267110D9971 /* [CP] Embed Pods Frameworks */ = { + A57F7C48B21998F5C851B138 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${SRCROOT}/../Pods/Target Support Files/Pods-PinLayoutSample/Pods-PinLayoutSample-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-PinLayoutSample/Pods-PinLayoutSample-frameworks.sh", "${BUILT_PRODUCTS_DIR}/PinLayout/PinLayout.framework", - "${PODS_ROOT}/Reveal-SDK/RevealServer-10/iOS/RevealServer.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PinLayout.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RevealServer.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-PinLayoutSample/Pods-PinLayoutSample-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PinLayoutSample/Pods-PinLayoutSample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; DDE1EE639E8C959FEBC41FDC /* [CP] Check Pods Manifest.lock */ = { @@ -582,40 +678,56 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - DF4C1AAF205AF78A00DED50B /* SafeAreaCornersView.swift in Sources */, + DF4C1AAF205AF78A00DED50B /* SafeAreaAndMarginsView.swift in Sources */, 24F246141FA8D57100B6332E /* UIImageView+Download.swift in Sources */, + DFBCAEFC213012930025F7BF /* MethodReadableInsetsCell.swift in Sources */, 24DA374B1EF7F90600D1AB2F /* BaseFormView.swift in Sources */, 24F75B5C1EE5644E008DB567 /* IntroViewController.swift in Sources */, 241637741F8E4BC200EE703A /* IntroObjectiveCViewController.m in Sources */, + DF390898211900320049FD56 /* AnimationsView.swift in Sources */, + C892FA2124A598170086A75E /* ProxyWrapper.swift in Sources */, 2439CC541E665C6B003326FB /* RelativeView.swift in Sources */, 2439CC551E665C6B003326FB /* RelativeViewController.swift in Sources */, 247157941F87BD680003424F /* UIEdgeInsets+PinLayout.swift in Sources */, 24D18D1E1F3DED0D008129EF /* IntroRTLViewController.swift in Sources */, + C892FA1F24A597FA0086A75E /* AutoSizingContainerView.swift in Sources */, + DFEAF74C20C9649F00E33147 /* WrapContentViewController.swift in Sources */, 24F246121FA8D4D100B6332E /* HouseCell.swift in Sources */, - DF4C1AAE205AF78A00DED50B /* SafeAreaCornersViewController.swift in Sources */, + DF4C1AAE205AF78A00DED50B /* SafeAreaAndMarginsViewController.swift in Sources */, 24A9C2031EF16A3E00F2CF64 /* AutoAdjustingSizeView.swift in Sources */, + DFD27848211B1D090056BD93 /* UITabBarController+Orientation.swift in Sources */, + DFBCAEFE2130146C0025F7BF /* AreaView.swift in Sources */, 24D18D1D1F3DED0D008129EF /* IntroRTLView.swift in Sources */, DF4C1AAA205AF10900DED50B /* RoundedButton.swift in Sources */, 2439CC351E665BF6003326FB /* MenuView.swift in Sources */, 2439CC4B1E665C6B003326FB /* BasicView.swift in Sources */, 24A9C1F61EF054BF00F2CF64 /* MethodCell.swift in Sources */, + DFD27841211B1A710056BD93 /* UINavigationController+Orientation.swift in Sources */, 24A9C2041EF16A3E00F2CF64 /* AutoAdjustingSizeViewController.swift in Sources */, 24CB999C1F29059B004EA7FB /* AdjustToContainerView.swift in Sources */, 2497CFED1EF40B9100DFD13B /* FormView.swift in Sources */, 24F75B5B1EE5644E008DB567 /* IntroView.swift in Sources */, + C892FA1B24A5822B0086A75E /* AutoSizingView.swift in Sources */, DF4C1AA5205AEDFC00DED50B /* SafeAreaViewController.swift in Sources */, + DFEAF74A20C9648A00E33147 /* WrapContentView.swift in Sources */, 249EFE431E64FAFE00165E39 /* AppDelegate.swift in Sources */, + DFD31BA1212EE4F200566CA4 /* TableViewReadableContentViewController.swift in Sources */, + DFBCAEFB213012930025F7BF /* MethodReadableInsetsGroupHeader.swift in Sources */, 2497CFEE1EF40B9100DFD13B /* FormViewController.swift in Sources */, 24A9C1F31EF0542F00F2CF64 /* TableViewExampleViewController.swift in Sources */, 24CB99981F290540004EA7FB /* MethodGroupHeader.swift in Sources */, 2439CC361E665BF6003326FB /* MenuViewController.swift in Sources */, 24F246111FA8D4D100B6332E /* CollectionViewExampleViewController.swift in Sources */, - 2439CC531E665C6B003326FB /* MultiRelativeViewController.swift in Sources */, + 2439CC531E665C6B003326FB /* BetweenViewController.swift in Sources */, DF4C1AA4205AEDFC00DED50B /* SafeAreaView.swift in Sources */, + C892FA1924A5821E0086A75E /* AutoSizingViewController.swift in Sources */, 241637771F8E4F9100EE703A /* IntroObjectiveCView.m in Sources */, 24CB99A01F290664004EA7FB /* ChoiceSelectorView.swift in Sources */, 249326891EEEEE3D00BCB814 /* Stylesheet.swift in Sources */, - 2439CC521E665C6B003326FB /* MultiRelativeView.swift in Sources */, + C892FA1D24A584010086A75E /* ContentService.swift in Sources */, + DFD31BA0212EE4F200566CA4 /* TableViewReadableContentView.swift in Sources */, + DF39089A211900480049FD56 /* AnimationsViewController.swift in Sources */, + 2439CC521E665C6B003326FB /* BetweenView.swift in Sources */, 24F246101FA8D4D100B6332E /* CollectionViewExampleView.swift in Sources */, 24A9C1F21EF0542F00F2CF64 /* TableViewExampleView.swift in Sources */, 24CB999D1F29059B004EA7FB /* AdjustToContainerViewController.swift in Sources */, @@ -624,14 +736,6 @@ }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 2439CC2A1E6658C3003326FB /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = PinLayout; - targetProxy = 2439CC291E6658C3003326FB /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 249EFE4B1E64FAFE00165E39 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; @@ -649,6 +753,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -658,6 +763,7 @@ 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_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -665,8 +771,10 @@ 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; @@ -691,12 +799,13 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -705,6 +814,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -714,6 +824,7 @@ 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_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -721,8 +832,10 @@ 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; @@ -741,10 +854,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -754,17 +869,21 @@ isa = XCBuildConfiguration; baseConfigurationReference = A35A00E6536E49A548E763E6 /* Pods-PinLayoutSample.debug.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "PinLayoutSample/Supporting Files/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.mirego.PinLayoutSample; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.layoutbox.PinLayoutSample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "PinLayoutSample/UI/Examples/PinLayoutSample-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Debug; }; @@ -772,16 +891,20 @@ isa = XCBuildConfiguration; baseConfigurationReference = C589624E868FCB20F7C10918 /* Pods-PinLayoutSample.release.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "PinLayoutSample/Supporting Files/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.mirego.PinLayoutSample; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.layoutbox.PinLayoutSample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "PinLayoutSample/UI/Examples/PinLayoutSample-Bridging-Header.h"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Release; }; diff --git a/Example/PinLayoutSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Example/PinLayoutSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/Example/PinLayoutSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Example/PinLayoutSample.xcodeproj/xcshareddata/xcschemes/PinLayoutSample.xcscheme b/Example/PinLayoutSample.xcodeproj/xcshareddata/xcschemes/PinLayoutSample.xcscheme index d6642870..176ac935 100644 --- a/Example/PinLayoutSample.xcodeproj/xcshareddata/xcschemes/PinLayoutSample.xcscheme +++ b/Example/PinLayoutSample.xcodeproj/xcshareddata/xcschemes/PinLayoutSample.xcscheme @@ -1,6 +1,6 @@ + + + + @@ -39,17 +48,6 @@ - - - - - - - - Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) window!.backgroundColor = UIColor.white diff --git a/Example/PinLayoutSample/Supporting Files/Info.plist b/Example/PinLayoutSample/Supporting Files/Info.plist index 8f7eb3c6..efa8cd72 100644 --- a/Example/PinLayoutSample/Supporting Files/Info.plist +++ b/Example/PinLayoutSample/Supporting Files/Info.plist @@ -31,12 +31,10 @@ UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortraitUpsideDown UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight diff --git a/Example/PinLayoutSample/UI/Common/BaseFormView.swift b/Example/PinLayoutSample/UI/Common/BaseFormView.swift index 04b676cf..f1a9bd20 100644 --- a/Example/PinLayoutSample/UI/Common/BaseFormView.swift +++ b/Example/PinLayoutSample/UI/Common/BaseFormView.swift @@ -22,57 +22,56 @@ import PinLayout class BaseFormView: UIView { let formScrollView = UIScrollView() - + init() { super.init(frame: .zero) - + formScrollView.showsVerticalScrollIndicator = false formScrollView.keyboardDismissMode = .onDrag formScrollView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapScrollView))) addSubview(formScrollView) - NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: .UIKeyboardWillShow, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: .UIKeyboardWillHide, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) } - + required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } deinit { - NotificationCenter.default.removeObserver(self) } override func layoutSubviews() { super.layoutSubviews() - + formScrollView.pin.all() } @objc internal func keyboardWillShow(notification: Notification) { - guard let sizeValue = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else { return } + guard let sizeValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return } setFormScrollView(bottomInset: sizeValue.cgRectValue.height) } - + @objc internal func keyboardWillHide(notification: Notification) { resetScrollOffset() } - + @objc internal func didTapScrollView() { endEditing(true) resetScrollOffset() } - - fileprivate func resetScrollOffset() { + + private func resetScrollOffset() { guard formScrollView.contentInset != .zero else { return } setFormScrollView(bottomInset: 0) } - - fileprivate func setFormScrollView(bottomInset: CGFloat) { + + private func setFormScrollView(bottomInset: CGFloat) { formScrollView.contentInset = UIEdgeInsets(top: formScrollView.contentInset.top, left: 0, bottom: bottomInset, right: 0) } diff --git a/Example/PinLayoutSample/UI/Common/BasicView.swift b/Example/PinLayoutSample/UI/Common/BasicView.swift index eb2cebd7..7ef5dcb5 100644 --- a/Example/PinLayoutSample/UI/Common/BasicView.swift +++ b/Example/PinLayoutSample/UI/Common/BasicView.swift @@ -20,15 +20,15 @@ import UIKit class BasicView: UIView { - fileprivate let label = UILabel() - + private let label = UILabel() + init(text: String? = nil, color: UIColor) { super.init(frame: .zero) backgroundColor = color layer.borderWidth = 1 layer.borderColor = UIColor.lightGray.cgColor - + label.text = text label.font = .systemFont(ofSize: 7) label.textColor = .white @@ -39,19 +39,22 @@ class BasicView: UIView { required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override func layoutSubviews() { super.layoutSubviews() - - label.pin.top().left().right().margin(4).sizeToFit(.width) + + label.pin.top().horizontally().margin(4).sizeToFit(.width) } - + var sizeThatFitsExpectedArea: CGFloat = 40 * 40 var sizeThatFitSizeOffset: CGFloat = 0 - + override func sizeThatFits(_ size: CGSize) -> CGSize { var newSize = CGSize() - if size.width != CGFloat.greatestFiniteMagnitude { + if size.width != CGFloat.greatestFiniteMagnitude && size.height != CGFloat.greatestFiniteMagnitude { + newSize.width = 35 + newSize.height = 45 + } else if size.width != CGFloat.greatestFiniteMagnitude { newSize.width = size.width + sizeThatFitSizeOffset newSize.height = sizeThatFitsExpectedArea / newSize.width } else if size.height != CGFloat.greatestFiniteMagnitude { @@ -61,7 +64,7 @@ class BasicView: UIView { newSize.width = 40 newSize.height = sizeThatFitsExpectedArea / newSize.width } - + return newSize } } diff --git a/Example/PinLayoutSample/UI/Common/ContentService.swift b/Example/PinLayoutSample/UI/Common/ContentService.swift new file mode 100644 index 00000000..65955d25 --- /dev/null +++ b/Example/PinLayoutSample/UI/Common/ContentService.swift @@ -0,0 +1,41 @@ +import UIKit + +class ContentService { + static let shared = ContentService() + + private init() {} + + func fetchText(numberOfParagraph: Int = 1, completionHandler: ((Result<[String], Error>) -> Void)?) { + URLSession.shared.dataTask(with: URL(string: "https://baconipsum.com/api/?type=all-meat¶s=\(numberOfParagraph)&start-with-lorem=1")!) { data, _, error in + guard let data = data, error == nil, + let paragraphs = try? JSONDecoder().decode([String].self, from: data) + else { + DispatchQueue.main.async { + completionHandler?(Result<[String], Error>.failure(error!)) + } + return + } + + DispatchQueue.main.async { + completionHandler?(Result<[String], Error>.success(paragraphs)) + } + }.resume() + } + + func fetchImage(width: Int, height: Int, completionHandler: ((Result) -> Void)?) { + URLSession.shared.dataTask(with: URL(string: "https://baconmockup.com/\(width)/\(height)")!) { data, _, error in + guard let data = data, error == nil, + let image = UIImage(data: data) + else { + DispatchQueue.main.async { + completionHandler?(Result.failure(error!)) + } + return + } + + DispatchQueue.main.async { + completionHandler?(Result.success(image)) + } + }.resume() + } +} diff --git a/Example/PinLayoutSample/UI/Common/ProxyWrapper.swift b/Example/PinLayoutSample/UI/Common/ProxyWrapper.swift new file mode 100644 index 00000000..9752998b --- /dev/null +++ b/Example/PinLayoutSample/UI/Common/ProxyWrapper.swift @@ -0,0 +1,31 @@ +import Foundation + +@propertyWrapper +public struct Proxy { + private let keyPath: ReferenceWritableKeyPath + + public init(_ keyPath: ReferenceWritableKeyPath) { + self.keyPath = keyPath + } + + public var wrappedValue: Value { + get { fatalError() } + set { fatalError() } + } + + public static subscript( + _enclosingInstance observed: EnclosingSelf, + wrapped wrappedKeyPath: ReferenceWritableKeyPath, + storage storageKeyPath: ReferenceWritableKeyPath + ) -> Value { + get { + let storageValue = observed[keyPath: storageKeyPath] + let value = observed[keyPath: storageValue.keyPath] + return value + } + set { + let storageValue = observed[keyPath: storageKeyPath] + observed[keyPath: storageValue.keyPath] = newValue + } + } +} diff --git a/Example/PinLayoutSample/UI/Common/Stylesheet.swift b/Example/PinLayoutSample/UI/Common/Stylesheet.swift index b4223652..364c68e6 100644 --- a/Example/PinLayoutSample/UI/Common/Stylesheet.swift +++ b/Example/PinLayoutSample/UI/Common/Stylesheet.swift @@ -20,5 +20,11 @@ import UIKit extension UIColor { - class var pinLayoutColor: UIColor { return UIColor(red: 90.0 / 255.0, green: 171.0 / 255.0, blue: 243.0 / 255.0, alpha: 1.0) } + class var pinLayoutColor: UIColor { + return UIColor(red: 90.0 / 255.0, green: 171.0 / 255.0, blue: 243.0 / 255.0, alpha: 1.0) + } + + class var lightColor: UIColor { + return UIColor(red: 230.0 / 255.0, green: 240.0 / 255.0, blue: 255.0 / 255.0, alpha: 1.0) + } } diff --git a/Example/PinLayoutSample/UI/Common/UIEdgeInsets+PinLayout.swift b/Example/PinLayoutSample/UI/Common/UIEdgeInsets+PinLayout.swift index 1d0f2bf3..3ee4adcf 100644 --- a/Example/PinLayoutSample/UI/Common/UIEdgeInsets+PinLayout.swift +++ b/Example/PinLayoutSample/UI/Common/UIEdgeInsets+PinLayout.swift @@ -38,7 +38,7 @@ extension UIEdgeInsets { right: minValue(self.right, minValue: dx)) } - fileprivate func minValue(_ value: CGFloat, minValue: CGFloat) -> CGFloat { + private func minValue(_ value: CGFloat, minValue: CGFloat) -> CGFloat { return value >= minValue ? value : minValue } } diff --git a/Example/PinLayoutSample/UI/Common/UINavigationController+Orientation.swift b/Example/PinLayoutSample/UI/Common/UINavigationController+Orientation.swift new file mode 100644 index 00000000..73b42984 --- /dev/null +++ b/Example/PinLayoutSample/UI/Common/UINavigationController+Orientation.swift @@ -0,0 +1,33 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +import UIKit + +extension UINavigationController { + open override var supportedInterfaceOrientations: UIInterfaceOrientationMask { + if let visibleViewControllerOrientations = visibleViewController?.supportedInterfaceOrientations { + return visibleViewControllerOrientations + } else { + return .all + } + } + + open override var shouldAutorotate: Bool { + return visibleViewController?.shouldAutorotate ?? super.shouldAutorotate + } +} diff --git a/Example/PinLayoutSample/UI/Common/UITabBarController+Orientation.swift b/Example/PinLayoutSample/UI/Common/UITabBarController+Orientation.swift new file mode 100644 index 00000000..4ee89d86 --- /dev/null +++ b/Example/PinLayoutSample/UI/Common/UITabBarController+Orientation.swift @@ -0,0 +1,35 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +import UIKit + +extension UITabBarController { + open override var supportedInterfaceOrientations: UIInterfaceOrientationMask { + if let selected = selectedViewController { + return selected.supportedInterfaceOrientations + } + return super.supportedInterfaceOrientations + } + + open override var shouldAutorotate: Bool { + if let selected = selectedViewController { + return selected.shouldAutorotate + } + return super.shouldAutorotate + } +} diff --git a/Example/PinLayoutSample/UI/Examples/AdjustToContainer/AdjustToContainerView.swift b/Example/PinLayoutSample/UI/Examples/AdjustToContainer/AdjustToContainerView.swift index 2e2cb5d2..680e391f 100644 --- a/Example/PinLayoutSample/UI/Examples/AdjustToContainer/AdjustToContainerView.swift +++ b/Example/PinLayoutSample/UI/Examples/AdjustToContainer/AdjustToContainerView.swift @@ -21,9 +21,9 @@ import UIKit import PinLayout class AdjustToContainerView: UIView { - fileprivate let languageSelectorView = ChoiceSelectorView(text: "What is your favorite language?", choices: ["Swift", "Objective-C", "C++"]) - fileprivate let swiftOpinionSelector = ChoiceSelectorView(text: "Overall, are you satisfied with the Swift performance in your projects?", choices: ["Yes", "No"]) - fileprivate let swiftUsageSelector = ChoiceSelectorView(text: "How often do you typically use Swift?", choices: ["Daily", "Weekly", "Montly", "Do not use"]) + private let languageSelectorView = ChoiceSelectorView(text: "What is your favorite language?", choices: ["Swift", "Objective-C", "C++"]) + private let swiftOpinionSelector = ChoiceSelectorView(text: "Overall, are you satisfied with the Swift performance in your projects?", choices: ["Yes", "No"]) + private let swiftUsageSelector = ChoiceSelectorView(text: "How often do you typically use Swift?", choices: ["Daily", "Weekly", "Montly", "Do not use"]) init() { super.init(frame: .zero) @@ -40,7 +40,7 @@ class AdjustToContainerView: UIView { override func layoutSubviews() { super.layoutSubviews() - + languageSelectorView.pin.top(pin.safeArea).horizontally(pin.safeArea).sizeToFit(.width) swiftOpinionSelector.pin.below(of: languageSelectorView).horizontally(pin.safeArea).marginTop(10).sizeToFit(.width) swiftUsageSelector.pin.below(of: swiftOpinionSelector).horizontally(pin.safeArea).marginTop(10).sizeToFit(.width) diff --git a/Example/PinLayoutSample/UI/Examples/AdjustToContainer/AdjustToContainerViewController.swift b/Example/PinLayoutSample/UI/Examples/AdjustToContainer/AdjustToContainerViewController.swift index 134e58c3..69eca3bf 100644 --- a/Example/PinLayoutSample/UI/Examples/AdjustToContainer/AdjustToContainerViewController.swift +++ b/Example/PinLayoutSample/UI/Examples/AdjustToContainer/AdjustToContainerViewController.swift @@ -20,20 +20,20 @@ import UIKit class AdjustToContainerViewController: UIViewController { - fileprivate var mainView: AdjustToContainerView { + private var mainView: AdjustToContainerView { return self.view as! AdjustToContainerView } init(pageType: PageType) { super.init(nibName: nil, bundle: nil) - - title = pageType.text + + title = pageType.title } - + required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } - + override func loadView() { view = AdjustToContainerView() } diff --git a/Example/PinLayoutSample/UI/Examples/AdjustToContainer/Subviews/ChoiceSelectorView.swift b/Example/PinLayoutSample/UI/Examples/AdjustToContainer/Subviews/ChoiceSelectorView.swift index f171e540..fc030c39 100644 --- a/Example/PinLayoutSample/UI/Examples/AdjustToContainer/Subviews/ChoiceSelectorView.swift +++ b/Example/PinLayoutSample/UI/Examples/AdjustToContainer/Subviews/ChoiceSelectorView.swift @@ -22,9 +22,9 @@ import PinLayout class ChoiceSelectorView: UIView { - fileprivate let textLabel = UILabel() - fileprivate let segmentedControl = UISegmentedControl() - + private let textLabel = UILabel() + private let segmentedControl = UISegmentedControl() + init(text: String, choices: [String]) { super.init(frame: .zero) @@ -33,33 +33,33 @@ class ChoiceSelectorView: UIView { textLabel.numberOfLines = 0 textLabel.lineBreakMode = .byWordWrapping addSubview(textLabel) - + choices.forEach({ segmentedControl.insertSegment(withTitle: $0, at: segmentedControl.numberOfSegments, animated: false) }) segmentedControl.tintColor = .pinLayoutColor segmentedControl.sizeToFit() addSubview(segmentedControl) } - + required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override func layoutSubviews() { super.layoutSubviews() _ = layout() } - + override func sizeThatFits(_ size: CGSize) -> CGSize { // 1) Set the width to the specified width self.pin.width(size.width) - + // 2) Layout the contentView's controls return layout() } - - fileprivate func layout() -> CGSize { + + private func layout() -> CGSize { let margin: CGFloat = 12 - + if frame.width > 500 { // The UISegmentedControl is at the top-right corner and the label takes the remaining horizontal space. segmentedControl.pin.top().right().margin(margin) @@ -69,7 +69,7 @@ class ChoiceSelectorView: UIView { textLabel.pin.top().horizontally().margin(margin).sizeToFit(.width) segmentedControl.pin.below(of: textLabel).right().margin(margin) } - + return CGSize(width: frame.width, height: max(textLabel.frame.maxY, segmentedControl.frame.maxY) + margin) } } diff --git a/Example/PinLayoutSample/UI/Examples/Animations/AnimationsView.swift b/Example/PinLayoutSample/UI/Examples/Animations/AnimationsView.swift new file mode 100644 index 00000000..30847f7f --- /dev/null +++ b/Example/PinLayoutSample/UI/Examples/Animations/AnimationsView.swift @@ -0,0 +1,144 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit +import PinLayout + +enum LayoutMode: Int { + case imageLeft + case imageRight + case imageTop + case overImage +} + +class AnimationsView: UIView { + private var currentLayoutMode = LayoutMode.imageLeft + + // This view exists only to support more easily iPhoneX landscape orientation. + private let safeAreaContainerView = UIView() + + private let textLabel = UILabel() + private let segmented = UISegmentedControl(items: ["Layout 1", "Layout 2", "Layout 3", "Layout 4"]) + + private let houseImageView = UIImageView(image: UIImage(named: "PinLayout-logo")) + private let margin: CGFloat = 10 + private let titleLabel = UILabel() + private let descriptionLabel = UILabel() + private let overlayView = UIView() + + init() { + super.init(frame: .zero) + backgroundColor = .white + + addSubview(safeAreaContainerView) + + textLabel.text = "Select the layout mode:" + textLabel.font = .systemFont(ofSize: 14) + textLabel.numberOfLines = 0 + textLabel.lineBreakMode = .byWordWrapping + safeAreaContainerView.addSubview(textLabel) + + segmented.selectedSegmentIndex = 0 + segmented.tintColor = .pinLayoutColor + segmented.addTarget(self, action: #selector(didChangeLayoutMode), for: .valueChanged) + safeAreaContainerView.addSubview(segmented) + + let url = URL(string: "https://i.pinimg.com/736x/76/cc/b4/76ccb45bc61b098c7b9b75de62fcf533--house-design-campo-grande.jpg")! + houseImageView.contentMode = .scaleAspectFill + houseImageView.clipsToBounds = true + houseImageView.layer.borderColor = UIColor.pinLayoutColor.cgColor + houseImageView.layer.borderWidth = 1 + houseImageView.download(url: url) + safeAreaContainerView.addSubview(houseImageView) + + overlayView.backgroundColor = UIColor.white.withAlphaComponent(0.7) + safeAreaContainerView.addSubview(overlayView) + + titleLabel.text = "Castlecrag House" + titleLabel.font = UIFont.boldSystemFont(ofSize: 16) + titleLabel.numberOfLines = 1 + safeAreaContainerView.addSubview(titleLabel) + + descriptionLabel.text = "The house is located in a unique Sydney bush suburb. The site itself has three particularly special qualities – a beautiful sandstone outcrop to the street, an adjacent bush reserve, and tranquil water views to the bay." + descriptionLabel.font = UIFont.systemFont(ofSize: 13) + descriptionLabel.textColor = .darkGray + descriptionLabel.numberOfLines = 0 + descriptionLabel.lineBreakMode = .byWordWrapping + safeAreaContainerView.addSubview(descriptionLabel) + + didChangeLayoutMode() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override func layoutSubviews() { + super.layoutSubviews() + + // This view exists only to support more easily iPhoneX landscape orientation. On larger + // screen the view's width is limited to 700 px and centered + safeAreaContainerView.pin.all(pin.safeArea).maxWidth(700).justify(.center) + + textLabel.pin.top(10).horizontally(margin).sizeToFit(.width) + segmented.pin.below(of: textLabel, aligned: .left).right(margin).marginTop(4) + + layoutCell() + } + + private func layoutCell() { + switch currentLayoutMode { + case .imageLeft: + houseImageView.pin.below(of: segmented).left(margin).marginTop(margin).size(140) + titleLabel.pin.after(of: houseImageView, aligned: .top).marginLeft(margin).sizeToFit() + descriptionLabel.pin.below(of: titleLabel, aligned: .left).right(margin).marginTop(margin).sizeToFit(.width) + + case .imageRight: + houseImageView.pin.below(of: segmented).right(margin).marginTop(margin).size(140) + titleLabel.pin.below(of: segmented, aligned: .left).margin(margin).sizeToFit() + descriptionLabel.pin.below(of: titleLabel, aligned: .left).before(of: houseImageView).marginTop(margin).marginRight(margin).sizeToFit(.width) + + case .imageTop: + houseImageView.pin.below(of: segmented).horizontally(margin).marginTop(margin).height(160) + titleLabel.pin.below(of: houseImageView, aligned: .left).marginTop(margin).sizeToFit() + descriptionLabel.pin.below(of: titleLabel).horizontally(margin).marginTop(margin).sizeToFit(.width) + + case .overImage: + houseImageView.pin.below(of: segmented).horizontally(pin.safeArea + margin).marginTop(margin).height(260) + descriptionLabel.pin.bottomLeft(to: houseImageView.anchor.bottomLeft).right(to: houseImageView.edge.right) + .margin(margin).sizeToFit(.width) + titleLabel.pin.above(of: descriptionLabel, aligned: .left).marginBottom(margin).sizeToFit() + + let overlayHeight = houseImageView.frame.maxY - titleLabel.frame.minY + margin + overlayView.pin.bottomLeft(to: houseImageView.anchor.bottomLeft).right(to: houseImageView.edge.right).height(overlayHeight) + } + + overlayView.alpha = currentLayoutMode == .overImage ? 1 : 0 + } + + @objc private func didChangeLayoutMode() { + guard let layoutMode = LayoutMode(rawValue: segmented.selectedSegmentIndex) else { return } + guard layoutMode != currentLayoutMode else { return } + self.currentLayoutMode = layoutMode + + UIView.animate(withDuration: 0.3) { + self.layoutCell() + } + } +} diff --git a/Example/PinLayoutSample/UI/Examples/Animations/AnimationsViewController.swift b/Example/PinLayoutSample/UI/Examples/Animations/AnimationsViewController.swift new file mode 100644 index 00000000..fe018a21 --- /dev/null +++ b/Example/PinLayoutSample/UI/Examples/Animations/AnimationsViewController.swift @@ -0,0 +1,40 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +class AnimationsViewController: UIViewController { + private var mainView: AnimationsView { + return self.view as! AnimationsView + } + + init(pageType: PageType) { + super.init(nibName: nil, bundle: nil) + + title = pageType.title + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override func loadView() { + view = AnimationsView() + } +} diff --git a/Example/PinLayoutSample/UI/Examples/AutoAdjustingSize/AutoAdjustingSizeView.swift b/Example/PinLayoutSample/UI/Examples/AutoAdjustingSize/AutoAdjustingSizeView.swift index 25a3238a..13ffc84a 100644 --- a/Example/PinLayoutSample/UI/Examples/AutoAdjustingSize/AutoAdjustingSizeView.swift +++ b/Example/PinLayoutSample/UI/Examples/AutoAdjustingSize/AutoAdjustingSizeView.swift @@ -22,22 +22,22 @@ import PinLayout class AutoAdjustingSizeView: UIView { - fileprivate let contentScrollView = UIScrollView() + private let contentScrollView = UIScrollView() - var row1 = BasicView(text: "", color: .lightGray) + var row1Container = BasicView(text: "", color: .lightGray) var row1Item1 = BasicView(text: "50px", color: .gray) var row1Item2 = BasicView(text: "Remaining space", color: .pinLayoutColor) - var row2 = BasicView(text: "", color: .lightGray) + var row2Container = BasicView(text: "", color: .lightGray) var row2Item1 = BasicView(text: "150px", color: .gray) var row2Item2 = BasicView(text: "Remaining space", color: .pinLayoutColor) - var row3 = BasicView(text: "", color: .lightGray) + var row3Container = BasicView(text: "", color: .lightGray) var row3Item1 = BasicView(text: "50px", color: .gray) var row3Item2 = BasicView(text: "Remaining space", color: .pinLayoutColor) var row3Item3 = BasicView(text: "Remaining space", color: .pinLayoutColor) - var row4 = BasicView(text: "", color: .lightGray) + var row4Container = BasicView(text: "", color: .lightGray) var row4Item1 = BasicView(text: "25%", color: .pinLayoutColor) var row4Item2 = BasicView(text: "50%", color: .gray) var row4Item3 = BasicView(text: "25%", color: .pinLayoutColor) @@ -49,23 +49,23 @@ class AutoAdjustingSizeView: UIView { contentScrollView.backgroundColor = .white addSubview(contentScrollView) - contentScrollView.addSubview(row1) - row1.addSubview(row1Item1) - row1.addSubview(row1Item2) + contentScrollView.addSubview(row1Container) + row1Container.addSubview(row1Item1) + row1Container.addSubview(row1Item2) - contentScrollView.addSubview(row2) - row2.addSubview(row2Item1) - row2.addSubview(row2Item2) + contentScrollView.addSubview(row2Container) + row2Container.addSubview(row2Item1) + row2Container.addSubview(row2Item2) - contentScrollView.addSubview(row3) - row3.addSubview(row3Item1) - row3.addSubview(row3Item2) - row3.addSubview(row3Item3) + contentScrollView.addSubview(row3Container) + row3Container.addSubview(row3Item1) + row3Container.addSubview(row3Item2) + row3Container.addSubview(row3Item3) - contentScrollView.addSubview(row4) - row4.addSubview(row4Item1) - row4.addSubview(row4Item2) - row4.addSubview(row4Item3) + contentScrollView.addSubview(row4Container) + row4Container.addSubview(row4Item1) + row4Container.addSubview(row4Item2) + row4Container.addSubview(row4Item3) } required init?(coder aDecoder: NSCoder) { @@ -77,24 +77,24 @@ class AutoAdjustingSizeView: UIView { contentScrollView.pin.all() - row1.pin.top().horizontally(pin.safeArea).height(40) - row1Item1.pin.top().left().bottom().width(50).margin(2) - row1Item2.pin.right(of: row1Item1, aligned: .top).bottomRight().margin(0, 2, 2, 2) + row1Container.pin.top().horizontally(pin.safeArea).height(40) + row1Item1.pin.vertically().left().width(50).margin(2) + row1Item2.pin.after(of: row1Item1, aligned: .top).bottomRight().margin(0, 2, 2, 2) - row2.pin.below(of: row1, aligned: .left).size(of: row1).marginTop(10) - row2Item1.pin.top().bottom().right().width(150).margin(2) - row2Item2.pin.left(of: row2Item1, aligned: .top).left().bottom().margin(0, 2, 2, 2) + row2Container.pin.below(of: row1Container, aligned: .left).size(of: row1Container).marginTop(10) + row2Item1.pin.vertically().right().width(150).margin(2) + row2Item2.pin.before(of: row2Item1, aligned: .top).left().bottom().margin(0, 2, 2, 2) - row3.pin.below(of: row2, aligned: .left).size(of: row1).marginTop(10) - row3Item1.pin.topCenter().bottom().width(50).margin(2) - row3Item2.pin.left(of: row3Item1, aligned: .top).left().bottom().margin(0, 2, 2, 2) - row3Item3.pin.right(of: row3Item1, aligned: .top).right().bottom().margin(0, 2, 2, 2) + row3Container.pin.below(of: row2Container, aligned: .left).size(of: row1Container).marginTop(10) + row3Item1.pin.vertically().hCenter().width(50).margin(2) + row3Item2.pin.before(of: row3Item1, aligned: .top).left().bottom().margin(0, 2, 2, 2) + row3Item3.pin.after(of: row3Item1, aligned: .top).right().bottom().margin(0, 2, 2, 2) - row4.pin.below(of: row3, aligned: .left).size(of: row1).marginTop(10) - row4Item1.pin.top().left().width(25%).bottom().margin(2) - row4Item2.pin.right(of: row4Item1, aligned: .top).width(50%).bottom().margin(0, 2, 2, 2) - row4Item3.pin.right(of: row4Item2, aligned: .top).right().bottom().margin(0, 2, 2, 2) + row4Container.pin.below(of: row3Container, aligned: .left).size(of: row1Container).marginTop(10) + row4Item1.pin.vertically().left().width(25%).margin(2) + row4Item2.pin.after(of: row4Item1, aligned: .top).width(50%).bottom().margin(0, 2, 2, 2) + row4Item3.pin.after(of: row4Item2, aligned: .top).right().bottom().margin(0, 2, 2, 2) - contentScrollView.contentSize = CGSize(width: contentScrollView.frame.width, height: row4.frame.maxY) + contentScrollView.contentSize = CGSize(width: contentScrollView.frame.width, height: row4Container.frame.maxY) } } diff --git a/Example/PinLayoutSample/UI/Examples/AutoAdjustingSize/AutoAdjustingSizeViewController.swift b/Example/PinLayoutSample/UI/Examples/AutoAdjustingSize/AutoAdjustingSizeViewController.swift index ed6f3d21..3e519032 100644 --- a/Example/PinLayoutSample/UI/Examples/AutoAdjustingSize/AutoAdjustingSizeViewController.swift +++ b/Example/PinLayoutSample/UI/Examples/AutoAdjustingSize/AutoAdjustingSizeViewController.swift @@ -20,14 +20,14 @@ import UIKit class AutoAdjustingSizeViewController: UIViewController { - fileprivate var mainView: AutoAdjustingSizeView { + private var mainView: AutoAdjustingSizeView { return self.view as! AutoAdjustingSizeView } init(pageType: PageType) { super.init(nibName: nil, bundle: nil) - title = pageType.text + title = pageType.title } required init?(coder aDecoder: NSCoder) { diff --git a/Example/PinLayoutSample/UI/Examples/AutoSizing/AutoSizingContainerView.swift b/Example/PinLayoutSample/UI/Examples/AutoSizing/AutoSizingContainerView.swift new file mode 100644 index 00000000..40048996 --- /dev/null +++ b/Example/PinLayoutSample/UI/Examples/AutoSizing/AutoSizingContainerView.swift @@ -0,0 +1,68 @@ +import UIKit + +final class AutoSizingContainerView: UIView { + private let imageView = UIImageView() + private let firstTextLabel = UILabel() + private let secondTextLabel = UILabel() + + private let margin: CGFloat = 10 + + @Proxy(\AutoSizingContainerView.imageView.image) + var image: UIImage? { + didSet { + setNeedsLayout() + } + } + + @Proxy(\AutoSizingContainerView.firstTextLabel.text) + var firstText: String? { + didSet { + setNeedsLayout() + } + } + + @Proxy(\AutoSizingContainerView.secondTextLabel.text) + var secondText: String? { + didSet { + setNeedsLayout() + } + } + + init() { + super.init(frame: CGRect.zero) + configureView() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func configureView() { + imageView.clipsToBounds = true + imageView.contentMode = .scaleAspectFill + addSubview(imageView) + + firstTextLabel.numberOfLines = 0 + firstTextLabel.backgroundColor = UIColor.orange.withAlphaComponent(0.3) + addSubview(firstTextLabel) + + secondTextLabel.numberOfLines = 0 + secondTextLabel.backgroundColor = UIColor.green.withAlphaComponent(0.3) + addSubview(secondTextLabel) + } + + override func layoutSubviews() { + super.layoutSubviews() + performLayout() + } + + private func performLayout() { + imageView.pin.top().horizontally().sizeToFit(.width).margin(margin) + firstTextLabel.pin.below(of: imageView).horizontally().sizeToFit(.width).margin(margin) + secondTextLabel.pin.below(of: firstTextLabel).horizontally().sizeToFit(.width).margin(margin) + } + + override func sizeThatFits(_ size: CGSize) -> CGSize { + autoSizeThatFits(size, layoutClosure: performLayout) + } +} diff --git a/Example/PinLayoutSample/UI/Examples/AutoSizing/AutoSizingView.swift b/Example/PinLayoutSample/UI/Examples/AutoSizing/AutoSizingView.swift new file mode 100644 index 00000000..a248d1be --- /dev/null +++ b/Example/PinLayoutSample/UI/Examples/AutoSizing/AutoSizingView.swift @@ -0,0 +1,54 @@ +import UIKit +import PinLayout + +final class AutoSizingView: UIView { + private let scrollView = UIScrollView() + private let containerView = AutoSizingContainerView() + + private let margin: CGFloat = 30 + + init() { + super.init(frame: CGRect.zero) + configureView() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func configureView() { + backgroundColor = .white + + scrollView.alwaysBounceVertical = true + addSubview(scrollView) + + containerView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.3) + scrollView.addSubview(containerView) + } + + func updateImage(_ image: UIImage?) { + containerView.image = image + setNeedsLayout() + } + + func updateTexts(firstText: String?, secondText: String?) { + containerView.firstText = firstText + containerView.secondText = secondText + setNeedsLayout() + } + + override func layoutSubviews() { + super.layoutSubviews() + performLayout() + didPerformLayout() + } + + private func performLayout() { + scrollView.pin.all() + containerView.pin.top(margin).horizontally(margin).sizeToFit(.width) + } + + private func didPerformLayout() { + scrollView.contentSize = CGSize(width: bounds.width, height: containerView.frame.maxY + margin) + } +} diff --git a/Example/PinLayoutSample/UI/Examples/AutoSizing/AutoSizingViewController.swift b/Example/PinLayoutSample/UI/Examples/AutoSizing/AutoSizingViewController.swift new file mode 100644 index 00000000..408c9f73 --- /dev/null +++ b/Example/PinLayoutSample/UI/Examples/AutoSizing/AutoSizingViewController.swift @@ -0,0 +1,39 @@ +import UIKit + +class AutoSizingViewController: UIViewController { + private var mainView: AutoSizingView { + return self.view as! AutoSizingView + } + + override func loadView() { + self.view = AutoSizingView() + } + + override func viewDidLoad() { + super.viewDidLoad() + configureNavigationBar() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + randomizeContent() + } + + private func configureNavigationBar() { + navigationItem.title = "AutoSizing" + navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Randomize", style: .plain, target: self, action: #selector(randomizeContent)) + } + + @objc + private func randomizeContent() { + ContentService.shared.fetchText(numberOfParagraph: 2) { [weak self] (result) in + guard let strongSelf = self, case let .success(paragraphs) = result else { return } + strongSelf.mainView.updateTexts(firstText: paragraphs[0], secondText: paragraphs[1]) + } + + ContentService.shared.fetchImage(width: Int.random(in: 200..<500), height: Int.random(in: 200..<500)) { [weak self] (result) in + guard let strongSelf = self, case let .success(image) = result else { return } + strongSelf.mainView.updateImage(image) + } + } +} diff --git a/Example/PinLayoutSample/UI/Examples/MultiRelativeView/MultiRelativeView.swift b/Example/PinLayoutSample/UI/Examples/BetweenView/BetweenView.swift similarity index 73% rename from Example/PinLayoutSample/UI/Examples/MultiRelativeView/MultiRelativeView.swift rename to Example/PinLayoutSample/UI/Examples/BetweenView/BetweenView.swift index cbd56123..40dc66e2 100644 --- a/Example/PinLayoutSample/UI/Examples/MultiRelativeView/MultiRelativeView.swift +++ b/Example/PinLayoutSample/UI/Examples/BetweenView/BetweenView.swift @@ -20,11 +20,11 @@ import UIKit import PinLayout -class MultiRelativeView: UIView { - fileprivate let view1 = BasicView(text: "Relative view 1 (width: 20%, height: 50%)", color: .lightGray) - fileprivate let view2 = BasicView(text: "Relative view 2 (width: 20%, height: 50%)", color: .lightGray) - fileprivate let view = BasicView(text: "View layouted using two relative views: \n - right(of: view1, aligned: .top)\n - left(of: view2, aligned: .bottom)", - color: .pinLayoutColor) +class BetweenView: UIView { + private let view1 = BasicView(text: "Relative view 1 (width: 20%, height: 50%)", color: .lightGray) + private let view2 = BasicView(text: "Relative view 2 (width: 20%, height: 50%)", color: .lightGray) + private let view = BasicView(text: "View layouted using method horizontallyBetween: \n - horizontallyBetween(view1, and: view2, aligned: .top)", + color: .pinLayoutColor) init() { super.init(frame: .zero) @@ -44,7 +44,7 @@ class MultiRelativeView: UIView { view1.pin.top(pin.safeArea).left(pin.safeArea).width(20%).height(50%) view2.pin.top(pin.safeArea).right(pin.safeArea).width(20%).height(50%) - - view.pin.right(of: view1, aligned: .top).left(of: view2, aligned: .bottom).marginHorizontal(10) + + view.pin.horizontallyBetween(view1, and: view2, aligned: .top).height(of: view1).marginHorizontal(10) } } diff --git a/Example/PinLayoutSample/UI/Examples/MultiRelativeView/MultiRelativeViewController.swift b/Example/PinLayoutSample/UI/Examples/BetweenView/BetweenViewController.swift similarity index 86% rename from Example/PinLayoutSample/UI/Examples/MultiRelativeView/MultiRelativeViewController.swift rename to Example/PinLayoutSample/UI/Examples/BetweenView/BetweenViewController.swift index 55740d7e..85a6256d 100644 --- a/Example/PinLayoutSample/UI/Examples/MultiRelativeView/MultiRelativeViewController.swift +++ b/Example/PinLayoutSample/UI/Examples/BetweenView/BetweenViewController.swift @@ -19,15 +19,15 @@ import UIKit -class MultiRelativeViewController: UIViewController { - fileprivate var mainView: MultiRelativeView { - return self.view as! MultiRelativeView +class BetweenViewController: UIViewController { + private var mainView: BetweenView { + return self.view as! BetweenView } init(pageType: PageType) { super.init(nibName: nil, bundle: nil) - title = pageType.text + title = pageType.title } required init?(coder aDecoder: NSCoder) { @@ -35,6 +35,6 @@ class MultiRelativeViewController: UIViewController { } override func loadView() { - view = MultiRelativeView() + view = BetweenView() } } diff --git a/Example/PinLayoutSample/UI/Examples/CollectionViewExample/CollectionViewExampleView.swift b/Example/PinLayoutSample/UI/Examples/CollectionViewExample/CollectionViewExampleView.swift index accfcd65..9f4172be 100644 --- a/Example/PinLayoutSample/UI/Examples/CollectionViewExample/CollectionViewExampleView.swift +++ b/Example/PinLayoutSample/UI/Examples/CollectionViewExample/CollectionViewExampleView.swift @@ -16,11 +16,11 @@ import UIKit class CollectionViewExampleView: UIView { - fileprivate let collectionView: UICollectionView - fileprivate let flowLayout = UICollectionViewFlowLayout() - fileprivate let cellTemplate = HouseCell() + private let collectionView: UICollectionView + private let flowLayout = UICollectionViewFlowLayout() + private let cellTemplate = HouseCell() - fileprivate var houses: [House] = [] + private var houses: [House] = [] init() { collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) @@ -34,7 +34,8 @@ class CollectionViewExampleView: UIView { if #available(iOS 11.0, tvOS 11.0, *) { flowLayout.sectionInsetReference = .fromSafeArea } - + + collectionView.contentInsetAdjustmentBehavior = .never collectionView.backgroundColor = .white collectionView.dataSource = self collectionView.delegate = self diff --git a/Example/PinLayoutSample/UI/Examples/CollectionViewExample/CollectionViewExampleViewController.swift b/Example/PinLayoutSample/UI/Examples/CollectionViewExample/CollectionViewExampleViewController.swift index 2ada9e11..f2ff5546 100644 --- a/Example/PinLayoutSample/UI/Examples/CollectionViewExample/CollectionViewExampleViewController.swift +++ b/Example/PinLayoutSample/UI/Examples/CollectionViewExample/CollectionViewExampleViewController.swift @@ -22,17 +22,16 @@ struct House { } class CollectionViewExampleViewController: UIViewController { - fileprivate var mainView: CollectionViewExampleView { + private var mainView: CollectionViewExampleView { return self.view as! CollectionViewExampleView } init(pageType: PageType) { super.init(nibName: nil, bundle: nil) - title = pageType.text + title = pageType.title // This property is used only on iOS 7/8/9/10. See https://developer.apple.com/documentation/uikit/uiviewcontroller/1621372-automaticallyadjustsscrollviewin - automaticallyAdjustsScrollViewInsets = false } required init?(coder aDecoder: NSCoder) { @@ -41,6 +40,7 @@ class CollectionViewExampleViewController: UIViewController { override func loadView() { view = CollectionViewExampleView() + mainView.configure(houses: [ House(name: "Castlecrag House", price: "1,500,000$", diff --git a/Example/PinLayoutSample/UI/Examples/CollectionViewExample/HouseCell.swift b/Example/PinLayoutSample/UI/Examples/CollectionViewExample/HouseCell.swift index 6b7761da..96df0e1c 100644 --- a/Example/PinLayoutSample/UI/Examples/CollectionViewExample/HouseCell.swift +++ b/Example/PinLayoutSample/UI/Examples/CollectionViewExample/HouseCell.swift @@ -18,15 +18,15 @@ import PinLayout class HouseCell: UICollectionViewCell { static let reuseIdentifier = "HouseCell" - fileprivate let headerView = UIView() - fileprivate let nameLabel = UILabel() - fileprivate let mainImage = UIImageView() + private let headerView = UIView() + private let nameLabel = UILabel() + private let mainImage = UIImageView() - fileprivate let footerView = UIView() - fileprivate let priceLabel = UILabel() - fileprivate let distanceLabel = UILabel() + private let footerView = UIView() + private let priceLabel = UILabel() + private let distanceLabel = UILabel() - fileprivate let padding: CGFloat = 8 + private let margin: CGFloat = 8 override init(frame: CGRect) { super.init(frame: frame) @@ -41,7 +41,7 @@ class HouseCell: UICollectionViewCell { nameLabel.textColor = .white headerView.addSubview(nameLabel) - /// IMAGE + // IMAGE mainImage.backgroundColor = .black mainImage.contentMode = .scaleAspectFill mainImage.clipsToBounds = true @@ -79,15 +79,15 @@ class HouseCell: UICollectionViewCell { } private func layout() { - headerView.pin.top().horizontally(pin.safeArea).height(100) - nameLabel.pin.top().horizontally().margin(padding).sizeToFit(.width) + headerView.pin.top().horizontally(pin.safeArea).height(40) + nameLabel.pin.vCenter().horizontally(margin).sizeToFit(.width) - mainImage.pin.below(of: nameLabel).horizontally(pin.safeArea).height(300).marginTop(padding) + mainImage.pin.below(of: headerView).horizontally(pin.safeArea).height(300) footerView.pin.below(of: mainImage).horizontally(pin.safeArea) - priceLabel.pin.top().horizontally().margin(6, padding).sizeToFit(.width) - distanceLabel.pin.top().after(of: priceLabel).right().margin(6, padding).sizeToFit(.width) - footerView.pin.height(max(priceLabel.frame.maxY, distanceLabel.frame.maxY) + 6) + priceLabel.pin.top().horizontally().margin(margin).sizeToFit(.widthFlexible) + distanceLabel.pin.top().after(of: priceLabel).right().margin(margin).sizeToFit(.width) + footerView.pin.wrapContent(.vertically, padding: margin) contentView.pin.height(footerView.frame.maxY) } diff --git a/Example/PinLayoutSample/UI/Examples/Form/FormView.swift b/Example/PinLayoutSample/UI/Examples/Form/FormView.swift index f39b9de7..9cac0a04 100644 --- a/Example/PinLayoutSample/UI/Examples/Form/FormView.swift +++ b/Example/PinLayoutSample/UI/Examples/Form/FormView.swift @@ -22,28 +22,28 @@ import PinLayout class FormView: BaseFormView { - fileprivate let formContainerView = UIView() - fileprivate let formTitleLabel = UILabel() - fileprivate let nameField = UITextField() + private let formContainerView = UIView() + private let formTitleLabel = UILabel() + private let nameField = UITextField() - fileprivate let ageSwitch = UISwitch() - fileprivate let ageField = UITextField() + private let ageSwitch = UISwitch() + private let ageField = UITextField() - fileprivate let addressField = UITextField() + private let addressField = UITextField() override init() { super.init() backgroundColor = .white - + formContainerView.backgroundColor = UIColor.pinLayoutColor.withAlphaComponent(0.3) formContainerView.layer.cornerRadius = 5 formScrollView.addSubview(formContainerView) - + formTitleLabel.text = "Simple Form Example" formTitleLabel.font = .boldSystemFont(ofSize: 14) formTitleLabel.sizeToFit() formContainerView.addSubview(formTitleLabel) - + nameField.placeholder = "Name" nameField.layer.borderColor = UIColor.gray.cgColor nameField.layer.borderWidth = 1 @@ -52,47 +52,47 @@ class FormView: BaseFormView { ageSwitch.tintColor = .lightGray ageSwitch.addTarget(self, action: #selector(didChangeAgeSwitch), for: .valueChanged) formContainerView.addSubview(ageSwitch) - + ageField.placeholder = "Age" ageField.alpha = 0 ageField.layer.borderColor = UIColor.gray.cgColor ageField.layer.borderWidth = 1 formContainerView.addSubview(ageField) - + addressField.placeholder = "Address" addressField.layer.borderColor = UIColor.gray.cgColor addressField.layer.borderWidth = 1 formContainerView.addSubview(addressField) } - + required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override func layoutSubviews() { super.layoutSubviews() layoutFormFields() } - - fileprivate func layoutFormFields() { + + private func layoutFormFields() { let margin: CGFloat = 12 - + // Layout the formContainerView using the view's safeArea. - formContainerView.pin.top().hCenter().width(100%).maxWidth(400).pinEdges().margin(margin, margin, margin) + formContainerView.pin.top().hCenter().width(100%).maxWidth(400).pinEdges().margin(margin) - formTitleLabel.pin.topCenter().margin(margin) + formTitleLabel.pin.topCenter(margin) - nameField.pin.below(of: formTitleLabel).left().right().height(40).margin(margin) + nameField.pin.below(of: formTitleLabel).horizontally().height(40).margin(margin) + + ageSwitch.pin.below(of: nameField, aligned: .left).marginTop(margin).sizeToFit() + ageField.pin.below(of: ageSwitch).horizontally().height(40).margin(margin) - ageSwitch.pin.below(of: nameField).left().right().height(40).margin(margin) - ageField.pin.below(of: ageSwitch).left().right().height(40).margin(margin) - // Layout the Address UITextField below the last visible view, either ageSwitch or ageField. - addressField.pin.below(of: visible([ageSwitch, ageField])).left().right().height(40).margin(margin) - + addressField.pin.below(of: visible([ageSwitch, ageField])).horizontally().height(40).margin(margin) + // Adjust the form container bottom to contains all its childrens - formContainerView.pin.height(addressField.frame.maxY + margin) - + formContainerView.pin.wrapContent(.vertically, padding: margin) + // Adjust UIScrollView contentSize formScrollView.contentSize = formContainerView.frame.size } diff --git a/Example/PinLayoutSample/UI/Examples/Form/FormViewController.swift b/Example/PinLayoutSample/UI/Examples/Form/FormViewController.swift index 242b992e..a7abb287 100644 --- a/Example/PinLayoutSample/UI/Examples/Form/FormViewController.swift +++ b/Example/PinLayoutSample/UI/Examples/Form/FormViewController.swift @@ -20,14 +20,14 @@ import UIKit class FormViewController: UIViewController { - fileprivate var mainView: FormView { + private var mainView: FormView { return self.view as! FormView } init(pageType: PageType) { super.init(nibName: nil, bundle: nil) - title = pageType.text + title = pageType.title } required init?(coder aDecoder: NSCoder) { diff --git a/Example/PinLayoutSample/UI/Examples/Intro/IntroView.swift b/Example/PinLayoutSample/UI/Examples/Intro/IntroView.swift index 4498410e..9de3fcbf 100644 --- a/Example/PinLayoutSample/UI/Examples/Intro/IntroView.swift +++ b/Example/PinLayoutSample/UI/Examples/Intro/IntroView.swift @@ -21,10 +21,10 @@ import UIKit import PinLayout class IntroView: UIView { - fileprivate let logo = UIImageView(image: UIImage(named: "PinLayout-logo")) - fileprivate let segmented = UISegmentedControl(items: ["Intro", "1", "2"]) - fileprivate let textLabel = UILabel() - fileprivate let separatorView = UIView() + private let logo = UIImageView(image: UIImage(named: "PinLayout-logo")) + private let segmented = UISegmentedControl(items: ["Intro", "1", "2"]) + private let textLabel = UILabel() + private let separatorView = UIView() init() { super.init(frame: .zero) diff --git a/Example/PinLayoutSample/UI/Examples/Intro/IntroViewController.swift b/Example/PinLayoutSample/UI/Examples/Intro/IntroViewController.swift index 60e81979..f6d9cb38 100644 --- a/Example/PinLayoutSample/UI/Examples/Intro/IntroViewController.swift +++ b/Example/PinLayoutSample/UI/Examples/Intro/IntroViewController.swift @@ -20,14 +20,14 @@ import UIKit class IntroViewController: UIViewController { - fileprivate var mainView: IntroView { + private var mainView: IntroView { return self.view as! IntroView } init(pageType: PageType) { super.init(nibName: nil, bundle: nil) - title = pageType.text + title = pageType.title } required init?(coder aDecoder: NSCoder) { diff --git a/Example/PinLayoutSample/UI/Examples/IntroObjectiveC/IntroObjectiveCView.m b/Example/PinLayoutSample/UI/Examples/IntroObjectiveC/IntroObjectiveCView.m index 9e45f61d..cf19e91a 100644 --- a/Example/PinLayoutSample/UI/Examples/IntroObjectiveC/IntroObjectiveCView.m +++ b/Example/PinLayoutSample/UI/Examples/IntroObjectiveC/IntroObjectiveCView.m @@ -64,11 +64,14 @@ - (void)dealloc { - (void) layoutSubviews { [super layoutSubviews]; - - [[[[[[logo.pinObjc top] left] width:100] aspectRatio] marginWithTop:topLayoutGuide + 10 horizontal:10 bottom:10] layout]; - [[[[segmented.pinObjc rightOf:logo aligned:VerticalAlignTop] right] marginHorizontal:10] layout]; - [[[[[[textLabel.pinObjc belowOf:segmented aligned:HorizontalAlignLeft] widthOf:segmented] pinEdges] marginTop:10] sizeToFit:FitWidth] layout]; - [[[[[separatorView.pinObjc belowOfViews:@[logo, textLabel] aligned:HorizontalAlignLeft] rightTo:segmented.edge.right] height:1] marginTop:10] layout]; + + CGFloat margin = 10; + UIEdgeInsets safeArea = self.pinObjc.safeArea; + + logo.pinObjc.topInsets(safeArea).leftInsets(safeArea).width(100).aspectRatio().margin(margin).layout(); + segmented.pinObjc.rightOfAligned(logo, VerticalAlignTop).rightInsets(safeArea).marginHorizontal(margin).layout(); + textLabel.pinObjc.belowOfAligned(segmented, HorizontalAlignLeft).widthOf(segmented).pinEdges().marginTop(margin).sizeToFitType(FitWidth).layout(); + separatorView.pinObjc.belowOfViewsAligned(@[logo, textLabel], HorizontalAlignLeft).rightToEdge(segmented.edge.right).height(1).marginTop(margin).layout(); } - (void) setLayoutGuidesTop:(CGFloat)top { diff --git a/Example/PinLayoutSample/UI/Examples/IntroObjectiveC/IntroObjectiveCViewController.m b/Example/PinLayoutSample/UI/Examples/IntroObjectiveC/IntroObjectiveCViewController.m index c12af0e6..73089346 100644 --- a/Example/PinLayoutSample/UI/Examples/IntroObjectiveC/IntroObjectiveCViewController.m +++ b/Example/PinLayoutSample/UI/Examples/IntroObjectiveC/IntroObjectiveCViewController.m @@ -38,7 +38,7 @@ - (void)loadView { - (void)viewWillLayoutSubviews { [super viewWillLayoutSubviews]; - [((IntroObjectiveCView*)self.view) setLayoutGuidesTop:self.topLayoutGuide.length]; + [((IntroObjectiveCView*)self.view) setLayoutGuidesTop:self.view.safeAreaInsets.top]; } @end diff --git a/Example/PinLayoutSample/UI/Examples/IntroRTL/IntroRTLView.swift b/Example/PinLayoutSample/UI/Examples/IntroRTL/IntroRTLView.swift index 69a3ec5d..e02f5c24 100644 --- a/Example/PinLayoutSample/UI/Examples/IntroRTL/IntroRTLView.swift +++ b/Example/PinLayoutSample/UI/Examples/IntroRTL/IntroRTLView.swift @@ -21,10 +21,10 @@ import UIKit import PinLayout class IntroRTLView: UIView { - fileprivate let logo = UIImageView(image: UIImage(named: "PinLayout-logo")) - fileprivate let segmented = UISegmentedControl(items: ["Intro", "1", "2"]) - fileprivate let textLabel = UILabel() - fileprivate let separatorView = UIView() + private let logo = UIImageView(image: UIImage(named: "PinLayout-logo")) + private let segmented = UISegmentedControl(items: ["Intro", "1", "2"]) + private let textLabel = UILabel() + private let separatorView = UIView() init() { super.init(frame: .zero) @@ -41,8 +41,13 @@ class IntroRTLView: UIView { segmented.selectedSegmentIndex = 0 segmented.tintColor = .pinLayoutColor addSubview(segmented) - - textLabel.text = "Swift manual views layouting without auto layout, no magic, pure code, full control. Concise syntax, readable & chainable.\n\nSwift manual views layouting without auto layout, no magic, pure code, full control. Concise syntax, readable & chainable." + + if isLTR() { + textLabel.text = "Swift manual views layouting without auto layout, no magic, pure code, full control. Concise syntax, readable & chainable.\n\nSwift manual views layouting without auto layout, no magic, pure code, full control. Concise syntax, readable & chainable." + } else { + textLabel.text = "ان هذا وكسبت وقدّموا التاريخ،, قد مكن ووصف يعبأ واُسدل. لإنعدام الأبرياء أسر ان. فقد و أراض إتفاقية, حدى و غضون وبولندا الأوروبيّون, لم العصبة معاملة مما. بـ يكن أمّا واُسدل مهمّات. بأيدي الفرنسي بـ نفس, تصفح لفرنسا بها في. لان قد الأوروبي الأوروبية, قد جُل أحدث وحرمان اليميني." + } + textLabel.font = .systemFont(ofSize: 14) textLabel.numberOfLines = 0 textLabel.lineBreakMode = .byWordWrapping @@ -56,6 +61,10 @@ class IntroRTLView: UIView { required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } + + deinit { + Pin.layoutDirection(.ltr) + } override func layoutSubviews() { super.layoutSubviews() diff --git a/Example/PinLayoutSample/UI/Examples/IntroRTL/IntroRTLViewController.swift b/Example/PinLayoutSample/UI/Examples/IntroRTL/IntroRTLViewController.swift index 53bf1011..7a6275d0 100644 --- a/Example/PinLayoutSample/UI/Examples/IntroRTL/IntroRTLViewController.swift +++ b/Example/PinLayoutSample/UI/Examples/IntroRTL/IntroRTLViewController.swift @@ -20,14 +20,14 @@ import UIKit class IntroRTLViewController: UIViewController { - fileprivate var mainView: IntroRTLView { + private var mainView: IntroRTLView { return self.view as! IntroRTLView } init(pageType: PageType) { super.init(nibName: nil, bundle: nil) - title = pageType.text + title = pageType.title } required init?(coder aDecoder: NSCoder) { diff --git a/Example/PinLayoutSample/UI/Examples/New Group/ObjectiveC.m b/Example/PinLayoutSample/UI/Examples/New Group/ObjectiveC.m deleted file mode 100644 index 7a16a7f8..00000000 --- a/Example/PinLayoutSample/UI/Examples/New Group/ObjectiveC.m +++ /dev/null @@ -1,9 +0,0 @@ -// -// ObjectiveC.m -// PinLayoutSample -// -// Created by DION, Luc (MTL) on 2017-10-11. -// Copyright © 2017 Mirego. All rights reserved. -// - -#import diff --git a/Example/PinLayoutSample/UI/Examples/RelativeView/RelativeView.swift b/Example/PinLayoutSample/UI/Examples/RelativeView/RelativeView.swift index cb3da776..0366a050 100644 --- a/Example/PinLayoutSample/UI/Examples/RelativeView/RelativeView.swift +++ b/Example/PinLayoutSample/UI/Examples/RelativeView/RelativeView.swift @@ -21,26 +21,26 @@ import UIKit import PinLayout class RelativeView: UIView { - fileprivate let centerView = UIView() + private let centerView = UIView() - fileprivate let topLeftView = UIView() - fileprivate let topCenterView = UIView() - fileprivate let topRightView = UIView() + private let topLeftView = UIView() + private let topCenterView = UIView() + private let topRightView = UIView() - fileprivate let leftTopView = UIView() - fileprivate let leftCenterView = UIView() - fileprivate let leftBottomView = UIView() + private let leftTopView = UIView() + private let leftCenterView = UIView() + private let leftBottomView = UIView() - fileprivate let bottomLeftView = UIView() - fileprivate let bottomCenterView = UIView() - fileprivate let bottomRightView = UIView() + private let bottomLeftView = UIView() + private let bottomCenterView = UIView() + private let bottomRightView = UIView() - fileprivate let rightTopView = UIView() - fileprivate let rightCenterView = UIView() - fileprivate let rightBottomView = UIView() + private let rightTopView = UIView() + private let rightCenterView = UIView() + private let rightBottomView = UIView() - fileprivate let relativeView = UIView() - fileprivate let childRelativeView = UIView() + private let relativeView = UIView() + private let childRelativeView = UIView() init() { super.init(frame: .zero) @@ -66,7 +66,7 @@ class RelativeView: UIView { addSquare(rightBottomView, color: .lightGray) } - fileprivate func addSquare(_ view: UIView, color: UIColor) { + private func addSquare(_ view: UIView, color: UIColor) { view.backgroundColor = color view.pin.size(40) addSubview(view) @@ -85,16 +85,16 @@ class RelativeView: UIView { topCenterView.pin.above(of: centerView, aligned: .center).marginBottom(10) topRightView.pin.above(of: centerView, aligned: .right).marginBottom(10) - rightTopView.pin.right(of: centerView, aligned: .top).marginLeft(10) - rightCenterView.pin.right(of: centerView, aligned: .center).marginLeft(10) - rightBottomView.pin.right(of: centerView, aligned: .bottom).marginLeft(10) + rightTopView.pin.after(of: centerView, aligned: .top).marginLeft(10) + rightCenterView.pin.after(of: centerView, aligned: .center).marginLeft(10) + rightBottomView.pin.after(of: centerView, aligned: .bottom).marginLeft(10) bottomLeftView.pin.below(of: centerView, aligned: .left).marginTop(10) bottomCenterView.pin.below(of: centerView, aligned: .center).marginTop(10) bottomRightView.pin.below(of: centerView, aligned: .right).marginTop(10) - leftTopView.pin.left(of: centerView, aligned: .top).marginRight(10) - leftCenterView.pin.left(of: centerView, aligned: .center).marginRight(10) - leftBottomView.pin.left(of: centerView, aligned: .bottom).marginRight(10) + leftTopView.pin.before(of: centerView, aligned: .top).marginRight(10) + leftCenterView.pin.before(of: centerView, aligned: .center).marginRight(10) + leftBottomView.pin.before(of: centerView, aligned: .bottom).marginRight(10) } } diff --git a/Example/PinLayoutSample/UI/Examples/RelativeView/RelativeViewController.swift b/Example/PinLayoutSample/UI/Examples/RelativeView/RelativeViewController.swift index ac6b2853..96018d6a 100644 --- a/Example/PinLayoutSample/UI/Examples/RelativeView/RelativeViewController.swift +++ b/Example/PinLayoutSample/UI/Examples/RelativeView/RelativeViewController.swift @@ -20,14 +20,14 @@ import UIKit class RelativeViewController: UIViewController { - fileprivate var mainView: RelativeView { + private var mainView: RelativeView { return self.view as! RelativeView } init(pageType: PageType) { super.init(nibName: nil, bundle: nil) - title = pageType.text + title = pageType.title } required init?(coder aDecoder: NSCoder) { diff --git a/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaAndMarginsView.swift b/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaAndMarginsView.swift new file mode 100644 index 00000000..31d63e83 --- /dev/null +++ b/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaAndMarginsView.swift @@ -0,0 +1,57 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit +import PinLayout + +class SafeAreaAndMarginsView: UIView { + private let safeAreaView = AreaView(name: "pin.safeArea", + color: UIColor(red: 0.02, green: 0.20, blue: 0.29, alpha: 1)) + private let layoutMarginsAreaView = AreaView(name: "pin.layoutMargins", + color: UIColor(red: 0.1, green: 0.49, blue: 0.58, alpha: 1)) + private let readableMarginsView = AreaView(name: " pin.readableMargins", + color: UIColor(red: 0.15, green: 0.60, blue: 0.89, alpha: 1)) + + init() { + super.init(frame: .zero) + backgroundColor = .white + + addSubview(safeAreaView) + addSubview(layoutMarginsAreaView) + addSubview(readableMarginsView) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override func safeAreaInsetsDidChange() { + if #available(iOS 11.0, *) { + super.safeAreaInsetsDidChange() + } + } + + override func layoutSubviews() { + super.layoutSubviews() + + safeAreaView.pin.all(pin.safeArea) + layoutMarginsAreaView.pin.all(pin.layoutMargins) + readableMarginsView.pin.all(pin.readableMargins) + } +} diff --git a/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaCornersViewController.swift b/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaAndMarginsViewController.swift similarity index 80% rename from Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaCornersViewController.swift rename to Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaAndMarginsViewController.swift index 618f61ab..dd557d28 100644 --- a/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaCornersViewController.swift +++ b/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaAndMarginsViewController.swift @@ -19,16 +19,15 @@ import UIKit -class SafeAreaCornersViewController: UIViewController { - fileprivate var mainView: SafeAreaCornersView { - return self.view as! SafeAreaCornersView +class SafeAreaAndMarginsViewController: UIViewController { + private var mainView: SafeAreaAndMarginsView { + return self.view as! SafeAreaAndMarginsView } init() { super.init(nibName: nil, bundle: nil) - title = "SafeArea Corners" - tabBarItem = UITabBarItem(title: "SafeArea Corners", image: UIImage(named: "Tab2"), tag: 0) + tabBarItem = UITabBarItem(title: "Layout areas", image: UIImage(named: "Tab2"), tag: 0) } required init?(coder aDecoder: NSCoder) { @@ -36,6 +35,6 @@ class SafeAreaCornersViewController: UIViewController { } override func loadView() { - view = SafeAreaCornersView() + view = SafeAreaAndMarginsView() } } diff --git a/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaCornersView.swift b/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaCornersView.swift deleted file mode 100644 index fea7c411..00000000 --- a/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaCornersView.swift +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) 2017 Luc Dion -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -import UIKit -import PinLayout - -class SafeAreaCornersView: UIView { - fileprivate let safeAreaZoneView = UIView() - - fileprivate let centerImageView = UIImageView(image: UIImage(named: "Center")!) - - // Corners - fileprivate let topLeftImageView = UIImageView(image: UIImage(named: "ArrowCorner")!) - fileprivate let topRightImageView = UIImageView(image: UIImage(named: "ArrowCorner")!) - fileprivate let bottomRightImageView = UIImageView(image: UIImage(named: "ArrowCorner")!) - fileprivate let bottomLeftImageView = UIImageView(image: UIImage(named: "ArrowCorner")!) - - // Edges - fileprivate let topImageView = UIImageView(image: UIImage(named: "Arrow")!) - fileprivate let rightImageView = UIImageView(image: UIImage(named: "Arrow")!) - fileprivate let bottomImageView = UIImageView(image: UIImage(named: "Arrow")!) - fileprivate let leftImageView = UIImageView(image: UIImage(named: "Arrow")!) - - init() { - super.init(frame: .zero) - backgroundColor = .white - - safeAreaZoneView.backgroundColor = UIColor.black.withAlphaComponent(0.2) - addSubview(safeAreaZoneView) - - addSubview(centerImageView) - - addSubview(topLeftImageView) - topRightImageView.transform = .init(rotationAngle: CGFloat.pi / 2) - addSubview(topRightImageView) - bottomRightImageView.transform = .init(rotationAngle: CGFloat.pi) - addSubview(bottomRightImageView) - bottomLeftImageView.transform = .init(rotationAngle: CGFloat.pi * 3 / 2) - addSubview(bottomLeftImageView) - - addSubview(topImageView) - rightImageView.transform = .init(rotationAngle: CGFloat.pi / 2) - addSubview(rightImageView) - bottomImageView.transform = .init(rotationAngle: CGFloat.pi) - addSubview(bottomImageView) - leftImageView.transform = .init(rotationAngle: CGFloat.pi * 3 / 2) - addSubview(leftImageView) - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - override func safeAreaInsetsDidChange() { - if #available(iOS 11.0, *) { - super.safeAreaInsetsDidChange() - } - } - - override func layoutSubviews() { - super.layoutSubviews() - - safeAreaZoneView.pin.all(pin.safeArea) - - centerImageView.pin.center().size(40) - - topRightImageView.pin.top(pin.safeArea).right(pin.safeArea).size(40) - topLeftImageView.pin.top(pin.safeArea).left(pin.safeArea).size(40) - bottomRightImageView.pin.bottom(pin.safeArea).right(pin.safeArea).size(40) - bottomLeftImageView.pin.bottom(pin.safeArea).left(pin.safeArea).size(40) - - topImageView.pin.top(pin.safeArea).hCenter().size(40) - rightImageView.pin.right(pin.safeArea).vCenter().size(40) - bottomImageView.pin.bottom(pin.safeArea).hCenter().size(40) - leftImageView.pin.left(pin.safeArea).vCenter().size(40) - } -} diff --git a/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaView.swift b/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaView.swift index e93e2cde..c8e371f1 100644 --- a/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaView.swift +++ b/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaView.swift @@ -21,10 +21,10 @@ import UIKit import PinLayout class SafeAreaView: UIView { - fileprivate let topTextLabel = UILabel() - fileprivate let scanButton = RoundedButton(text: "Scan", icon: UIImage(named: "Barcode")!) - fileprivate let iconImageView = UIImageView(image: UIImage(named: "IconOrder")!) - fileprivate let textLabel = UILabel() + private let topTextLabel = UILabel() + private let scanButton = RoundedButton(text: "Scan", icon: UIImage(named: "Barcode")!) + private let iconImageView = UIImageView(image: UIImage(named: "IconOrder")!) + private let textLabel = UILabel() init() { super.init(frame: .zero) @@ -62,7 +62,7 @@ class SafeAreaView: UIView { topTextLabel.pin.top(pin.safeArea.top + 10).hCenter() iconImageView.pin.hCenter().vCenter(-10%) - textLabel.pin.below(of: iconImageView).hCenter().width(60%).maxWidth(400).sizeToFit(.width).marginTop(20) + textLabel.pin.below(of: iconImageView).hCenter().width(60%).maxWidth(400).marginTop(20).sizeToFit(.width) scanButton.pin.bottom(pin.safeArea.bottom + 5).hCenter().width(80%).maxWidth(300).height(40) } } diff --git a/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaViewController.swift b/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaViewController.swift index df97024d..1e9f9f06 100644 --- a/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaViewController.swift +++ b/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaViewController.swift @@ -20,14 +20,13 @@ import UIKit class SafeAreaViewController: UIViewController { - fileprivate var mainView: SafeAreaView { + private var mainView: SafeAreaView { return self.view as! SafeAreaView } init() { super.init(nibName: nil, bundle: nil) - title = "SafeArea" tabBarItem = UITabBarItem(title: "SafeArea", image: UIImage(named: "Tab1"), tag: 0) } diff --git a/Example/PinLayoutSample/UI/Examples/SafeArea/Subviews/AreaView.swift b/Example/PinLayoutSample/UI/Examples/SafeArea/Subviews/AreaView.swift new file mode 100644 index 00000000..c9602833 --- /dev/null +++ b/Example/PinLayoutSample/UI/Examples/SafeArea/Subviews/AreaView.swift @@ -0,0 +1,29 @@ +import Foundation + +class AreaView: UIView { + private let nameLabel = UILabel() + + init(name: String, color: UIColor) { + super.init(frame: .zero) + + layer.borderColor = color.cgColor + layer.borderWidth = 2 + + nameLabel.text = name + nameLabel.textColor = color + nameLabel.font = .systemFont(ofSize: 18) + nameLabel.layer.anchorPoint = CGPoint(x: 0, y: 1) + nameLabel.transform = .init(rotationAngle: (CGFloat.pi / 2)) + addSubview(nameLabel) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + + nameLabel.pin.topLeft(1).sizeToFit() + } +} diff --git a/Example/PinLayoutSample/UI/Examples/SafeArea/Subviews/RoundedButton.swift b/Example/PinLayoutSample/UI/Examples/SafeArea/Subviews/RoundedButton.swift index 927345ac..89f99836 100644 --- a/Example/PinLayoutSample/UI/Examples/SafeArea/Subviews/RoundedButton.swift +++ b/Example/PinLayoutSample/UI/Examples/SafeArea/Subviews/RoundedButton.swift @@ -21,8 +21,8 @@ import UIKit import PinLayout class RoundedButton: UIButton { - fileprivate let iconImageView = UIImageView() - fileprivate let label = UILabel() + private let iconImageView = UIImageView() + private let label = UILabel() override var isHighlighted: Bool { didSet { diff --git a/Example/PinLayoutSample/UI/Examples/TableViewExample/Cells/MethodCell.swift b/Example/PinLayoutSample/UI/Examples/TableViewExample/Cells/MethodCell.swift index 0ec1757c..27fd5834 100644 --- a/Example/PinLayoutSample/UI/Examples/TableViewExample/Cells/MethodCell.swift +++ b/Example/PinLayoutSample/UI/Examples/TableViewExample/Cells/MethodCell.swift @@ -23,12 +23,12 @@ import PinLayout class MethodCell: UITableViewCell { static let reuseIdentifier = "MethodCell" - fileprivate let iconImageView = UIImageView(image: UIImage(named: "method")) - fileprivate let nameLabel = UILabel() - fileprivate let descriptionLabel = UILabel() - fileprivate let padding: CGFloat = 10 + private let iconImageView = UIImageView(image: UIImage(named: "method")) + private let nameLabel = UILabel() + private let descriptionLabel = UILabel() + private let padding: CGFloat = 10 - override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none @@ -59,9 +59,9 @@ class MethodCell: UITableViewCell { layout() } - fileprivate func layout() { - iconImageView.pin.top().left().size(30).margin(padding) - nameLabel.pin.right(of: iconImageView, aligned: .center).right().marginHorizontal(padding).sizeToFit(.width) + private func layout() { + iconImageView.pin.topLeft(padding).size(30) + nameLabel.pin.after(of: iconImageView, aligned: .center).right().marginHorizontal(padding).sizeToFit(.width) descriptionLabel.pin.below(of: [iconImageView, nameLabel]).horizontally().margin(padding).sizeToFit(.width) } diff --git a/Example/PinLayoutSample/UI/Examples/TableViewExample/Cells/MethodGroupHeader.swift b/Example/PinLayoutSample/UI/Examples/TableViewExample/Cells/MethodGroupHeader.swift index 5dd3d036..dbb65b69 100644 --- a/Example/PinLayoutSample/UI/Examples/TableViewExample/Cells/MethodGroupHeader.swift +++ b/Example/PinLayoutSample/UI/Examples/TableViewExample/Cells/MethodGroupHeader.swift @@ -23,7 +23,7 @@ class MethodGroupHeader: UITableViewHeaderFooterView { static let reuseIdentifier = "MethodGroupHeader" static let height: CGFloat = 50 - fileprivate let titleLabel = UILabel() + private let titleLabel = UILabel() override init(reuseIdentifier: String?) { super.init(reuseIdentifier: reuseIdentifier) @@ -35,14 +35,14 @@ class MethodGroupHeader: UITableViewHeaderFooterView { required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + func configure(title: String) { titleLabel.text = title } - + override func layoutSubviews() { super.layoutSubviews() - - titleLabel.pin.horizontally().vCenter().margin(10).sizeToFit(.width) + + titleLabel.pin.horizontally(10).vCenter().sizeToFit(.width) } } diff --git a/Example/PinLayoutSample/UI/Examples/TableViewExample/TableViewExampleView.swift b/Example/PinLayoutSample/UI/Examples/TableViewExample/TableViewExampleView.swift index b0124ccd..8ff1a3ff 100644 --- a/Example/PinLayoutSample/UI/Examples/TableViewExample/TableViewExampleView.swift +++ b/Example/PinLayoutSample/UI/Examples/TableViewExample/TableViewExampleView.swift @@ -21,10 +21,10 @@ import UIKit class TableViewExampleView: UIView { - fileprivate let tableView = UITableView() - fileprivate let methodCellTemplate = MethodCell() + private let tableView = UITableView() + private let methodCellTemplate = MethodCell() - fileprivate var methods: [Method] = [] + private var methods: [Method] = [] init() { super.init(frame: .zero) @@ -81,6 +81,6 @@ extension TableViewExampleView: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { // The UITableView will call the cell's sizeThatFit() method to compute the height. // WANRING: You must also set the UITableView.estimatedRowHeight for this to work. - return UITableViewAutomaticDimension + return UITableView.automaticDimension } } diff --git a/Example/PinLayoutSample/UI/Examples/TableViewExample/TableViewExampleViewController.swift b/Example/PinLayoutSample/UI/Examples/TableViewExample/TableViewExampleViewController.swift index 628bd259..e97f625f 100644 --- a/Example/PinLayoutSample/UI/Examples/TableViewExample/TableViewExampleViewController.swift +++ b/Example/PinLayoutSample/UI/Examples/TableViewExample/TableViewExampleViewController.swift @@ -25,14 +25,14 @@ struct Method { } class TableViewExampleViewController: UIViewController { - fileprivate var mainView: TableViewExampleView { + private var mainView: TableViewExampleView { return self.view as! TableViewExampleView } init(pageType: PageType) { super.init(nibName: nil, bundle: nil) - title = pageType.text + title = pageType.title } required init?(coder aDecoder: NSCoder) { diff --git a/Example/PinLayoutSample/UI/Examples/TableViewReadableContent/Cells/MethodReadableInsetsCell.swift b/Example/PinLayoutSample/UI/Examples/TableViewReadableContent/Cells/MethodReadableInsetsCell.swift new file mode 100644 index 00000000..8f957ce9 --- /dev/null +++ b/Example/PinLayoutSample/UI/Examples/TableViewReadableContent/Cells/MethodReadableInsetsCell.swift @@ -0,0 +1,66 @@ +import Foundation + +class MethodReadableInsetsCell: UITableViewCell { + static let reuseIdentifier = "MethodReadableInsetsCell" + + private let iconImageView = UIImageView(image: UIImage(named: "method")) + private let nameLabel = UILabel() + private let descriptionLabel = UILabel() + private let margin: CGFloat = 10 + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + selectionStyle = .none + separatorInset = .zero + preservesSuperviewLayoutMargins = false + + contentView.addSubview(iconImageView) + + nameLabel.font = UIFont.boldSystemFont(ofSize: 14) + nameLabel.lineBreakMode = .byTruncatingTail + contentView.addSubview(nameLabel) + + descriptionLabel.font = UIFont.systemFont(ofSize: 12) + descriptionLabel.numberOfLines = 0 + contentView.addSubview(descriptionLabel) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + func configure(method: Method) { + nameLabel.text = method.name + descriptionLabel.text = method.description + } + + override func layoutSubviews() { + super.layoutSubviews() + layout() + } + + private func layout() { + // 1) Layout the contentView + contentView.pin.top(margin).horizontally(pin.readableMargins) + + // 2) Layout contentView's childs + iconImageView.pin.topLeft().size(30) + nameLabel.pin.after(of: iconImageView, aligned: .center).right().marginHorizontal(margin).sizeToFit(.width) + descriptionLabel.pin.below(of: [iconImageView, nameLabel]).horizontally().marginTop(margin).sizeToFit(.width) + + // 3) Adjust the contentView size to wrap its childs + contentView.pin.wrapContent(.vertically) + } + + override func sizeThatFits(_ size: CGSize) -> CGSize { + // 1) Set the width to the specified size parameter + pin.width(size.width) + + // 2) Layout the contentView's controls + layout() + + // 3) Returns a size that contains all views + return CGSize(width: size.width, height: contentView.frame.maxY + margin) + } +} diff --git a/Example/PinLayoutSample/UI/Examples/TableViewReadableContent/Cells/MethodReadableInsetsGroupHeader.swift b/Example/PinLayoutSample/UI/Examples/TableViewReadableContent/Cells/MethodReadableInsetsGroupHeader.swift new file mode 100644 index 00000000..3af4d16d --- /dev/null +++ b/Example/PinLayoutSample/UI/Examples/TableViewReadableContent/Cells/MethodReadableInsetsGroupHeader.swift @@ -0,0 +1,51 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +class MethodReadableInsetsGroupHeader: UITableViewHeaderFooterView { + static let reuseIdentifier = "MethodReadableInsetsGroupHeader" + static let height: CGFloat = 50 + + fileprivate let titleLabel = UILabel() + + override init(reuseIdentifier: String?) { + super.init(reuseIdentifier: reuseIdentifier) + + preservesSuperviewLayoutMargins = false + + titleLabel.font = .systemFont(ofSize: 20) + contentView.addSubview(titleLabel) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func configure(title: String) { + titleLabel.text = title + } + + override func layoutSubviews() { + super.layoutSubviews() + + contentView.pin.horizontally(pin.readableMargins) + titleLabel.pin.horizontally().vCenter().sizeToFit(.width) + } +} diff --git a/Example/PinLayoutSample/UI/Examples/TableViewReadableContent/TableViewReadableContentView.swift b/Example/PinLayoutSample/UI/Examples/TableViewReadableContent/TableViewReadableContentView.swift new file mode 100644 index 00000000..34fcd940 --- /dev/null +++ b/Example/PinLayoutSample/UI/Examples/TableViewReadableContent/TableViewReadableContentView.swift @@ -0,0 +1,85 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +class TableViewReadableContentView: UIView { + + fileprivate let tableView = UITableView() + fileprivate let methodCellTemplate = MethodReadableInsetsCell() + fileprivate var methods: [Method] = [] + + init() { + super.init(frame: .zero) + backgroundColor = .white + + tableView.dataSource = self + tableView.delegate = self + tableView.tableFooterView = UIView() + tableView.register(MethodReadableInsetsCell.self, forCellReuseIdentifier: MethodReadableInsetsCell.reuseIdentifier) + tableView.register(MethodReadableInsetsGroupHeader.self, forHeaderFooterViewReuseIdentifier: MethodReadableInsetsGroupHeader.reuseIdentifier) + addSubview(tableView) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + func configure(methods: [Method]) { + self.methods = methods + tableView.reloadData() + } + + override func layoutSubviews() { + super.layoutSubviews() + + tableView.pin.all() + } +} + +// MARK: UITableViewDataSource, UITableViewDelegate +extension TableViewReadableContentView: UITableViewDataSource, UITableViewDelegate { + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return MethodReadableInsetsGroupHeader.height + } + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: MethodReadableInsetsGroupHeader.reuseIdentifier) as! MethodReadableInsetsGroupHeader + header.configure(title: "PinLayout few methods") + return header + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return methods.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: MethodReadableInsetsCell.reuseIdentifier, for: indexPath) as! MethodReadableInsetsCell + cell.configure(method: methods[indexPath.row]) + + return cell + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + // WARNING: + // Configure a MethodReadableInsetsCell and ask its size. + methodCellTemplate.configure(method: methods[indexPath.row]) + return methodCellTemplate.sizeThatFits(CGSize(width: tableView.bounds.width, height: .greatestFiniteMagnitude)).height + } +} diff --git a/Example/PinLayoutSample/UI/Examples/TableViewReadableContent/TableViewReadableContentViewController.swift b/Example/PinLayoutSample/UI/Examples/TableViewReadableContent/TableViewReadableContentViewController.swift new file mode 100644 index 00000000..2a878916 --- /dev/null +++ b/Example/PinLayoutSample/UI/Examples/TableViewReadableContent/TableViewReadableContentViewController.swift @@ -0,0 +1,55 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +class TableViewReadableContentViewController: UIViewController { + fileprivate var mainView: TableViewReadableContentView { + return self.view as! TableViewReadableContentView + } + + init(pageType: PageType) { + super.init(nibName: nil, bundle: nil) + + title = pageType.title + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override func loadView() { + view = TableViewReadableContentView() + mainView.configure(methods: [ + Method(name: "top(_ value: CGFloat)", description: "The value specifies the top edge distance from the superview's top edge in pixels."), + Method(name: "top(_ percent: Percent)", description: "The value specifies the top edge distance from the superview's top edge in percentage of its superview's height."), + Method(name: "vCenter(_ value: CGFloat)", description: "The value specifies the distance vertically of the view's center related to the superview's center in pixels."), + Method(name: "vCenter(_ percent: Percent)", description: "The value specifies the distance vertically of the view's center related to the superview's center in percentage of its superview's height."), + Method(name: "bottom(_ value: CGFloat)", description: "The value specifies the bottom edge distance from the superview's bottom edge in pixels."), + Method(name: "bottom(_ percent: Percent)", description: "The value specifies the bottom edge distance from the superview's bottom edge in percentage of its superview's height."), + + Method(name: "left(_ value: CGFloat)", description: "The value specifies the left edge distance from the superview's left edge in pixels. The value specifies the left edge distance from the superview's left edge in pixels."), + Method(name: "left(_ percent: Percent)", description: "The value specifies the left edge distance from the superview's left edge in percentage of its superview's width."), + Method(name: "hCenter(_ value: CGFloat)", description: "The value specifies the distance horizontally of the view's center related to the superview's center in pixels."), + Method(name: "hCenter(_ percent: Percent)", description: "The value specifies the distance horizontally of the view's center related to the superview's center in percentage of its superview's height."), + Method(name: "right(_ value: CGFloat)", description: "The value specifies the right edge distance from the superview's right edge in pixels."), + Method(name: "right(_ percent: Percent)", description: "The value specifies the right edge distance from the superview's right edge in percentage of its superview's width.") + ]) + } +} diff --git a/Example/PinLayoutSample/UI/Examples/WrapContent/WrapContentView.swift b/Example/PinLayoutSample/UI/Examples/WrapContent/WrapContentView.swift new file mode 100644 index 00000000..80d6c5c9 --- /dev/null +++ b/Example/PinLayoutSample/UI/Examples/WrapContent/WrapContentView.swift @@ -0,0 +1,154 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit +import PinLayout + +class WrapContentView: UIView { + private let scrollView = UIScrollView() + + private let topContainer = UIView() + private let topLogo = UIImageView(image: UIImage(named: "PinLayout-logo")) + private let topLabel = UILabel() + + private let separatorView1 = UIView() + + private let middleContainer = UIView() + private let middleLabel = UILabel() + private let button1 = UIButton(type: .custom) + private let button2 = UIButton(type: .custom) + private let button3 = UIButton(type: .custom) + + private let separatorView2 = UIView() + + private let bottomContainer = UIView() + private let bottomLogo = UIImageView(image: UIImage(named: "PinLayout-logo")) + private let bottomLabel = UILabel() + + init() { + super.init(frame: .zero) + backgroundColor = .white + + addSubview(scrollView) + + topContainer.backgroundColor = .lightColor + scrollView.addSubview(topContainer) + + // + // Top section + // + topLogo.contentMode = .scaleAspectFit + topContainer.addSubview(topLogo) + + configureLabel(topLabel, text: "This view use 'pin.wrapContent()' to wrap its subviews (logo and label). This view horizontally center at the top.") + topContainer.addSubview(topLabel) + + // Separator 1 + separatorView1.pin.height(1) + separatorView1.backgroundColor = .pinLayoutColor + scrollView.addSubview(separatorView1) + + // + // Middle section + // + middleContainer.backgroundColor = .lightColor + scrollView.addSubview(middleContainer) + + configureLabel(middleLabel, text: "This view use 'pin.wrapContent()' to wrap its subviews (1 label and 3 buttons). This view is horizontally centered.") + middleContainer.addSubview(middleLabel) + + button1.setTitle("Button 1", for: .normal) + button1.setTitleColor(.black, for: .normal) + button1.sizeToFit() + middleContainer.addSubview(button1) + + button2.setTitle("Button 2", for: .normal) + button2.setTitleColor(.black, for: .normal) + button2.sizeToFit() + middleContainer.addSubview(button2) + + button3.setTitle("Button 3", for: .normal) + button3.setTitleColor(.black, for: .normal) + button3.sizeToFit() + middleContainer.addSubview(button3) + + // Separator 2 + separatorView2.pin.height(1) + separatorView2.backgroundColor = .pinLayoutColor + if #available(iOS 11.0, *) { + scrollView.contentInsetAdjustmentBehavior = .never + } + scrollView.addSubview(separatorView2) + + // + // Bottom section + // + bottomContainer.backgroundColor = .lightColor + scrollView.addSubview(bottomContainer) + + bottomLogo.contentMode = .scaleAspectFit + bottomContainer.addSubview(bottomLogo) + + configureLabel(bottomLabel, text: "This view use 'pin.wrapContent()' to wrap its subviews (logo and label). This view is horizontally centered. This view is vertically center in the region between the separator and the bottom of the screen using '.align(.center)'") + bottomContainer.addSubview(bottomLabel) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override func layoutSubviews() { + super.layoutSubviews() + let padding: CGFloat = 10 + + scrollView.pin.all() + + // Top section + topLogo.pin.width(100).aspectRatio() + topLabel.pin.below(of: topLogo, aligned: .center).width(260).sizeToFit(.width).marginTop(padding) + topContainer.pin.wrapContent(padding: 4).top(pin.safeArea.top + padding).hCenter() + + // Separator1 + separatorView1.pin.below(of: topContainer, aligned: .center).width(80%).marginTop(padding) + + middleLabel.pin.width(260).sizeToFit(.width) + button2.pin.below(of: middleLabel, aligned: .center).marginTop(padding) + button1.pin.before(of: button2, aligned: .center).marginRight(padding) + button3.pin.after(of: button2, aligned: .center).marginLeft(padding) + middleContainer.pin.wrapContent(padding: 4).below(of: separatorView1).hCenter().marginTop(padding) + + // Separator2 + separatorView2.pin.below(of: middleContainer, aligned: .center).width(80%).marginTop(padding) + + // Bottom section + bottomLogo.pin.width(50).aspectRatio() + bottomLabel.pin.after(of: bottomLogo, aligned: .top).width(200).marginLeft(10).sizeToFit(.width) + bottomContainer.pin.wrapContent(padding: 30).below(of: separatorView2).hCenter().marginTop(padding) + + scrollView.contentSize = CGSize(width: frame.width, height: bottomContainer.frame.maxY) + } + + private func configureLabel(_ label: UILabel, text: String) { + label.text = text + label.font = .systemFont(ofSize: 14) + label.textAlignment = .center + label.numberOfLines = 0 + label.lineBreakMode = .byWordWrapping + } +} diff --git a/Example/PinLayoutSample/UI/Examples/WrapContent/WrapContentViewController.swift b/Example/PinLayoutSample/UI/Examples/WrapContent/WrapContentViewController.swift new file mode 100644 index 00000000..0664d9b7 --- /dev/null +++ b/Example/PinLayoutSample/UI/Examples/WrapContent/WrapContentViewController.swift @@ -0,0 +1,40 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +class WrapContentViewController: UIViewController { + private var mainView: WrapContentView { + return self.view as! WrapContentView + } + + init(pageType: PageType) { + super.init(nibName: nil, bundle: nil) + + title = pageType.title + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override func loadView() { + view = WrapContentView() + } +} diff --git a/Example/PinLayoutSample/UI/Menu/MenuView.swift b/Example/PinLayoutSample/UI/Menu/MenuView.swift index 441786c6..5311462f 100644 --- a/Example/PinLayoutSample/UI/Menu/MenuView.swift +++ b/Example/PinLayoutSample/UI/Menu/MenuView.swift @@ -20,15 +20,15 @@ import UIKit import PinLayout -protocol MenuViewDelegate: class { +protocol MenuViewDelegate: AnyObject { func didSelect(pageType: PageType) } class MenuView: UIView { weak var delegate: MenuViewDelegate? - fileprivate let tableView = UITableView() - fileprivate let cellIdentifier = "MenuViewCell" + private let tableView = UITableView() + private let cellIdentifier = "MenuViewCell" init() { super.init(frame: .zero) @@ -61,7 +61,7 @@ extension MenuView: UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) - cell.textLabel?.text = PageType(rawValue: indexPath.row)?.text ?? "PinLayout Example" + cell.textLabel?.text = PageType(rawValue: indexPath.row)?.title ?? "PinLayout Example" cell.textLabel?.font = .systemFont(ofSize: 12) return cell } @@ -76,3 +76,14 @@ extension MenuView: UITableViewDelegate { tableView.deselectRow(at: indexPath, animated: true) } } + +// MARK: PinLayout validation +extension MenuView { + private func validation() { + // Compilation validation +#if compiler(>=5.5) // Xcode 13+ + // iOS 15+ + _ = tableView.pin.keyboardArea +#endif + } +} diff --git a/Example/PinLayoutSample/UI/Menu/MenuViewController.swift b/Example/PinLayoutSample/UI/Menu/MenuViewController.swift index c47e6861..3ed89167 100644 --- a/Example/PinLayoutSample/UI/Menu/MenuViewController.swift +++ b/Example/PinLayoutSample/UI/Menu/MenuViewController.swift @@ -24,28 +24,36 @@ enum PageType: Int { case adjustToContainer case tableView case collectionView + case animations + case autoAdjustingSize case safeArea - case form case relativePositions - case multiRelativePositions - case autoAdjustingSize + case between + case form + case wrapContent + case autoSizing + case tableViewWithReadable case introRTL case introObjC case count - var text: String { + var title: String { switch self { - case .intro: return "Introduction example" - case .adjustToContainer: return "Adjust to container size" - case .tableView: return "UITableView with variable cell's height" + case .intro: return "Introduction Example" + case .adjustToContainer: return "Adjust to Container Size" + case .tableView: return "UITableView with Variable Cell's Height" case .collectionView: return "UICollectionView Example" - case .safeArea: return "SafeArea" + case .animations: return "Animation Example" + case .autoAdjustingSize: return "Auto Adjusting Size" + case .safeArea: return "SafeArea & readableMargins" + case .relativePositions: return "Relative Positioning" + case .between: return "Between Example" case .form: return "Form Example" - case .relativePositions: return "Relative Positionning" - case .multiRelativePositions: return "Multiple Relatives Positionning" - case .autoAdjustingSize: return "Auto adjusting size" - case .introRTL: return "PinLayout's right-to-left language support" + case .wrapContent: return "wrapContent Example" + case .autoSizing: return "Auto Sizing" + case .tableViewWithReadable: return "UITableView using readableMargins" + case .introRTL: return "Right-to-left Language Support" case .introObjC: return "Objective-C PinLayout Example" case .count: return "" } @@ -63,16 +71,25 @@ enum PageType: Int { return CollectionViewExampleViewController(pageType: self) case .safeArea: let tabbarController = UITabBarController() - tabbarController.setViewControllers([SafeAreaViewController(), SafeAreaCornersViewController()], animated: false) + tabbarController.title = self.title + tabbarController.setViewControllers([SafeAreaViewController(), SafeAreaAndMarginsViewController()], animated: false) return tabbarController - case .form: - return FormViewController(pageType: self) - case .relativePositions: - return RelativeViewController(pageType: self) - case .multiRelativePositions: - return MultiRelativeViewController(pageType: self) + case .animations: + return AnimationsViewController(pageType: self) case .autoAdjustingSize: return AutoAdjustingSizeViewController(pageType: self) + case .relativePositions: + return RelativeViewController(pageType: self) + case .between: + return BetweenViewController(pageType: self) + case .form: + return FormViewController(pageType: self) + case .wrapContent: + return WrapContentViewController(pageType: self) + case .autoSizing: + return AutoSizingViewController() + case .tableViewWithReadable: + return TableViewReadableContentViewController(pageType: self) case .introRTL: return IntroRTLViewController(pageType: self) case .introObjC: @@ -84,13 +101,15 @@ enum PageType: Int { } class MenuViewController: UIViewController { - fileprivate var mainView: MenuView { + private var mainView: MenuView { return self.view as! MenuView } init() { super.init(nibName: nil, bundle: nil) title = "PinLayout Examples" + + navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) } required init(coder aDecoder: NSCoder) { @@ -104,7 +123,7 @@ class MenuViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(true) -// didSelect(pageType: .safeArea) +// didSelect(pageType: .intro) } } diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 2450b525..00000000 --- a/Gemfile +++ /dev/null @@ -1,6 +0,0 @@ -source 'https://rubygems.org' -gem 'synx' -gem 'cocoapods', '~> 1.5.0' -gem 'jazzy', '0.9' -gem 'fastlane' -gem 'xcpretty-travis-formatter' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 0982da22..00000000 --- a/Gemfile.lock +++ /dev/null @@ -1,239 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - CFPropertyList (3.0.0) - activesupport (4.2.10) - i18n (~> 0.7) - minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) - tzinfo (~> 1.1) - addressable (2.5.2) - public_suffix (>= 2.0.2, < 4.0) - atomos (0.1.2) - babosa (1.0.2) - claide (1.0.2) - clamp (0.6.5) - cocoapods (1.5.0) - activesupport (>= 4.0.2, < 5) - claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.5.0) - cocoapods-deintegrate (>= 1.0.2, < 2.0) - cocoapods-downloader (>= 1.2.0, < 2.0) - cocoapods-plugins (>= 1.0.0, < 2.0) - cocoapods-search (>= 1.0.0, < 2.0) - cocoapods-stats (>= 1.0.0, < 2.0) - cocoapods-trunk (>= 1.3.0, < 2.0) - cocoapods-try (>= 1.1.0, < 2.0) - colored2 (~> 3.1) - escape (~> 0.0.4) - fourflusher (~> 2.0.1) - gh_inspector (~> 1.0) - molinillo (~> 0.6.5) - nap (~> 1.0) - ruby-macho (~> 1.1) - xcodeproj (>= 1.5.7, < 2.0) - cocoapods-core (1.5.0) - activesupport (>= 4.0.2, < 6) - fuzzy_match (~> 2.0.4) - nap (~> 1.0) - cocoapods-deintegrate (1.0.2) - cocoapods-downloader (1.2.0) - cocoapods-plugins (1.0.0) - nap - cocoapods-search (1.0.0) - cocoapods-stats (1.0.0) - cocoapods-trunk (1.3.0) - nap (>= 0.8, < 2.0) - netrc (~> 0.11) - cocoapods-try (1.1.0) - colored (1.2) - colored2 (3.1.2) - colorize (0.8.1) - commander-fastlane (4.4.6) - highline (~> 1.7.2) - concurrent-ruby (1.0.5) - declarative (0.0.10) - declarative-option (0.1.0) - domain_name (0.5.20170404) - unf (>= 0.0.5, < 1.0.0) - dotenv (2.2.2) - emoji_regex (0.1.1) - escape (0.0.4) - excon (0.62.0) - faraday (0.14.0) - multipart-post (>= 1.2, < 3) - faraday-cookie_jar (0.0.6) - faraday (>= 0.7.4) - http-cookie (~> 1.0.0) - faraday_middleware (0.12.2) - faraday (>= 0.7.4, < 1.0) - fastimage (2.1.1) - fastlane (2.91.0) - CFPropertyList (>= 2.3, < 4.0.0) - addressable (>= 2.3, < 3.0.0) - babosa (>= 1.0.2, < 2.0.0) - bundler (>= 1.12.0, < 2.0.0) - colored - commander-fastlane (>= 4.4.6, < 5.0.0) - dotenv (>= 2.1.1, < 3.0.0) - emoji_regex (~> 0.1) - excon (>= 0.45.0, < 1.0.0) - faraday (~> 0.9) - faraday-cookie_jar (~> 0.0.6) - faraday_middleware (~> 0.9) - fastimage (>= 2.1.0, < 3.0.0) - gh_inspector (>= 1.1.2, < 2.0.0) - google-api-client (>= 0.13.1, < 0.14.0) - highline (>= 1.7.2, < 2.0.0) - json (< 3.0.0) - mini_magick (~> 4.5.1) - multi_json - multi_xml (~> 0.5) - multipart-post (~> 2.0.0) - plist (>= 3.1.0, < 4.0.0) - public_suffix (~> 2.0.0) - rubyzip (>= 1.1.0, < 2.0.0) - security (= 0.1.3) - simctl (~> 1.6.3) - slack-notifier (>= 2.0.0, < 3.0.0) - terminal-notifier (>= 1.6.2, < 2.0.0) - terminal-table (>= 1.4.5, < 2.0.0) - tty-screen (>= 0.6.3, < 1.0.0) - tty-spinner (>= 0.8.0, < 1.0.0) - word_wrap (~> 1.0.0) - xcodeproj (>= 1.5.7, < 2.0.0) - xcpretty (>= 0.2.4, < 1.0.0) - xcpretty-travis-formatter (>= 0.0.3) - ffi (1.9.18) - fourflusher (2.0.1) - fuzzy_match (2.0.4) - gh_inspector (1.1.3) - google-api-client (0.13.6) - addressable (~> 2.5, >= 2.5.1) - googleauth (~> 0.5) - httpclient (>= 2.8.1, < 3.0) - mime-types (~> 3.0) - representable (~> 3.0) - retriable (>= 2.0, < 4.0) - googleauth (0.6.2) - faraday (~> 0.12) - jwt (>= 1.4, < 3.0) - logging (~> 2.0) - memoist (~> 0.12) - multi_json (~> 1.11) - os (~> 0.9) - signet (~> 0.7) - highline (1.7.10) - http-cookie (1.0.3) - domain_name (~> 0.5) - httpclient (2.8.3) - i18n (0.9.5) - concurrent-ruby (~> 1.0) - jazzy (0.9.0) - cocoapods (~> 1.0) - mustache (~> 0.99) - open4 - redcarpet (~> 3.2) - rouge (~> 1.5) - sass (~> 3.4) - sqlite3 (~> 1.3) - xcinvoke (~> 0.3.0) - json (2.1.0) - jwt (2.1.0) - liferaft (0.0.6) - little-plugger (1.1.4) - logging (2.2.2) - little-plugger (~> 1.1) - multi_json (~> 1.10) - memoist (0.16.0) - mime-types (3.1) - mime-types-data (~> 3.2015) - mime-types-data (3.2016.0521) - mini_magick (4.5.1) - minitest (5.11.3) - molinillo (0.6.5) - multi_json (1.13.1) - multi_xml (0.6.0) - multipart-post (2.0.0) - mustache (0.99.8) - nanaimo (0.2.5) - nap (1.1.0) - naturally (2.1.0) - netrc (0.11.0) - open4 (1.3.4) - os (0.9.6) - plist (3.4.0) - public_suffix (2.0.5) - rb-fsevent (0.10.2) - rb-inotify (0.9.10) - ffi (>= 0.5.0, < 2) - redcarpet (3.4.0) - representable (3.0.4) - declarative (< 0.1.0) - declarative-option (< 0.2.0) - uber (< 0.2.0) - retriable (3.1.1) - rouge (1.11.1) - ruby-macho (1.1.0) - rubyzip (1.2.1) - sass (3.5.3) - sass-listen (~> 4.0.0) - sass-listen (4.0.0) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - security (0.1.3) - signet (0.8.1) - addressable (~> 2.3) - faraday (~> 0.9) - jwt (>= 1.5, < 3.0) - multi_json (~> 1.10) - simctl (1.6.4) - CFPropertyList - naturally - slack-notifier (2.3.2) - sqlite3 (1.3.13) - synx (0.2.1) - clamp (~> 0.6) - colorize (~> 0.7) - xcodeproj (~> 1.0) - terminal-notifier (1.8.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) - thread_safe (0.3.6) - tty-cursor (0.5.0) - tty-screen (0.6.4) - tty-spinner (0.8.0) - tty-cursor (>= 0.5.0) - tzinfo (1.2.5) - thread_safe (~> 0.1) - uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.7.5) - unicode-display_width (1.3.0) - word_wrap (1.0.0) - xcinvoke (0.3.0) - liferaft (~> 0.0.6) - xcodeproj (1.5.7) - CFPropertyList (>= 2.3.3, < 4.0) - atomos (~> 0.1.2) - claide (>= 1.0.2, < 2.0) - colored2 (~> 3.1) - nanaimo (~> 0.2.4) - xcpretty (0.2.6) - rouge (~> 1.8) - xcpretty-travis-formatter (1.0.0) - xcpretty (~> 0.2, >= 0.0.7) - -PLATFORMS - ruby - -DEPENDENCIES - cocoapods (~> 1.5.0) - fastlane - jazzy (= 0.9) - synx - xcpretty-travis-formatter - -BUNDLED WITH - 1.16.1 diff --git a/LICENSE b/LICENSE index ca156ec7..ad678573 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Luc Dion +Copyright (c) 2017-2018 Luc Dion Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Package.swift b/Package.swift index f2ebc857..cf97e75a 100644 --- a/Package.swift +++ b/Package.swift @@ -1,8 +1,19 @@ -// swift-tools-version:3.1 +// swift-tools-version:5.4 import PackageDescription let package = Package( name: "PinLayout", - exclude: ["Tests"] + products: [ + .library(name: "PinLayout", targets: ["PinLayout"]) + ], + targets: [ + .target( + name: "PinLayout", + path: "Sources", + exclude: [ + "SupportingFiles/Info.plist" + ] + ) + ] ) diff --git a/PinLayout.podspec b/PinLayout.podspec index a1d570cd..d1b62dd3 100644 --- a/PinLayout.podspec +++ b/PinLayout.podspec @@ -8,22 +8,22 @@ Pod::Spec.new do |spec| spec.name = "PinLayout" - spec.version = "1.7.3" - spec.summary = "Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast. [iOS/macOS/tvOS]" - spec.description = "Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainable. [iOS/macOS/tvOS]" - spec.homepage = "https://mirego.github.io/PinLayout/" + spec.version = "1.10.6" + spec.summary = "Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast." + spec.description = "Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainable. [iOS/macOS/tvOS/CALayer]" + spec.homepage = "https://github.com/layoutBox/PinLayout" spec.license = "MIT license" spec.author = { "Luc Dion" => "luc_dion@yahoo.com" } - spec.source = { :git => "https://github.com/mirego/PinLayout.git", :tag => "#{spec.version}" } + spec.source = { :git => "https://github.com/layoutBox/PinLayout.git", :tag => "#{spec.version}" } spec.source_files = "Sources/**/*.swift" - spec.swift_version = '4.0' + spec.swift_versions = ['4.2', '5.7'] - spec.ios.deployment_target = '8.0' + spec.ios.deployment_target = '12.0' spec.ios.frameworks = 'Foundation', 'CoreGraphics', 'UIKit' - spec.tvos.deployment_target = '9.0' + spec.tvos.deployment_target = '12.0' spec.tvos.frameworks = 'Foundation', 'CoreGraphics', 'UIKit' - spec.osx.deployment_target = '10.9' + spec.osx.deployment_target = '10.13' spec.osx.frameworks = 'Foundation', 'CoreGraphics', 'AppKit' end diff --git a/PinLayout.xcodeproj/project.pbxproj b/PinLayout.xcodeproj/project.pbxproj index d82f6417..47587734 100644 --- a/PinLayout.xcodeproj/project.pbxproj +++ b/PinLayout.xcodeproj/project.pbxproj @@ -7,25 +7,15 @@ objects = { /* Begin PBXBuildFile section */ - 0862040479C5CDB1087DD5BE /* Pods_PinLayoutTests_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B7A69A7C17E9071CF457C39A /* Pods_PinLayoutTests_tvOS.framework */; }; - 111A0F2DFE45A60621382616 /* Pods_PinLayoutTests_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D973E3CEE5E4BD40167AFED /* Pods_PinLayoutTests_macOS.framework */; }; 240F88BE1F0C066800280FC8 /* JustifyAlignSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 240F88BC1F0C042500280FC8 /* JustifyAlignSpec.swift */; }; 240F88C11F0C1F5000280FC8 /* WarningSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 240F88BF1F0C1ED900280FC8 /* WarningSpec.swift */; }; 241A277D1F8E958F00B1AD39 /* PinLayoutObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241A277B1F8E958F00B1AD39 /* PinLayoutObjC.swift */; }; - 241A277E1F8E958F00B1AD39 /* PinLayoutObjCImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241A277C1F8E958F00B1AD39 /* PinLayoutObjCImpl.swift */; }; 242723731F008BF7006A5C3A /* MinMaxWidthHeightSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 242723711F008B85006A5C3A /* MinMaxWidthHeightSpec.swift */; }; 242E8DC31EED5AB2005935FB /* RelativePositionMultipleViewsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 242E8DC11EED5982005935FB /* RelativePositionMultipleViewsSpec.swift */; }; 243B12BC1FC393580072A9C3 /* Percent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243C620E1FC3834B0082C327 /* Percent.swift */; }; 243B12C81FC3D06F0072A9C3 /* LayoutMethodSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243B12C41FC3CFC10072A9C3 /* LayoutMethodSpec.swift */; }; - 243B12CB1FC469D00072A9C3 /* ObjectiveCSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 243B12C91FC469550072A9C3 /* ObjectiveCSpec.m */; }; - 243C62041FC37F680082C327 /* PinLayoutImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C61FC37C1C0054CADD /* PinLayoutImpl.swift */; }; - 243C62051FC37F6C0082C327 /* PinLayoutImpl+Coordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C71FC37C1C0054CADD /* PinLayoutImpl+Coordinates.swift */; }; - 243C62061FC37F6C0082C327 /* PinLayoutImpl+Layouting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C41FC37A900054CADD /* PinLayoutImpl+Layouting.swift */; }; - 243C62071FC37F6C0082C327 /* PinLayoutImpl+Relative.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C91FC37C1C0054CADD /* PinLayoutImpl+Relative.swift */; }; - 243C62081FC37F6C0082C327 /* PinLayoutImpl+Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C81FC37C1C0054CADD /* PinLayoutImpl+Warning.swift */; }; 243C62091FC37F6C0082C327 /* Coordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6D21FC37CD40054CADD /* Coordinates.swift */; }; 243C620A1FC37F6C0082C327 /* TypesImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6CE1FC37C570054CADD /* TypesImpl.swift */; }; - 243C620B1FC37F6C0082C327 /* UIView+LTR.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6D01FC37C8C0054CADD /* UIView+LTR.swift */; }; 243C620C1FC37FA30082C327 /* PinLayoutTVOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 2475B6D71FC37D4D0054CADD /* PinLayoutTVOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 243C620F1FC3834B0082C327 /* Percent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243C620E1FC3834B0082C327 /* Percent.swift */; }; 244C6E151E776A0C0074FC74 /* MarginsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 244C6E141E776A0C0074FC74 /* MarginsSpec.swift */; }; @@ -35,48 +25,87 @@ 2469C5021E75D88500073BEE /* BasicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2469C5011E75D88500073BEE /* BasicView.swift */; }; 2469C5041E75DB7600073BEE /* RectNimbleMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2469C5031E75DB7600073BEE /* RectNimbleMatcher.swift */; }; 246D36481E6C46F50050F202 /* PinPointCoordinatesSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 246D36471E6C46F50050F202 /* PinPointCoordinatesSpec.swift */; }; - 2475B6C51FC37A900054CADD /* PinLayoutImpl+Layouting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C41FC37A900054CADD /* PinLayoutImpl+Layouting.swift */; }; - 2475B6CA1FC37C1C0054CADD /* PinLayoutImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C61FC37C1C0054CADD /* PinLayoutImpl.swift */; }; - 2475B6CB1FC37C1C0054CADD /* PinLayoutImpl+Coordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C71FC37C1C0054CADD /* PinLayoutImpl+Coordinates.swift */; }; - 2475B6CC1FC37C1C0054CADD /* PinLayoutImpl+Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C81FC37C1C0054CADD /* PinLayoutImpl+Warning.swift */; }; - 2475B6CD1FC37C1C0054CADD /* PinLayoutImpl+Relative.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C91FC37C1C0054CADD /* PinLayoutImpl+Relative.swift */; }; 2475B6CF1FC37C570054CADD /* TypesImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6CE1FC37C570054CADD /* TypesImpl.swift */; }; - 2475B6D11FC37C8C0054CADD /* UIView+LTR.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6D01FC37C8C0054CADD /* UIView+LTR.swift */; }; 2475B6D31FC37CD40054CADD /* Coordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6D21FC37CD40054CADD /* Coordinates.swift */; }; 2475B6DB1FC37D4D0054CADD /* PinLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 2475B6D91FC37D4D0054CADD /* PinLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2482908C1E78CFFC00667D08 /* RelativePositionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2482908B1E78CFFC00667D08 /* RelativePositionSpec.swift */; }; 248E4C741F7A883800C0E7F7 /* AspectRatioTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 248E4C721F7A83FA00C0E7F7 /* AspectRatioTests.swift */; }; 248E4C771F7A88D200C0E7F7 /* UIImage+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 248E4C751F7A88CF00C0E7F7 /* UIImage+Color.swift */; }; - 24949A2E1EF69474003643D3 /* PinLayout+Filters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24949A2D1EF69474003643D3 /* PinLayout+Filters.swift */; }; + 24949A2E1EF69474003643D3 /* Filters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24949A2D1EF69474003643D3 /* Filters.swift */; }; 249EFE841E64FB4C00165E39 /* PinLayout.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 249EFE7A1E64FB4C00165E39 /* PinLayout.framework */; }; - 24B02B091F2A713000C18179 /* PinLayout+Filters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24949A2D1EF69474003643D3 /* PinLayout+Filters.swift */; }; - 24B02B0A1F2A713300C18179 /* PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFC97CA61E8A8F2C001545D5 /* PinLayout.swift */; }; + 24B02B091F2A713000C18179 /* Filters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24949A2D1EF69474003643D3 /* Filters.swift */; }; 24D18D171F3B4381008129EF /* RTLSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24D18D151F3B42E0008129EF /* RTLSpec.swift */; }; 24D18D241F3E37DD008129EF /* Pin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24D18D231F3E37DD008129EF /* Pin.swift */; }; 24D18D261F3E5EA5008129EF /* Pin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24D18D231F3E37DD008129EF /* Pin.swift */; }; - 32A65F57D5D8D5204831FF3A /* Pods_PinLayoutTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 417E52FC81E208F91CF03C1D /* Pods_PinLayoutTests_iOS.framework */; }; - DF1A5CBB208106A900725EF5 /* UIView+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF1A5CBA208106A900725EF5 /* UIView+PinLayout.swift */; }; - DF1A5D0420812DE100725EF5 /* UIView+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF1A5CBA208106A900725EF5 /* UIView+PinLayout.swift */; }; + 733BCEB894DC625445120A4B /* Pods_PinLayoutTests_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 23D84A60C294055979A28086 /* Pods_PinLayoutTests_tvOS.framework */; }; + C80435D320D0891C00EB1BD7 /* SizeCalculable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80435D220D0891C00EB1BD7 /* SizeCalculable.swift */; }; + C80435D520D0898000EB1BD7 /* Layoutable+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80435D420D0898000EB1BD7 /* Layoutable+PinLayout.swift */; }; + C80435D620D08A2B00EB1BD7 /* SizeCalculable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80435D220D0891C00EB1BD7 /* SizeCalculable.swift */; }; + C80435D720D08A2C00EB1BD7 /* SizeCalculable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80435D220D0891C00EB1BD7 /* SizeCalculable.swift */; }; + C80435D820D08B7300EB1BD7 /* Layoutable+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80435D420D0898000EB1BD7 /* Layoutable+PinLayout.swift */; }; + C80435D920D08B7400EB1BD7 /* Layoutable+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80435D420D0898000EB1BD7 /* Layoutable+PinLayout.swift */; }; + C8291E41247A242600E95886 /* AutoSizeCalculable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8291E40247A242600E95886 /* AutoSizeCalculable.swift */; }; + C8291E42247A243900E95886 /* AutoSizeCalculable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8291E40247A242600E95886 /* AutoSizeCalculable.swift */; }; + C8291E43247A243900E95886 /* AutoSizeCalculable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8291E40247A242600E95886 /* AutoSizeCalculable.swift */; }; + C82DC20C20CE9F6800B7ACF5 /* Layoutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C82DC20B20CE9F6800B7ACF5 /* Layoutable.swift */; }; + C82DC20D20CE9F6800B7ACF5 /* Layoutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C82DC20B20CE9F6800B7ACF5 /* Layoutable.swift */; }; + C82DC20E20CE9F6800B7ACF5 /* Layoutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C82DC20B20CE9F6800B7ACF5 /* Layoutable.swift */; }; + C83588C120DBC65500D6E8F9 /* CALayerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */; }; + C83588C220DBC65600D6E8F9 /* CALayerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */; }; + C83588C320DBC65600D6E8F9 /* CALayerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */; }; + C8C4928D20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C4928C20DA7DA700048357 /* CALayer+PinLayout.swift */; }; + C8C4928E20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C4928C20DA7DA700048357 /* CALayer+PinLayout.swift */; }; + C8C4928F20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C4928C20DA7DA700048357 /* CALayer+PinLayout.swift */; }; + D9723CB04DDFEA273EE46181 /* Pods_PinLayoutTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C38C34C80E44E6721B2E9C3 /* Pods_PinLayoutTests_iOS.framework */; }; + DF10846C212D8B5900C23B80 /* BetweenSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF10846A212D8B4B00C23B80 /* BetweenSpec.swift */; }; + DF10846D212D8B5900C23B80 /* BetweenSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF10846A212D8B4B00C23B80 /* BetweenSpec.swift */; }; + DF10846E212D8B5900C23B80 /* BetweenSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF10846A212D8B4B00C23B80 /* BetweenSpec.swift */; }; + DF108470212D8E8700C23B80 /* PinLayout+Between.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF10846F212D8E8700C23B80 /* PinLayout+Between.swift */; }; DF1A5D202084C94700725EF5 /* PinLayoutTestMacOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF1A5D1F2084C94700725EF5 /* PinLayoutTestMacOS.swift */; }; DF1A5D302084CF9700725EF5 /* PinLayoutMacOS.h in Headers */ = {isa = PBXBuildFile; fileRef = DF1A5D2E2084CF9700725EF5 /* PinLayoutMacOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; - DF1A5D352084CFC100725EF5 /* NSView+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF1A5CBC208106C900725EF5 /* NSView+PinLayout.swift */; }; - DF1A5D362084CFC100725EF5 /* NSView+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF1A5CBC208106C900725EF5 /* NSView+PinLayout.swift */; }; - DF1A5D372084CFC200725EF5 /* NSView+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF1A5CBC208106C900725EF5 /* NSView+PinLayout.swift */; }; - DF1A5D382084CFC600725EF5 /* PinLayout+Filters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24949A2D1EF69474003643D3 /* PinLayout+Filters.swift */; }; + DF1A5D382084CFC600725EF5 /* Filters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24949A2D1EF69474003643D3 /* Filters.swift */; }; DF1A5D392084CFC600725EF5 /* Pin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24D18D231F3E37DD008129EF /* Pin.swift */; }; - DF1A5D3A2084CFC600725EF5 /* UIView+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF1A5CBA208106A900725EF5 /* UIView+PinLayout.swift */; }; - DF1A5D3B2084CFD600725EF5 /* PinLayoutImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C61FC37C1C0054CADD /* PinLayoutImpl.swift */; }; - DF1A5D3C2084CFD600725EF5 /* PinLayoutImpl+Coordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C71FC37C1C0054CADD /* PinLayoutImpl+Coordinates.swift */; }; - DF1A5D3D2084CFD600725EF5 /* PinLayoutImpl+Layouting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C41FC37A900054CADD /* PinLayoutImpl+Layouting.swift */; }; - DF1A5D3E2084CFD600725EF5 /* PinLayoutImpl+Relative.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C91FC37C1C0054CADD /* PinLayoutImpl+Relative.swift */; }; - DF1A5D3F2084CFD600725EF5 /* PinLayoutImpl+Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6C81FC37C1C0054CADD /* PinLayoutImpl+Warning.swift */; }; DF1A5D402084CFD600725EF5 /* PinSafeArea.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB3ECB02061602E005F226B /* PinSafeArea.swift */; }; DF1A5D412084CFD600725EF5 /* Coordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6D21FC37CD40054CADD /* Coordinates.swift */; }; DF1A5D422084CFD600725EF5 /* Percent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243C620E1FC3834B0082C327 /* Percent.swift */; }; DF1A5D432084CFD600725EF5 /* TypesImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6CE1FC37C570054CADD /* TypesImpl.swift */; }; - DF1A5D442084CFD600725EF5 /* UIView+LTR.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2475B6D01FC37C8C0054CADD /* UIView+LTR.swift */; }; DF1E39B520482B200002D0AA /* PinSafeAreaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF1E39B420482B1F0002D0AA /* PinSafeAreaTests.swift */; }; + DF28022420C2B13800A1833B /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFF222CC20B999BD00AC2A84 /* Types.swift */; }; + DF28022520C2B13900A1833B /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFF222CC20B999BD00AC2A84 /* Types.swift */; }; + DF28022620C2B15A00A1833B /* Types+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFF222CE20B99A6600AC2A84 /* Types+Description.swift */; }; + DF28022720C2B15B00A1833B /* Types+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFF222CE20B99A6600AC2A84 /* Types+Description.swift */; }; + DF46F684212442EE0055B081 /* PEdgeInsets+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD2783E211A52C60056BD93 /* PEdgeInsets+Operators.swift */; }; + DF46F685212442EE0055B081 /* PEdgeInsets+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD2783E211A52C60056BD93 /* PEdgeInsets+Operators.swift */; }; + DF46F686212442EF0055B081 /* PEdgeInsets+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD2783E211A52C60056BD93 /* PEdgeInsets+Operators.swift */; }; + DF702D8E20D33BA90062045C /* PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D8A20D33BA90062045C /* PinLayout.swift */; }; + DF702D8F20D33BA90062045C /* PinLayout+WrapContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D8B20D33BA90062045C /* PinLayout+WrapContent.swift */; }; + DF702D9020D33BA90062045C /* PinLayout+Size.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D8C20D33BA90062045C /* PinLayout+Size.swift */; }; + DF702D9120D33BA90062045C /* PinLayout+Relative.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D8D20D33BA90062045C /* PinLayout+Relative.swift */; }; + DF702D9520D33C0E0062045C /* PinLayout+Coordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D9220D33C0D0062045C /* PinLayout+Coordinates.swift */; }; + DF702D9620D33C0E0062045C /* PinLayout+Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D9320D33C0E0062045C /* PinLayout+Warning.swift */; }; + DF702D9720D33C0E0062045C /* PinLayout+Layouting.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D9420D33C0E0062045C /* PinLayout+Layouting.swift */; }; + DF702D9920D33CF10062045C /* PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D8A20D33BA90062045C /* PinLayout.swift */; }; + DF702D9A20D33CF10062045C /* PinLayout+Relative.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D8D20D33BA90062045C /* PinLayout+Relative.swift */; }; + DF702D9B20D33CF10062045C /* PinLayout+Size.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D8C20D33BA90062045C /* PinLayout+Size.swift */; }; + DF702D9C20D33CF10062045C /* PinLayout+WrapContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D8B20D33BA90062045C /* PinLayout+WrapContent.swift */; }; + DF702D9D20D33CF20062045C /* PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D8A20D33BA90062045C /* PinLayout.swift */; }; + DF702D9E20D33CF20062045C /* PinLayout+Relative.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D8D20D33BA90062045C /* PinLayout+Relative.swift */; }; + DF702D9F20D33CF20062045C /* PinLayout+Size.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D8C20D33BA90062045C /* PinLayout+Size.swift */; }; + DF702DA020D33CF20062045C /* PinLayout+WrapContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D8B20D33BA90062045C /* PinLayout+WrapContent.swift */; }; + DF702DA120D33CF90062045C /* PinLayout+Coordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D9220D33C0D0062045C /* PinLayout+Coordinates.swift */; }; + DF702DA220D33CF90062045C /* PinLayout+Layouting.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D9420D33C0E0062045C /* PinLayout+Layouting.swift */; }; + DF702DA320D33CF90062045C /* PinLayout+Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D9320D33C0E0062045C /* PinLayout+Warning.swift */; }; + DF702DA420D33CFA0062045C /* PinLayout+Coordinates.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D9220D33C0D0062045C /* PinLayout+Coordinates.swift */; }; + DF702DA520D33CFA0062045C /* PinLayout+Layouting.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D9420D33C0E0062045C /* PinLayout+Layouting.swift */; }; + DF702DA620D33CFA0062045C /* PinLayout+Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702D9320D33C0E0062045C /* PinLayout+Warning.swift */; }; + DF702DAA20D33D660062045C /* NSView+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702DA820D33D660062045C /* NSView+PinLayout.swift */; }; + DF702DAB20D33D660062045C /* UIView+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702DA920D33D660062045C /* UIView+PinLayout.swift */; }; + DF702DAC20D33D6A0062045C /* NSView+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702DA820D33D660062045C /* NSView+PinLayout.swift */; }; + DF702DAD20D33D6A0062045C /* UIView+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702DA920D33D660062045C /* UIView+PinLayout.swift */; }; + DF702DAE20D33D6A0062045C /* NSView+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702DA820D33D660062045C /* NSView+PinLayout.swift */; }; + DF702DAF20D33D6A0062045C /* UIView+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF702DA920D33D660062045C /* UIView+PinLayout.swift */; }; DF7A36BD1E918301000F9856 /* PinEdgesSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF7A36BC1E918301000F9856 /* PinEdgesSpec.swift */; }; + DF96F30E20CB6362007CA714 /* PinLayoutObjCImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241A277C1F8E958F00B1AD39 /* PinLayoutObjCImpl.swift */; }; DFABC01F208781A900CB6494 /* Types+Appkit.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFABC01E208781A900CB6494 /* Types+Appkit.swift */; }; DFABC022208781ED00CB6494 /* Types+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFABC020208781C700CB6494 /* Types+UIKit.swift */; }; DFABC023208781EE00CB6494 /* Types+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFABC020208781C700CB6494 /* Types+UIKit.swift */; }; @@ -94,14 +123,27 @@ DFB288B720854252001F9588 /* UIImage+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 248E4C751F7A88CF00C0E7F7 /* UIImage+Color.swift */; }; DFB3ECB12061602F005F226B /* PinSafeArea.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB3ECB02061602E005F226B /* PinSafeArea.swift */; }; DFB3ECB72062A937005F226B /* PinSafeArea.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB3ECB02061602E005F226B /* PinSafeArea.swift */; }; - DFC97CA71E8A8F2C001545D5 /* PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFC97CA61E8A8F2C001545D5 /* PinLayout.swift */; }; + DFBCAF01213023D10025F7BF /* ReadableLayoutMarginsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFBCAEFF213023700025F7BF /* ReadableLayoutMarginsSpec.swift */; }; + DFBCAF02213023D30025F7BF /* ReadableLayoutMarginsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFBCAEFF213023700025F7BF /* ReadableLayoutMarginsSpec.swift */; }; + DFBCAF1A2132C55A0025F7BF /* PinLayout+Between.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF10846F212D8E8700C23B80 /* PinLayout+Between.swift */; }; + DFBCAF1B2132C55B0025F7BF /* PinLayout+Between.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF10846F212D8E8700C23B80 /* PinLayout+Between.swift */; }; DFCA5F1620111E0B00180CD7 /* UIScrollViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFCA5F1420111BCF00180CD7 /* UIScrollViewSpec.swift */; }; + DFEAF75020C9661B00E33147 /* ObjectiveCSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = DFEAF74D20C965DC00E33147 /* ObjectiveCSpec.m */; }; + DFEAF75220C9661D00E33147 /* ObjectiveCSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = DFEAF74D20C965DC00E33147 /* ObjectiveCSpec.m */; }; DFED154F20852F7E009EF9A7 /* BasicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2469C5011E75D88500073BEE /* BasicView.swift */; }; DFED15502085304B009EF9A7 /* AdjustSizeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2469C4FF1E75D74000073BEE /* AdjustSizeSpec.swift */; }; - DFED155120853085009EF9A7 /* PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFC97CA61E8A8F2C001545D5 /* PinLayout.swift */; }; DFED1552208533DA009EF9A7 /* AspectRatioTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 248E4C721F7A83FA00C0E7F7 /* AspectRatioTests.swift */; }; DFED15532085349A009EF9A7 /* RectNimbleMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2469C5031E75DB7600073BEE /* RectNimbleMatcher.swift */; }; DFED15542085349B009EF9A7 /* RectNimbleMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2469C5031E75DB7600073BEE /* RectNimbleMatcher.swift */; }; + DFF222B120B877F400AC2A84 /* PinLayoutObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241A277B1F8E958F00B1AD39 /* PinLayoutObjC.swift */; }; + DFF222B220B877F600AC2A84 /* PinLayoutObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241A277B1F8E958F00B1AD39 /* PinLayoutObjC.swift */; }; + DFF222B320B877F800AC2A84 /* PinLayoutObjCImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241A277C1F8E958F00B1AD39 /* PinLayoutObjCImpl.swift */; }; + DFF222B420B877F900AC2A84 /* PinLayoutObjCImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241A277C1F8E958F00B1AD39 /* PinLayoutObjCImpl.swift */; }; + DFF222CD20B999BD00AC2A84 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFF222CC20B999BD00AC2A84 /* Types.swift */; }; + DFF222CF20B99A6600AC2A84 /* Types+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFF222CE20B99A6600AC2A84 /* Types+Description.swift */; }; + DFF222E320BACBBF00AC2A84 /* WrapContentSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFF222E120BACBA800AC2A84 /* WrapContentSpec.swift */; }; + DFF222E420BACBC000AC2A84 /* WrapContentSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFF222E120BACBA800AC2A84 /* WrapContentSpec.swift */; }; + DFF222E520BACBC000AC2A84 /* WrapContentSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFF222E120BACBA800AC2A84 /* WrapContentSpec.swift */; }; DFF6F9C72084DCD3004F5AED /* PinLayout.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 244DF2F81EF46C500090508B /* PinLayout.framework */; }; DFF6F9CD2084E15A004F5AED /* BasicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2469C5011E75D88500073BEE /* BasicView.swift */; }; DFF6F9CE2084E15A004F5AED /* AdjustSizeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2469C4FF1E75D74000073BEE /* AdjustSizeSpec.swift */; }; @@ -110,17 +152,16 @@ DFF6F9D12084E15A004F5AED /* LayoutMethodSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243B12C41FC3CFC10072A9C3 /* LayoutMethodSpec.swift */; }; DFF6F9D22084E15A004F5AED /* MarginsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 244C6E141E776A0C0074FC74 /* MarginsSpec.swift */; }; DFF6F9D32084E15A004F5AED /* MinMaxWidthHeightSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 242723711F008B85006A5C3A /* MinMaxWidthHeightSpec.swift */; }; - DFF6F9D42084E15A004F5AED /* ObjectiveCSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 243B12C91FC469550072A9C3 /* ObjectiveCSpec.m */; }; DFF6F9D62084E15A004F5AED /* PinEdgesSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF7A36BC1E918301000F9856 /* PinEdgesSpec.swift */; }; DFF6F9D72084E15A004F5AED /* PinEdgeCoordinateSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2469C4FB1E74855D00073BEE /* PinEdgeCoordinateSpec.swift */; }; DFF6F9D82084E15A004F5AED /* PinPointCoordinatesSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 246D36471E6C46F50050F202 /* PinPointCoordinatesSpec.swift */; }; - DFF6F9D92084E15A004F5AED /* PinSafeAreaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF1E39B420482B1F0002D0AA /* PinSafeAreaTests.swift */; }; DFF6F9DA2084E15A004F5AED /* RTLSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24D18D151F3B42E0008129EF /* RTLSpec.swift */; }; DFF6F9DB2084E15A004F5AED /* RelativePositionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2482908B1E78CFFC00667D08 /* RelativePositionSpec.swift */; }; DFF6F9DC2084E15A004F5AED /* RelativePositionMultipleViewsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 242E8DC11EED5982005935FB /* RelativePositionMultipleViewsSpec.swift */; }; DFF6F9DD2084E15A004F5AED /* TransformSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 245C1DA11FEC4FC6007594F7 /* TransformSpec.swift */; }; DFF6F9DE2084E15A004F5AED /* UIScrollViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFCA5F1420111BCF00180CD7 /* UIScrollViewSpec.swift */; }; DFF6F9DF2084E15A004F5AED /* WarningSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 240F88BF1F0C1ED900280FC8 /* WarningSpec.swift */; }; + E4C7A81A5635E18A86956CA7 /* Pods_PinLayoutTests_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288E4316B3B6E27AA09363BB /* Pods_PinLayoutTests_macOS.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -153,6 +194,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 23D84A60C294055979A28086 /* Pods_PinLayoutTests_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PinLayoutTests_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 240F88BC1F0C042500280FC8 /* JustifyAlignSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JustifyAlignSpec.swift; sourceTree = ""; }; 240F88BF1F0C1ED900280FC8 /* WarningSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WarningSpec.swift; sourceTree = ""; }; 241A277B1F8E958F00B1AD39 /* PinLayoutObjC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinLayoutObjC.swift; sourceTree = ""; }; @@ -160,7 +202,6 @@ 242723711F008B85006A5C3A /* MinMaxWidthHeightSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinMaxWidthHeightSpec.swift; sourceTree = ""; }; 242E8DC11EED5982005935FB /* RelativePositionMultipleViewsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelativePositionMultipleViewsSpec.swift; sourceTree = ""; }; 243B12C41FC3CFC10072A9C3 /* LayoutMethodSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutMethodSpec.swift; sourceTree = ""; }; - 243B12C91FC469550072A9C3 /* ObjectiveCSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ObjectiveCSpec.m; sourceTree = ""; }; 243C620E1FC3834B0082C327 /* Percent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Percent.swift; path = Impl/Percent.swift; sourceTree = ""; }; 244C6E141E776A0C0074FC74 /* MarginsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarginsSpec.swift; sourceTree = ""; }; 244DF2F81EF46C500090508B /* PinLayout.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PinLayout.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -170,13 +211,7 @@ 2469C5011E75D88500073BEE /* BasicView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicView.swift; sourceTree = ""; }; 2469C5031E75DB7600073BEE /* RectNimbleMatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RectNimbleMatcher.swift; sourceTree = ""; }; 246D36471E6C46F50050F202 /* PinPointCoordinatesSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinPointCoordinatesSpec.swift; sourceTree = ""; }; - 2475B6C41FC37A900054CADD /* PinLayoutImpl+Layouting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "PinLayoutImpl+Layouting.swift"; path = "Impl/PinLayoutImpl+Layouting.swift"; sourceTree = ""; }; - 2475B6C61FC37C1C0054CADD /* PinLayoutImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PinLayoutImpl.swift; path = Impl/PinLayoutImpl.swift; sourceTree = ""; }; - 2475B6C71FC37C1C0054CADD /* PinLayoutImpl+Coordinates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "PinLayoutImpl+Coordinates.swift"; path = "Impl/PinLayoutImpl+Coordinates.swift"; sourceTree = ""; }; - 2475B6C81FC37C1C0054CADD /* PinLayoutImpl+Warning.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "PinLayoutImpl+Warning.swift"; path = "Impl/PinLayoutImpl+Warning.swift"; sourceTree = ""; }; - 2475B6C91FC37C1C0054CADD /* PinLayoutImpl+Relative.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "PinLayoutImpl+Relative.swift"; path = "Impl/PinLayoutImpl+Relative.swift"; sourceTree = ""; }; 2475B6CE1FC37C570054CADD /* TypesImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TypesImpl.swift; path = Impl/TypesImpl.swift; sourceTree = ""; }; - 2475B6D01FC37C8C0054CADD /* UIView+LTR.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+LTR.swift"; path = "Impl/UIView+LTR.swift"; sourceTree = ""; }; 2475B6D21FC37CD40054CADD /* Coordinates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Coordinates.swift; path = Impl/Coordinates.swift; sourceTree = ""; }; 2475B6D61FC37D4D0054CADD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 2475B6D71FC37D4D0054CADD /* PinLayoutTVOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PinLayoutTVOS.h; sourceTree = ""; }; @@ -184,35 +219,55 @@ 2482908B1E78CFFC00667D08 /* RelativePositionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelativePositionSpec.swift; sourceTree = ""; }; 248E4C721F7A83FA00C0E7F7 /* AspectRatioTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AspectRatioTests.swift; sourceTree = ""; }; 248E4C751F7A88CF00C0E7F7 /* UIImage+Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Color.swift"; sourceTree = ""; }; - 24949A2D1EF69474003643D3 /* PinLayout+Filters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PinLayout+Filters.swift"; sourceTree = ""; }; + 24949A2D1EF69474003643D3 /* Filters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Filters.swift; sourceTree = ""; }; 249EFE7A1E64FB4C00165E39 /* PinLayout.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PinLayout.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 249EFE831E64FB4C00165E39 /* PinLayoutTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PinLayoutTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 249EFE8A1E64FB4C00165E39 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Common/Info.plist; sourceTree = ""; }; 24D18D151F3B42E0008129EF /* RTLSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RTLSpec.swift; sourceTree = ""; }; 24D18D231F3E37DD008129EF /* Pin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pin.swift; sourceTree = ""; }; - 27D858B9204FFE057EEF11A1 /* Pods-PinLayoutTests-macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutTests-macOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-PinLayoutTests-macOS/Pods-PinLayoutTests-macOS.release.xcconfig"; sourceTree = ""; }; - 2D973E3CEE5E4BD40167AFED /* Pods_PinLayoutTests_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PinLayoutTests_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 40AAADE2A4B6590E3AD4FA54 /* Pods-PinLayoutTests-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutTests-tvOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-PinLayoutTests-tvOS/Pods-PinLayoutTests-tvOS.release.xcconfig"; sourceTree = ""; }; - 417E52FC81E208F91CF03C1D /* Pods_PinLayoutTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PinLayoutTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 69594AA5081BC60D0E3D6D1A /* Pods-PinLayoutTests-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutTests-tvOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PinLayoutTests-tvOS/Pods-PinLayoutTests-tvOS.debug.xcconfig"; sourceTree = ""; }; - A02D58138C6BA063EB3E8E66 /* Pods-PinLayoutTests-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutTests-iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-PinLayoutTests-iOS/Pods-PinLayoutTests-iOS.release.xcconfig"; sourceTree = ""; }; - B7A69A7C17E9071CF457C39A /* Pods_PinLayoutTests_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PinLayoutTests_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BC74F9FA5B66BF5C299B7581 /* Pods-PinLayoutTests-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutTests-macOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PinLayoutTests-macOS/Pods-PinLayoutTests-macOS.debug.xcconfig"; sourceTree = ""; }; - C8BDEE8FC7F6D6F36D69AE89 /* Pods-PinLayoutTests-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutTests-iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PinLayoutTests-iOS/Pods-PinLayoutTests-iOS.debug.xcconfig"; sourceTree = ""; }; - DF1A5CBA208106A900725EF5 /* UIView+PinLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+PinLayout.swift"; sourceTree = ""; }; - DF1A5CBC208106C900725EF5 /* NSView+PinLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSView+PinLayout.swift"; sourceTree = ""; }; + 288E4316B3B6E27AA09363BB /* Pods_PinLayoutTests_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PinLayoutTests_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7BD3AD48CD5B8BA8E2C04EC9 /* Pods-PinLayoutTests-macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutTests-macOS.release.xcconfig"; path = "Target Support Files/Pods-PinLayoutTests-macOS/Pods-PinLayoutTests-macOS.release.xcconfig"; sourceTree = ""; }; + 880260D5785424979964CA1A /* Pods-PinLayoutTests-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutTests-iOS.debug.xcconfig"; path = "Target Support Files/Pods-PinLayoutTests-iOS/Pods-PinLayoutTests-iOS.debug.xcconfig"; sourceTree = ""; }; + 89672C5CCF1F123104855629 /* Pods-PinLayoutTests-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutTests-tvOS.release.xcconfig"; path = "Target Support Files/Pods-PinLayoutTests-tvOS/Pods-PinLayoutTests-tvOS.release.xcconfig"; sourceTree = ""; }; + 9C38C34C80E44E6721B2E9C3 /* Pods_PinLayoutTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PinLayoutTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A55FA6F0D7AFC9918887FBF2 /* Pods-PinLayoutTests-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutTests-iOS.release.xcconfig"; path = "Target Support Files/Pods-PinLayoutTests-iOS/Pods-PinLayoutTests-iOS.release.xcconfig"; sourceTree = ""; }; + C80435D220D0891C00EB1BD7 /* SizeCalculable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SizeCalculable.swift; sourceTree = ""; }; + C80435D420D0898000EB1BD7 /* Layoutable+PinLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Layoutable+PinLayout.swift"; sourceTree = ""; }; + C8291E40247A242600E95886 /* AutoSizeCalculable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoSizeCalculable.swift; sourceTree = ""; }; + C82DC20B20CE9F6800B7ACF5 /* Layoutable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Layoutable.swift; sourceTree = ""; }; + C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CALayerSpec.swift; sourceTree = ""; }; + C83600A520E2949200A3D891 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; usesTabs = 0; }; + C8C4928C20DA7DA700048357 /* CALayer+PinLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CALayer+PinLayout.swift"; sourceTree = ""; }; + DE878C8BA276883C90524382 /* Pods-PinLayoutTests-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutTests-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-PinLayoutTests-tvOS/Pods-PinLayoutTests-tvOS.debug.xcconfig"; sourceTree = ""; }; + DF10846A212D8B4B00C23B80 /* BetweenSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BetweenSpec.swift; sourceTree = ""; }; + DF10846F212D8E8700C23B80 /* PinLayout+Between.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PinLayout+Between.swift"; sourceTree = ""; }; DF1A5D1D2084C94700725EF5 /* PinLayoutTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PinLayoutTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; DF1A5D1F2084C94700725EF5 /* PinLayoutTestMacOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinLayoutTestMacOS.swift; sourceTree = ""; }; DF1A5D2C2084CF9700725EF5 /* PinLayout.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PinLayout.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DF1A5D2E2084CF9700725EF5 /* PinLayoutMacOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PinLayoutMacOS.h; sourceTree = ""; }; DF1E39B420482B1F0002D0AA /* PinSafeAreaTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinSafeAreaTests.swift; sourceTree = ""; }; + DF702D8A20D33BA90062045C /* PinLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinLayout.swift; sourceTree = ""; }; + DF702D8B20D33BA90062045C /* PinLayout+WrapContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PinLayout+WrapContent.swift"; sourceTree = ""; }; + DF702D8C20D33BA90062045C /* PinLayout+Size.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PinLayout+Size.swift"; sourceTree = ""; }; + DF702D8D20D33BA90062045C /* PinLayout+Relative.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PinLayout+Relative.swift"; sourceTree = ""; }; + DF702D9220D33C0D0062045C /* PinLayout+Coordinates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "PinLayout+Coordinates.swift"; path = "Impl/PinLayout+Coordinates.swift"; sourceTree = ""; }; + DF702D9320D33C0E0062045C /* PinLayout+Warning.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "PinLayout+Warning.swift"; path = "Impl/PinLayout+Warning.swift"; sourceTree = ""; }; + DF702D9420D33C0E0062045C /* PinLayout+Layouting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "PinLayout+Layouting.swift"; path = "Impl/PinLayout+Layouting.swift"; sourceTree = ""; }; + DF702DA820D33D660062045C /* NSView+PinLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSView+PinLayout.swift"; sourceTree = ""; }; + DF702DA920D33D660062045C /* UIView+PinLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+PinLayout.swift"; sourceTree = ""; }; DF7A36BC1E918301000F9856 /* PinEdgesSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinEdgesSpec.swift; sourceTree = ""; }; DFABC01E208781A900CB6494 /* Types+Appkit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Types+Appkit.swift"; sourceTree = ""; }; DFABC020208781C700CB6494 /* Types+UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Types+UIKit.swift"; sourceTree = ""; }; DFB3ECB02061602E005F226B /* PinSafeArea.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PinSafeArea.swift; path = Impl/PinSafeArea.swift; sourceTree = ""; }; - DFC97CA61E8A8F2C001545D5 /* PinLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinLayout.swift; sourceTree = ""; }; + DFBCAEFF213023700025F7BF /* ReadableLayoutMarginsSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadableLayoutMarginsSpec.swift; sourceTree = ""; }; DFCA5F1420111BCF00180CD7 /* UIScrollViewSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIScrollViewSpec.swift; sourceTree = ""; }; + DFD2783E211A52C60056BD93 /* PEdgeInsets+Operators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PEdgeInsets+Operators.swift"; sourceTree = ""; }; + DFEAF74D20C965DC00E33147 /* ObjectiveCSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjectiveCSpec.m; sourceTree = ""; }; + DFF222CC20B999BD00AC2A84 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; + DFF222CE20B99A6600AC2A84 /* Types+Description.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Types+Description.swift"; sourceTree = ""; }; + DFF222E120BACBA800AC2A84 /* WrapContentSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WrapContentSpec.swift; sourceTree = ""; }; DFF6F9C22084DCD3004F5AED /* PinLayoutTests-tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PinLayoutTests-tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + FA341A1D293877848A382C1A /* Pods-PinLayoutTests-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayoutTests-macOS.debug.xcconfig"; path = "Target Support Files/Pods-PinLayoutTests-macOS/Pods-PinLayoutTests-macOS.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -235,7 +290,7 @@ buildActionMask = 2147483647; files = ( 249EFE841E64FB4C00165E39 /* PinLayout.framework in Frameworks */, - 32A65F57D5D8D5204831FF3A /* Pods_PinLayoutTests_iOS.framework in Frameworks */, + D9723CB04DDFEA273EE46181 /* Pods_PinLayoutTests_iOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -243,7 +298,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 111A0F2DFE45A60621382616 /* Pods_PinLayoutTests_macOS.framework in Frameworks */, + E4C7A81A5635E18A86956CA7 /* Pods_PinLayoutTests_macOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -259,23 +314,13 @@ buildActionMask = 2147483647; files = ( DFF6F9C72084DCD3004F5AED /* PinLayout.framework in Frameworks */, - 0862040479C5CDB1087DD5BE /* Pods_PinLayoutTests_tvOS.framework in Frameworks */, + 733BCEB894DC625445120A4B /* Pods_PinLayoutTests_tvOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 138A1F602470415292B7194B /* Frameworks */ = { - isa = PBXGroup; - children = ( - 417E52FC81E208F91CF03C1D /* Pods_PinLayoutTests_iOS.framework */, - 2D973E3CEE5E4BD40167AFED /* Pods_PinLayoutTests_macOS.framework */, - B7A69A7C17E9071CF457C39A /* Pods_PinLayoutTests_tvOS.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; 241A277A1F8E958F00B1AD39 /* ObjectiveC */ = { isa = PBXGroup; children = ( @@ -299,11 +344,12 @@ 249EFE701E64FB4C00165E39 = { isa = PBXGroup; children = ( + C83600A520E2949200A3D891 /* README.md */, 249EFE7C1E64FB4C00165E39 /* Sources */, 249EFE871E64FB4C00165E39 /* Tests */, 249EFE7B1E64FB4C00165E39 /* Products */, - 138A1F602470415292B7194B /* Frameworks */, - E2EE9A188294BFBC07C19DF6 /* Pods */, + 8DC632A70D21B70A40C40458 /* Pods */, + 686F2F5ECA92559A14ED39EE /* Frameworks */, ); sourceTree = ""; }; @@ -323,11 +369,19 @@ 249EFE7C1E64FB4C00165E39 /* Sources */ = { isa = PBXGroup; children = ( - DFC97CA61E8A8F2C001545D5 /* PinLayout.swift */, - 24949A2D1EF69474003643D3 /* PinLayout+Filters.swift */, + 24949A2D1EF69474003643D3 /* Filters.swift */, + C82DC20B20CE9F6800B7ACF5 /* Layoutable.swift */, + C80435D420D0898000EB1BD7 /* Layoutable+PinLayout.swift */, 24D18D231F3E37DD008129EF /* Pin.swift */, - DF1A5CBA208106A900725EF5 /* UIView+PinLayout.swift */, - DF1A5CBC208106C900725EF5 /* NSView+PinLayout.swift */, + DF702D8A20D33BA90062045C /* PinLayout.swift */, + DF10846F212D8E8700C23B80 /* PinLayout+Between.swift */, + DF702D8D20D33BA90062045C /* PinLayout+Relative.swift */, + DF702D8C20D33BA90062045C /* PinLayout+Size.swift */, + DF702D8B20D33BA90062045C /* PinLayout+WrapContent.swift */, + C80435D220D0891C00EB1BD7 /* SizeCalculable.swift */, + C8291E40247A242600E95886 /* AutoSizeCalculable.swift */, + DFF222CC20B999BD00AC2A84 /* Types.swift */, + DF702DA720D33D2A0062045C /* Extensions */, DFA06B031E8B38B300B6D5E7 /* Impl */, 241A277A1F8E958F00B1AD39 /* ObjectiveC */, 2475B6D51FC37D270054CADD /* SupportingFiles */, @@ -349,14 +403,37 @@ path = Tests; sourceTree = ""; }; + 686F2F5ECA92559A14ED39EE /* Frameworks */ = { + isa = PBXGroup; + children = ( + 9C38C34C80E44E6721B2E9C3 /* Pods_PinLayoutTests_iOS.framework */, + 288E4316B3B6E27AA09363BB /* Pods_PinLayoutTests_macOS.framework */, + 23D84A60C294055979A28086 /* Pods_PinLayoutTests_tvOS.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 8DC632A70D21B70A40C40458 /* Pods */ = { + isa = PBXGroup; + children = ( + 880260D5785424979964CA1A /* Pods-PinLayoutTests-iOS.debug.xcconfig */, + A55FA6F0D7AFC9918887FBF2 /* Pods-PinLayoutTests-iOS.release.xcconfig */, + FA341A1D293877848A382C1A /* Pods-PinLayoutTests-macOS.debug.xcconfig */, + 7BD3AD48CD5B8BA8E2C04EC9 /* Pods-PinLayoutTests-macOS.release.xcconfig */, + DE878C8BA276883C90524382 /* Pods-PinLayoutTests-tvOS.debug.xcconfig */, + 89672C5CCF1F123104855629 /* Pods-PinLayoutTests-tvOS.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; DF1A5D252084C9E200725EF5 /* iOS */ = { isa = PBXGroup; children = ( - 243B12C91FC469550072A9C3 /* ObjectiveCSpec.m */, DF1E39B420482B1F0002D0AA /* PinSafeAreaTests.swift */, + DFBCAEFF213023700025F7BF /* ReadableLayoutMarginsSpec.swift */, 245C1DA11FEC4FC6007594F7 /* TransformSpec.swift */, - DFCA5F1420111BCF00180CD7 /* UIScrollViewSpec.swift */, DFABC020208781C700CB6494 /* Types+UIKit.swift */, + DFCA5F1420111BCF00180CD7 /* UIScrollViewSpec.swift */, ); path = iOS; sourceTree = ""; @@ -370,19 +447,28 @@ path = macOS; sourceTree = ""; }; + DF702DA720D33D2A0062045C /* Extensions */ = { + isa = PBXGroup; + children = ( + DF702DA820D33D660062045C /* NSView+PinLayout.swift */, + DF702DA920D33D660062045C /* UIView+PinLayout.swift */, + C8C4928C20DA7DA700048357 /* CALayer+PinLayout.swift */, + DFD2783E211A52C60056BD93 /* PEdgeInsets+Operators.swift */, + ); + path = Extensions; + sourceTree = ""; + }; DFA06B031E8B38B300B6D5E7 /* Impl */ = { isa = PBXGroup; children = ( - 2475B6C61FC37C1C0054CADD /* PinLayoutImpl.swift */, - 2475B6C71FC37C1C0054CADD /* PinLayoutImpl+Coordinates.swift */, - 2475B6C41FC37A900054CADD /* PinLayoutImpl+Layouting.swift */, - 2475B6C91FC37C1C0054CADD /* PinLayoutImpl+Relative.swift */, - 2475B6C81FC37C1C0054CADD /* PinLayoutImpl+Warning.swift */, + DF702D9220D33C0D0062045C /* PinLayout+Coordinates.swift */, + DF702D9420D33C0E0062045C /* PinLayout+Layouting.swift */, + DF702D9320D33C0E0062045C /* PinLayout+Warning.swift */, DFB3ECB02061602E005F226B /* PinSafeArea.swift */, 2475B6D21FC37CD40054CADD /* Coordinates.swift */, 243C620E1FC3834B0082C327 /* Percent.swift */, 2475B6CE1FC37C570054CADD /* TypesImpl.swift */, - 2475B6D01FC37C8C0054CADD /* UIView+LTR.swift */, + DFF222CE20B99A6600AC2A84 /* Types+Description.swift */, ); name = Impl; sourceTree = ""; @@ -392,10 +478,13 @@ children = ( 2469C4FF1E75D74000073BEE /* AdjustSizeSpec.swift */, 248E4C721F7A83FA00C0E7F7 /* AspectRatioTests.swift */, + DF10846A212D8B4B00C23B80 /* BetweenSpec.swift */, + C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */, 240F88BC1F0C042500280FC8 /* JustifyAlignSpec.swift */, 243B12C41FC3CFC10072A9C3 /* LayoutMethodSpec.swift */, 244C6E141E776A0C0074FC74 /* MarginsSpec.swift */, 242723711F008B85006A5C3A /* MinMaxWidthHeightSpec.swift */, + DFEAF74D20C965DC00E33147 /* ObjectiveCSpec.m */, DF7A36BC1E918301000F9856 /* PinEdgesSpec.swift */, 2469C4FB1E74855D00073BEE /* PinEdgeCoordinateSpec.swift */, 246D36471E6C46F50050F202 /* PinPointCoordinatesSpec.swift */, @@ -403,23 +492,11 @@ 2482908B1E78CFFC00667D08 /* RelativePositionSpec.swift */, 242E8DC11EED5982005935FB /* RelativePositionMultipleViewsSpec.swift */, 240F88BF1F0C1ED900280FC8 /* WarningSpec.swift */, + DFF222E120BACBA800AC2A84 /* WrapContentSpec.swift */, ); path = Common; sourceTree = ""; }; - E2EE9A188294BFBC07C19DF6 /* Pods */ = { - isa = PBXGroup; - children = ( - C8BDEE8FC7F6D6F36D69AE89 /* Pods-PinLayoutTests-iOS.debug.xcconfig */, - A02D58138C6BA063EB3E8E66 /* Pods-PinLayoutTests-iOS.release.xcconfig */, - BC74F9FA5B66BF5C299B7581 /* Pods-PinLayoutTests-macOS.debug.xcconfig */, - 27D858B9204FFE057EEF11A1 /* Pods-PinLayoutTests-macOS.release.xcconfig */, - 69594AA5081BC60D0E3D6D1A /* Pods-PinLayoutTests-tvOS.debug.xcconfig */, - 40AAADE2A4B6590E3AD4FA54 /* Pods-PinLayoutTests-tvOS.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -489,12 +566,12 @@ isa = PBXNativeTarget; buildConfigurationList = 249EFE911E64FB4C00165E39 /* Build configuration list for PBXNativeTarget "PinLayoutTests-iOS" */; buildPhases = ( - C13A14974C1425B4B67ECF64 /* [CP] Check Pods Manifest.lock */, + 503526694CFF6E8DCDA3F5B0 /* [CP] Check Pods Manifest.lock */, 249EFE7F1E64FB4C00165E39 /* Sources */, 249EFE801E64FB4C00165E39 /* Frameworks */, 249EFE811E64FB4C00165E39 /* Resources */, 2419623B1E7F58F000A0466C /* CopyFiles */, - 88D90C444A5D379438EF7792 /* [CP] Embed Pods Frameworks */, + 08FFE4EAD9B24AEF2AC3A5A2 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -510,11 +587,11 @@ isa = PBXNativeTarget; buildConfigurationList = DF1A5D242084C94700725EF5 /* Build configuration list for PBXNativeTarget "PinLayoutTests-macOS" */; buildPhases = ( - 0E8D077D5C5ED76A11C46E36 /* [CP] Check Pods Manifest.lock */, + 5ADE456B5B6D6D591B6901AE /* [CP] Check Pods Manifest.lock */, DF1A5D192084C94700725EF5 /* Sources */, DF1A5D1A2084C94700725EF5 /* Frameworks */, DF1A5D1B2084C94700725EF5 /* Resources */, - A143968F873D85D48BABCDBF /* [CP] Embed Pods Frameworks */, + B707FE5E321E18163C39395D /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -547,11 +624,11 @@ isa = PBXNativeTarget; buildConfigurationList = DFF6F9CA2084DCD3004F5AED /* Build configuration list for PBXNativeTarget "PinLayoutTests-tvOS" */; buildPhases = ( - 41F720B3B7874CC3847A48CC /* [CP] Check Pods Manifest.lock */, + 9D6B9D7C269AC7A546496304 /* [CP] Check Pods Manifest.lock */, DFF6F9BE2084DCD3004F5AED /* Sources */, DFF6F9BF2084DCD3004F5AED /* Frameworks */, DFF6F9C02084DCD3004F5AED /* Resources */, - 23E0897A994CD18AE81019CE /* [CP] Embed Pods Frameworks */, + 3528A077350BF7D441563964 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -570,7 +647,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0930; - LastUpgradeCheck = 0900; + LastUpgradeCheck = 1420; ORGANIZATIONNAME = mcswiftlayyout.mirego.com; TargetAttributes = { 244DF2F71EF46C500090508B = { @@ -579,13 +656,13 @@ }; 249EFE791E64FB4C00165E39 = { CreatedOnToolsVersion = 8.2.1; - LastSwiftMigration = 0910; + LastSwiftMigration = 1030; ProvisioningStyle = Automatic; }; 249EFE821E64FB4C00165E39 = { CreatedOnToolsVersion = 8.2.1; DevelopmentTeam = 4Q596JWQC5; - LastSwiftMigration = 0920; + LastSwiftMigration = 1030; ProvisioningStyle = Automatic; }; DF1A5D1C2084C94700725EF5 = { @@ -606,10 +683,11 @@ }; buildConfigurationList = 249EFE741E64FB4C00165E39 /* Build configuration list for PBXProject "PinLayout" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = 249EFE701E64FB4C00165E39; productRefGroup = 249EFE7B1E64FB4C00165E39 /* Products */; @@ -665,31 +743,33 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 0E8D077D5C5ED76A11C46E36 /* [CP] Check Pods Manifest.lock */ = { + 08FFE4EAD9B24AEF2AC3A5A2 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", + "${PODS_ROOT}/Target Support Files/Pods-PinLayoutTests-iOS/Pods-PinLayoutTests-iOS-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Nimble-iOS/Nimble.framework", + "${BUILT_PRODUCTS_DIR}/Quick-iOS/Quick.framework", ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-PinLayoutTests-macOS-checkManifestLockResult.txt", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PinLayoutTests-iOS/Pods-PinLayoutTests-iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 23E0897A994CD18AE81019CE /* [CP] Embed Pods Frameworks */ = { + 3528A077350BF7D441563964 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-PinLayoutTests-tvOS/Pods-PinLayoutTests-tvOS-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-PinLayoutTests-tvOS/Pods-PinLayoutTests-tvOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/Nimble-tvOS/Nimble.framework", "${BUILT_PRODUCTS_DIR}/Quick-tvOS/Quick.framework", ); @@ -700,83 +780,93 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PinLayoutTests-tvOS/Pods-PinLayoutTests-tvOS-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PinLayoutTests-tvOS/Pods-PinLayoutTests-tvOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 41F720B3B7874CC3847A48CC /* [CP] Check Pods Manifest.lock */ = { + 503526694CFF6E8DCDA3F5B0 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-PinLayoutTests-tvOS-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-PinLayoutTests-iOS-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 88D90C444A5D379438EF7792 /* [CP] Embed Pods Frameworks */ = { + 5ADE456B5B6D6D591B6901AE /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-PinLayoutTests-iOS/Pods-PinLayoutTests-iOS-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/Nimble-iOS/Nimble.framework", - "${BUILT_PRODUCTS_DIR}/Quick-iOS/Quick.framework", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", + "$(DERIVED_FILE_DIR)/Pods-PinLayoutTests-macOS-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PinLayoutTests-iOS/Pods-PinLayoutTests-iOS-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - A143968F873D85D48BABCDBF /* [CP] Embed Pods Frameworks */ = { + 9D6B9D7C269AC7A546496304 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-PinLayoutTests-macOS/Pods-PinLayoutTests-macOS-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/Nimble-macOS/Nimble.framework", - "${BUILT_PRODUCTS_DIR}/Quick-macOS/Quick.framework", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", + "$(DERIVED_FILE_DIR)/Pods-PinLayoutTests-tvOS-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PinLayoutTests-macOS/Pods-PinLayoutTests-macOS-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - C13A14974C1425B4B67ECF64 /* [CP] Check Pods Manifest.lock */ = { + B707FE5E321E18163C39395D /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", + "${PODS_ROOT}/Target Support Files/Pods-PinLayoutTests-macOS/Pods-PinLayoutTests-macOS-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Nimble-macOS/Nimble.framework", + "${BUILT_PRODUCTS_DIR}/Quick-macOS/Quick.framework", ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-PinLayoutTests-iOS-checkManifestLockResult.txt", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PinLayoutTests-macOS/Pods-PinLayoutTests-macOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -786,21 +876,32 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 243C62081FC37F6C0082C327 /* PinLayoutImpl+Warning.swift in Sources */, + DFF222B420B877F900AC2A84 /* PinLayoutObjCImpl.swift in Sources */, + C8C4928F20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */, + DF28022720C2B15B00A1833B /* Types+Description.swift in Sources */, + DFF222B220B877F600AC2A84 /* PinLayoutObjC.swift in Sources */, + DFBCAF1B2132C55B0025F7BF /* PinLayout+Between.swift in Sources */, + DF702D9E20D33CF20062045C /* PinLayout+Relative.swift in Sources */, 24D18D261F3E5EA5008129EF /* Pin.swift in Sources */, - DF1A5D362084CFC100725EF5 /* NSView+PinLayout.swift in Sources */, - 243C62041FC37F680082C327 /* PinLayoutImpl.swift in Sources */, - 243C62071FC37F6C0082C327 /* PinLayoutImpl+Relative.swift in Sources */, + DF28022420C2B13800A1833B /* Types.swift in Sources */, + C80435D720D08A2C00EB1BD7 /* SizeCalculable.swift in Sources */, 243B12BC1FC393580072A9C3 /* Percent.swift in Sources */, DFB3ECB72062A937005F226B /* PinSafeArea.swift in Sources */, - DF1A5D0420812DE100725EF5 /* UIView+PinLayout.swift in Sources */, - 24B02B091F2A713000C18179 /* PinLayout+Filters.swift in Sources */, - 24B02B0A1F2A713300C18179 /* PinLayout.swift in Sources */, - 243C620B1FC37F6C0082C327 /* UIView+LTR.swift in Sources */, + C82DC20E20CE9F6800B7ACF5 /* Layoutable.swift in Sources */, + 24B02B091F2A713000C18179 /* Filters.swift in Sources */, + DF702DA420D33CFA0062045C /* PinLayout+Coordinates.swift in Sources */, 243C62091FC37F6C0082C327 /* Coordinates.swift in Sources */, + DF702DA020D33CF20062045C /* PinLayout+WrapContent.swift in Sources */, + DF702DAE20D33D6A0062045C /* NSView+PinLayout.swift in Sources */, + DF702D9F20D33CF20062045C /* PinLayout+Size.swift in Sources */, 243C620A1FC37F6C0082C327 /* TypesImpl.swift in Sources */, - 243C62061FC37F6C0082C327 /* PinLayoutImpl+Layouting.swift in Sources */, - 243C62051FC37F6C0082C327 /* PinLayoutImpl+Coordinates.swift in Sources */, + C80435D920D08B7400EB1BD7 /* Layoutable+PinLayout.swift in Sources */, + DF46F686212442EF0055B081 /* PEdgeInsets+Operators.swift in Sources */, + DF702DA520D33CFA0062045C /* PinLayout+Layouting.swift in Sources */, + DF702DA620D33CFA0062045C /* PinLayout+Warning.swift in Sources */, + C8291E43247A243900E95886 /* AutoSizeCalculable.swift in Sources */, + DF702DAF20D33D6A0062045C /* UIView+PinLayout.swift in Sources */, + DF702D9D20D33CF20062045C /* PinLayout.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -809,22 +910,31 @@ buildActionMask = 2147483647; files = ( 24D18D241F3E37DD008129EF /* Pin.swift in Sources */, - 2475B6CA1FC37C1C0054CADD /* PinLayoutImpl.swift in Sources */, - 241A277E1F8E958F00B1AD39 /* PinLayoutObjCImpl.swift in Sources */, - DF1A5D372084CFC200725EF5 /* NSView+PinLayout.swift in Sources */, + C8C4928D20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */, + DFF222CF20B99A6600AC2A84 /* Types+Description.swift in Sources */, + DFF222CD20B999BD00AC2A84 /* Types.swift in Sources */, + DF108470212D8E8700C23B80 /* PinLayout+Between.swift in Sources */, 243C620F1FC3834B0082C327 /* Percent.swift in Sources */, - 2475B6CB1FC37C1C0054CADD /* PinLayoutImpl+Coordinates.swift in Sources */, - 2475B6C51FC37A900054CADD /* PinLayoutImpl+Layouting.swift in Sources */, 2475B6CF1FC37C570054CADD /* TypesImpl.swift in Sources */, - 2475B6D11FC37C8C0054CADD /* UIView+LTR.swift in Sources */, - DFC97CA71E8A8F2C001545D5 /* PinLayout.swift in Sources */, - 2475B6CC1FC37C1C0054CADD /* PinLayoutImpl+Warning.swift in Sources */, - DF1A5CBB208106A900725EF5 /* UIView+PinLayout.swift in Sources */, + DF702D9020D33BA90062045C /* PinLayout+Size.swift in Sources */, + C80435D320D0891C00EB1BD7 /* SizeCalculable.swift in Sources */, + DF702D9620D33C0E0062045C /* PinLayout+Warning.swift in Sources */, + C82DC20C20CE9F6800B7ACF5 /* Layoutable.swift in Sources */, + DF96F30E20CB6362007CA714 /* PinLayoutObjCImpl.swift in Sources */, + DF702D8F20D33BA90062045C /* PinLayout+WrapContent.swift in Sources */, 2475B6D31FC37CD40054CADD /* Coordinates.swift in Sources */, + DF702D9720D33C0E0062045C /* PinLayout+Layouting.swift in Sources */, + DF702D9520D33C0E0062045C /* PinLayout+Coordinates.swift in Sources */, + DF702DAA20D33D660062045C /* NSView+PinLayout.swift in Sources */, + DF702D8E20D33BA90062045C /* PinLayout.swift in Sources */, + DF702D9120D33BA90062045C /* PinLayout+Relative.swift in Sources */, 241A277D1F8E958F00B1AD39 /* PinLayoutObjC.swift in Sources */, - 24949A2E1EF69474003643D3 /* PinLayout+Filters.swift in Sources */, + DF46F684212442EE0055B081 /* PEdgeInsets+Operators.swift in Sources */, + 24949A2E1EF69474003643D3 /* Filters.swift in Sources */, DFB3ECB12061602F005F226B /* PinSafeArea.swift in Sources */, - 2475B6CD1FC37C1C0054CADD /* PinLayoutImpl+Relative.swift in Sources */, + C8291E41247A242600E95886 /* AutoSizeCalculable.swift in Sources */, + DF702DAB20D33D660062045C /* UIView+PinLayout.swift in Sources */, + C80435D520D0898000EB1BD7 /* Layoutable+PinLayout.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -832,17 +942,20 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + DFF222E320BACBBF00AC2A84 /* WrapContentSpec.swift in Sources */, + DF10846C212D8B5900C23B80 /* BetweenSpec.swift in Sources */, 240F88BE1F0C066800280FC8 /* JustifyAlignSpec.swift in Sources */, 2469C5001E75D74000073BEE /* AdjustSizeSpec.swift in Sources */, + C83588C120DBC65500D6E8F9 /* CALayerSpec.swift in Sources */, 243B12C81FC3D06F0072A9C3 /* LayoutMethodSpec.swift in Sources */, 2482908C1E78CFFC00667D08 /* RelativePositionSpec.swift in Sources */, DF1E39B520482B200002D0AA /* PinSafeAreaTests.swift in Sources */, - 243B12CB1FC469D00072A9C3 /* ObjectiveCSpec.m in Sources */, 248E4C741F7A883800C0E7F7 /* AspectRatioTests.swift in Sources */, 240F88C11F0C1F5000280FC8 /* WarningSpec.swift in Sources */, 245C1DA31FEC4FCC007594F7 /* TransformSpec.swift in Sources */, DFCA5F1620111E0B00180CD7 /* UIScrollViewSpec.swift in Sources */, 242E8DC31EED5AB2005935FB /* RelativePositionMultipleViewsSpec.swift in Sources */, + DFBCAF01213023D10025F7BF /* ReadableLayoutMarginsSpec.swift in Sources */, 2469C5041E75DB7600073BEE /* RectNimbleMatcher.swift in Sources */, 242723731F008BF7006A5C3A /* MinMaxWidthHeightSpec.swift in Sources */, 2469C4FC1E74855D00073BEE /* PinEdgeCoordinateSpec.swift in Sources */, @@ -850,6 +963,7 @@ 2469C5021E75D88500073BEE /* BasicView.swift in Sources */, 246D36481E6C46F50050F202 /* PinPointCoordinatesSpec.swift in Sources */, DF7A36BD1E918301000F9856 /* PinEdgesSpec.swift in Sources */, + DFEAF75020C9661B00E33147 /* ObjectiveCSpec.m in Sources */, 244C6E151E776A0C0074FC74 /* MarginsSpec.swift in Sources */, DFABC022208781ED00CB6494 /* Types+UIKit.swift in Sources */, 248E4C771F7A88D200C0E7F7 /* UIImage+Color.swift in Sources */, @@ -861,11 +975,13 @@ buildActionMask = 2147483647; files = ( DFB288A520853F3C001F9588 /* MarginsSpec.swift in Sources */, + DF10846D212D8B5900C23B80 /* BetweenSpec.swift in Sources */, DF1A5D202084C94700725EF5 /* PinLayoutTestMacOS.swift in Sources */, DFED15502085304B009EF9A7 /* AdjustSizeSpec.swift in Sources */, DFED154F20852F7E009EF9A7 /* BasicView.swift in Sources */, DFB288AF20854127001F9588 /* PinPointCoordinatesSpec.swift in Sources */, DFB288A320853E82001F9588 /* JustifyAlignSpec.swift in Sources */, + DFF222E420BACBC000AC2A84 /* WrapContentSpec.swift in Sources */, DFED15532085349A009EF9A7 /* RectNimbleMatcher.swift in Sources */, DFB288B12085419B001F9588 /* RelativePositionSpec.swift in Sources */, DFB288B2208541B7001F9588 /* RelativePositionMultipleViewsSpec.swift in Sources */, @@ -874,6 +990,7 @@ DFABC01F208781A900CB6494 /* Types+Appkit.swift in Sources */, DFED1552208533DA009EF9A7 /* AspectRatioTests.swift in Sources */, DFB288AE208540F2001F9588 /* PinEdgeCoordinateSpec.swift in Sources */, + C83588C220DBC65600D6E8F9 /* CALayerSpec.swift in Sources */, DFB288A420853F32001F9588 /* LayoutMethodSpec.swift in Sources */, DFB288B3208541D9001F9588 /* WarningSpec.swift in Sources */, DFB288AD208540B8001F9588 /* PinEdgesSpec.swift in Sources */, @@ -884,21 +1001,32 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - DF1A5D3F2084CFD600725EF5 /* PinLayoutImpl+Warning.swift in Sources */, - DF1A5D3B2084CFD600725EF5 /* PinLayoutImpl.swift in Sources */, + DFF222B320B877F800AC2A84 /* PinLayoutObjCImpl.swift in Sources */, + C8C4928E20DA7DA700048357 /* CALayer+PinLayout.swift in Sources */, + DF28022620C2B15A00A1833B /* Types+Description.swift in Sources */, + DFF222B120B877F400AC2A84 /* PinLayoutObjC.swift in Sources */, + DFBCAF1A2132C55A0025F7BF /* PinLayout+Between.swift in Sources */, + DF702D9A20D33CF10062045C /* PinLayout+Relative.swift in Sources */, + DF28022520C2B13900A1833B /* Types.swift in Sources */, DF1A5D412084CFD600725EF5 /* Coordinates.swift in Sources */, DF1A5D422084CFD600725EF5 /* Percent.swift in Sources */, - DF1A5D3E2084CFD600725EF5 /* PinLayoutImpl+Relative.swift in Sources */, - DF1A5D442084CFD600725EF5 /* UIView+LTR.swift in Sources */, - DF1A5D352084CFC100725EF5 /* NSView+PinLayout.swift in Sources */, - DF1A5D3A2084CFC600725EF5 /* UIView+PinLayout.swift in Sources */, - DFED155120853085009EF9A7 /* PinLayout.swift in Sources */, - DF1A5D382084CFC600725EF5 /* PinLayout+Filters.swift in Sources */, + C80435D620D08A2B00EB1BD7 /* SizeCalculable.swift in Sources */, + C82DC20D20CE9F6800B7ACF5 /* Layoutable.swift in Sources */, + DF1A5D382084CFC600725EF5 /* Filters.swift in Sources */, DF1A5D392084CFC600725EF5 /* Pin.swift in Sources */, - DF1A5D3D2084CFD600725EF5 /* PinLayoutImpl+Layouting.swift in Sources */, - DF1A5D3C2084CFD600725EF5 /* PinLayoutImpl+Coordinates.swift in Sources */, + DF702DA120D33CF90062045C /* PinLayout+Coordinates.swift in Sources */, DF1A5D402084CFD600725EF5 /* PinSafeArea.swift in Sources */, + DF702D9C20D33CF10062045C /* PinLayout+WrapContent.swift in Sources */, + DF702DAC20D33D6A0062045C /* NSView+PinLayout.swift in Sources */, + DF702D9B20D33CF10062045C /* PinLayout+Size.swift in Sources */, + C80435D820D08B7300EB1BD7 /* Layoutable+PinLayout.swift in Sources */, DF1A5D432084CFD600725EF5 /* TypesImpl.swift in Sources */, + DF46F685212442EE0055B081 /* PEdgeInsets+Operators.swift in Sources */, + DF702DA220D33CF90062045C /* PinLayout+Layouting.swift in Sources */, + DF702DA320D33CF90062045C /* PinLayout+Warning.swift in Sources */, + C8291E42247A243900E95886 /* AutoSizeCalculable.swift in Sources */, + DF702DAD20D33D6A0062045C /* UIView+PinLayout.swift in Sources */, + DF702D9920D33CF10062045C /* PinLayout.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -906,6 +1034,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + DFF222E520BACBC000AC2A84 /* WrapContentSpec.swift in Sources */, DFF6F9DC2084E15A004F5AED /* RelativePositionMultipleViewsSpec.swift in Sources */, DFABC023208781EE00CB6494 /* Types+UIKit.swift in Sources */, DFF6F9DB2084E15A004F5AED /* RelativePositionSpec.swift in Sources */, @@ -916,17 +1045,19 @@ DFF6F9CE2084E15A004F5AED /* AdjustSizeSpec.swift in Sources */, DFB288B720854252001F9588 /* UIImage+Color.swift in Sources */, DFF6F9DE2084E15A004F5AED /* UIScrollViewSpec.swift in Sources */, - DFF6F9D42084E15A004F5AED /* ObjectiveCSpec.m in Sources */, DFF6F9DD2084E15A004F5AED /* TransformSpec.swift in Sources */, + C83588C320DBC65600D6E8F9 /* CALayerSpec.swift in Sources */, DFF6F9D32084E15A004F5AED /* MinMaxWidthHeightSpec.swift in Sources */, + DF10846E212D8B5900C23B80 /* BetweenSpec.swift in Sources */, DFF6F9CD2084E15A004F5AED /* BasicView.swift in Sources */, + DFBCAF02213023D30025F7BF /* ReadableLayoutMarginsSpec.swift in Sources */, DFF6F9DA2084E15A004F5AED /* RTLSpec.swift in Sources */, DFF6F9CF2084E15A004F5AED /* AspectRatioTests.swift in Sources */, DFF6F9D72084E15A004F5AED /* PinEdgeCoordinateSpec.swift in Sources */, DFF6F9D12084E15A004F5AED /* LayoutMethodSpec.swift in Sources */, + DFEAF75220C9661D00E33147 /* ObjectiveCSpec.m in Sources */, DFF6F9DF2084E15A004F5AED /* WarningSpec.swift in Sources */, DFF6F9D62084E15A004F5AED /* PinEdgesSpec.swift in Sources */, - DFF6F9D92084E15A004F5AED /* PinSafeAreaTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -950,7 +1081,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_IDENTITY = ""; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -959,14 +1090,14 @@ INFOPLIST_FILE = Sources/SupportingFiles/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = "com.mirego.PinLayout-tvOS"; PRODUCT_NAME = PinLayout; SDKROOT = appletvos; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; - SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.0; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Debug; }; @@ -983,14 +1114,14 @@ INFOPLIST_FILE = Sources/SupportingFiles/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = "com.mirego.PinLayout-tvOS"; PRODUCT_NAME = PinLayout; SDKROOT = appletvos; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; - SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.0; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Release; }; @@ -998,6 +1129,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -1007,6 +1139,7 @@ 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_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -1014,8 +1147,10 @@ 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; @@ -1041,7 +1176,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -1049,9 +1184,9 @@ SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TVOS_DEPLOYMENT_TARGET = 9.0; + TVOS_DEPLOYMENT_TARGET = 12.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -1061,6 +1196,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -1070,6 +1206,7 @@ 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_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -1077,8 +1214,10 @@ 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; @@ -1098,15 +1237,15 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TVOS_DEPLOYMENT_TARGET = 9.0; + TVOS_DEPLOYMENT_TARGET = 12.0; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -1117,7 +1256,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_IDENTITY = ""; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1125,12 +1264,14 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/SupportingFiles/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = "com.mirego.PinLayout-iOS"; PRODUCT_NAME = PinLayout; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Debug; }; @@ -1146,49 +1287,49 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/SupportingFiles/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = "com.mirego.PinLayout-iOS"; PRODUCT_NAME = PinLayout; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.0; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Release; }; 249EFE921E64FB4C00165E39 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C8BDEE8FC7F6D6F36D69AE89 /* Pods-PinLayoutTests-iOS.debug.xcconfig */; + baseConfigurationReference = 880260D5785424979964CA1A /* Pods-PinLayoutTests-iOS.debug.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; DEVELOPMENT_TEAM = 4Q596JWQC5; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Tests/Common/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = com.mirego.PinLayoutTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; }; name = Debug; }; 249EFE931E64FB4C00165E39 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A02D58138C6BA063EB3E8E66 /* Pods-PinLayoutTests-iOS.release.xcconfig */; + baseConfigurationReference = A55FA6F0D7AFC9918887FBF2 /* Pods-PinLayoutTests-iOS.release.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; DEVELOPMENT_TEAM = 4Q596JWQC5; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Tests/Common/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = com.mirego.PinLayoutTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; }; name = Release; }; DF1A5D222084C94700725EF5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = BC74F9FA5B66BF5C299B7581 /* Pods-PinLayoutTests-macOS.debug.xcconfig */; + baseConfigurationReference = FA341A1D293877848A382C1A /* Pods-PinLayoutTests-macOS.debug.xcconfig */; buildSettings = { CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -1196,25 +1337,25 @@ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4Q596JWQC5; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Tests/Common/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = com.layoutbox.PinLayoutTestMacOS; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SUPPORTED_PLATFORMS = macosx; - SWIFT_VERSION = 4.0; }; name = Debug; }; DF1A5D232084C94700725EF5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 27D858B9204FFE057EEF11A1 /* Pods-PinLayoutTests-macOS.release.xcconfig */; + baseConfigurationReference = 7BD3AD48CD5B8BA8E2C04EC9 /* Pods-PinLayoutTests-macOS.release.xcconfig */; buildSettings = { CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -1225,16 +1366,16 @@ CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4Q596JWQC5; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Tests/Common/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = com.layoutbox.PinLayoutTestMacOS; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SUPPORTED_PLATFORMS = macosx; - SWIFT_VERSION = 4.0; }; name = Release; }; @@ -1247,9 +1388,10 @@ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1260,13 +1402,13 @@ INFOPLIST_FILE = Sources/SupportingFiles/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = "com.layoutbox.PinLayout-macOS"; PRODUCT_NAME = PinLayout; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = macosx; - SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -1279,9 +1421,10 @@ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1292,19 +1435,19 @@ INFOPLIST_FILE = Sources/SupportingFiles/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = "com.layoutbox.PinLayout-macOS"; PRODUCT_NAME = PinLayout; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = macosx; - SWIFT_VERSION = 4.0; }; name = Release; }; DFF6F9CB2084DCD3004F5AED /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 69594AA5081BC60D0E3D6D1A /* Pods-PinLayoutTests-tvOS.debug.xcconfig */; + baseConfigurationReference = DE878C8BA276883C90524382 /* Pods-PinLayoutTests-tvOS.debug.xcconfig */; buildSettings = { CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -1317,19 +1460,19 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Tests/Common/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = com.layoutbox.PinLayoutTestsTVOS; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; - SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 11.3; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Debug; }; DFF6F9CC2084DCD3004F5AED /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 40AAADE2A4B6590E3AD4FA54 /* Pods-PinLayoutTests-tvOS.release.xcconfig */; + baseConfigurationReference = 89672C5CCF1F123104855629 /* Pods-PinLayoutTests-tvOS.release.xcconfig */; buildSettings = { CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -1342,13 +1485,13 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Tests/Common/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = com.layoutbox.PinLayoutTestsTVOS; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; - SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 11.3; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Release; }; diff --git a/PinLayout.xcodeproj/xcshareddata/xcschemes/PinLayout-iOS.xcscheme b/PinLayout.xcodeproj/xcshareddata/xcschemes/PinLayout-iOS.xcscheme index 3288a030..18dcd66e 100644 --- a/PinLayout.xcodeproj/xcshareddata/xcschemes/PinLayout-iOS.xcscheme +++ b/PinLayout.xcodeproj/xcshareddata/xcschemes/PinLayout-iOS.xcscheme @@ -1,6 +1,6 @@ + onlyGenerateCoverageForSpecifiedTargets = "YES"> + + + + - - - - - - - - - - - - - - - - + + + + @@ -39,17 +48,6 @@ - - - - - - - - - - diff --git a/Podfile b/Podfile index 8b197a0c..2142354f 100644 --- a/Podfile +++ b/Podfile @@ -1,9 +1,10 @@ +source 'https://cdn.cocoapods.org/' use_frameworks! workspace 'PinLayout.xcworkspace' target 'PinLayoutTests-iOS' do - platform :ios, "8.0" + platform :ios, "12.0" project 'PinLayout.xcodeproj' pod 'Quick' @@ -11,7 +12,7 @@ target 'PinLayoutTests-iOS' do end target 'PinLayoutTests-tvOS' do - platform :tvos, "9.0" + platform :tvos, "12.0" project 'PinLayout.xcodeproj' pod 'Quick' @@ -19,7 +20,7 @@ target 'PinLayoutTests-tvOS' do end target 'PinLayoutTests-macOS' do - platform :osx, "10.10" + platform :osx, "10.13" project 'PinLayout.xcodeproj' pod 'Quick' @@ -27,19 +28,9 @@ target 'PinLayoutTests-macOS' do end target 'PinLayoutSample' do - platform :ios, "8.0" + platform :ios, "12.0" project 'Example/PinLayoutSample.xcodeproj' pod 'PinLayout', :path => './' pod 'SwiftLint' - - # Debug only - pod 'Reveal-SDK', '~> 10', :configurations => ['Debug'] end - -#target 'PinLayoutMacOsSample' do -# platform :osx, '10.9' -# project 'Example/PinLayoutMacOsSample.xcodeproj' -# -# pod 'PinLayout', :path => './' -#end diff --git a/Podfile.lock b/Podfile.lock index 108e8cc1..9d761c0a 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,22 +1,19 @@ PODS: - - Nimble (7.0.3) - - PinLayout (1.7.0) - - Quick (1.2.0) - - Reveal-SDK (10) - - SwiftLint (0.25.1) + - Nimble (10.0.0) + - PinLayout (1.10.4) + - Quick (5.0.1) + - SwiftLint (0.50.3) DEPENDENCIES: - Nimble - PinLayout (from `./`) - Quick - - Reveal-SDK (~> 10) - SwiftLint SPEC REPOS: - https://github.com/CocoaPods/Specs.git: + trunk: - Nimble - Quick - - Reveal-SDK - SwiftLint EXTERNAL SOURCES: @@ -24,12 +21,11 @@ EXTERNAL SOURCES: :path: "./" SPEC CHECKSUMS: - Nimble: 7f5a9c447a33002645a071bddafbfb24ea70e0ac - PinLayout: 816805698a6c478d5f0cf97dd298243c098d6e50 - Quick: 58d203b1c5e27fff7229c4c1ae445ad7069a7a08 - Reveal-SDK: 7869ddf1f902cabbb07a1f0dd06bd25861a126f7 - SwiftLint: ce933681be10c3266e82576dad676fa815a602e9 + Nimble: 5316ef81a170ce87baf72dd961f22f89a602ff84 + PinLayout: f8a677ce0cd1cfe96b58435d029b4ceb4ce9c04c + Quick: 749aa754fd1e7d984f2000fe051e18a3a9809179 + SwiftLint: 77f7cb2b9bb81ab4a12fcc86448ba3f11afa50c6 -PODFILE CHECKSUM: ff779de6fa1cee5c61d6b4a57db319cf6d759839 +PODFILE CHECKSUM: 373beda8a5b3bfadeb1918c9d5c439ae90f949f1 -COCOAPODS: 1.5.0 +COCOAPODS: 1.12.0 diff --git a/README.md b/README.md index ed4777cd..47045d2c 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,42 @@

- PinLayout +

- - + +

+ - - - + +

-

- - - - -

- - +Extremely Fast views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainable. PinLayout can layouts UIView, NSView and CALayer. - -Extremely Fast views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainable. > "No Auto layout constraints attached" ### Requirements -* iOS 8.0+ / tvOS 9.0+ / macOS 10.9+ -* Xcode 8.0+ / Xcode 9.0+ -* Swift 3.2+ / Swift 4.0 / Objective-C - -### Recent features -* :star: PinLayout now support macOS. See [macOS Support](#macos_support) for more information. - -* :star: PinLayout expose the `safeAreaInsets` through [`UIView.pin.safeArea`](#safeAreaInsets), this property support not only iOS 11, but is also backward compatible for earlier iOS releases (7/8/9/10). See [safeAreaInsets support](#safeAreaInsets) for more information. +* iOS 9.0+ / tvOS 9.0+ / macOS 10.9+ +* Swift 5.x / 4 / 3 / Objective-C +* Xcode 13 / 12 / 11 / 10 -* :star: Methods [`all(:CGFloat)`, `horizontally(:CGFloat)` / `horizontally(:Percent)`, `vertically(:CGFloat)` / `vertically(:Percent)`](#pin_multiple_edges) +### Recent changes/features +* :star: Add [`pin.keyboardArea`](#safeAreaInsets) property [iOS 15+] +* :star: New chainable Objective-C syntax. See [PinLayout using Objective-C](#objective_c_interface) +* :star: Automatic Sizing, use PinLayout to compute view size. See [Automatic sizing](#automatic_sizing) +* :star: Add methods to position a view between two other views. See [Layout between other views](#layout_between). +* :star: Add [`pin.readableMargins` and `pin.layoutMargins`](#safeAreaInsets) properties. +* :star: Add `sizeToFit()` method. See [Adjusting size](#adjusting_size). +* :star: PinLayout can now layout CALayer. See [CALayer Support](#calayer_support) for more information. +* :star: PinLayout is #8 in the list of Swift Layout frameworks on [Awesome Swift](https://swift.libhunt.com/categories/714-layout) +* See [Changelog](https://github.com/layoutBox/PinLayout/blob/master/CHANGELOG.md) for all changes. @@ -51,21 +47,29 @@ Extremely Fast views layouting without auto layout. No magic, pure code, full co * [Performance](#performance) * [Documentation](#documentation) * [Right to left languages (RTL) support](#rtl_support) - * [Layout using distances from superview’s edges](#distance_from_superview_edge) + * [Edges layout](#layout_edges) + * [Relative Edges layout](#relative_edges_layout) + * [Relative Edges layout with alignment](#relative_edges_layout_w_alignment) + * [Layout between other views](#layout_between) + * [Layout between other views with alignment](#layout_between_w_alignment) * [Edges](#edges) * [Anchors](#anchors) - * [Relative positioning](#relative_positioning) * [Width, height and size](#width_height_size) + * [Adjusting size](#adjusting_size) * [minWidth, maxWidth, minHeight, maxHeight](#minmax_width_height_size) - * [Aspect Ratio](#aspect_ratio) * [Margins](#margins) - * [safeAreaInsets support](#safeAreaInsets) + * [Aspect Ratio](#aspect_ratio) + * [safeArea, readable and layout margins](#safeAreaInsets) + * [WrapContent](#wrapContent) * [justify, align](#justify_align) + * [Automatic sizing](#automatic_sizing) * [UIView's transforms](#uiview_transform) * [Warnings](#warnings) + * [Animations using PinLayout](#animations) * [More examples](#more_examples) * [Examples App](#examples_app) * [macOS Support](#macos_support) +* [CALayer Support](#calayer_support) * [PinLayout in Xcode Playgrounds](#playgrounds) * [PinLayout using Objective-C](#objective_c_interface) * [Installation](#installation) @@ -77,11 +81,11 @@ Extremely Fast views layouting without auto layout. No magic, pure code, full co :pushpin: PinLayout is actively updated. So please come often to see latest changes. You can also **Star** it to be able to retrieve it easily later. -### PinLayout + FlexLayout +### PinLayout and layoutBox -FlexLayout + -**PinLayout** is a companion of **[FlexLayout](https://github.com/layoutBox/FlexLayout)**. They share a similar syntax and method names. FlexLayout is a flexbox implementation. A view can layouts its subviews using PinLayout, FlexLayout, or both! FlexLayout it is particularly useful in situations where you need to layouts many views but don't require the PinLayout's finest control nor complex animations. +**PinLayout** is part of the **layoutBox** organization containing few Open Source projects related to layout using Swift. See **[layoutBox](https://github.com/layoutBox)**. ### PinLayout + Autolayout You don't need to choose, you can layout some views using PinLayout and some other with autolayout. Your views just to need to implement the autolayout `intrinsicContentSize` properties. @@ -100,7 +104,7 @@ This example layout an image, a UISegmentedControl, a label and a line separator * **Separator** is below the UIImageView and the UILabel, i.e. below the tallest one. The separator has a top margin of 10 pixels, left-aligned to the UIImageView and right-aligned to the UISegmentedControl. - + ```swift override func layoutSubviews() { @@ -117,7 +121,7 @@ override func layoutSubviews() { * 4 views, 4 lines * PinLayout expose the `safeAreaInsets` through [`UIView.pin.safeArea`](#safeAreaInsets), this property support not only iOS 11, but is also backward compatible for earlier iOS releases (7/8/9/10). See [safeAreaInsets support](#safeAreaInsets) for more information. * PinLayout doesn't use auto layout constraints, it is a framework that manually layout views. For that reason you need to update the layout inside either `UIView.layoutSubviews()` or `UIViewController.viewDidLayoutSubviews()` to handle container size's changes, including device rotation. You'll also need to handle UITraitCollection changes for app's that support multitasking. In the example above PinLayout's commands are inside UIView's `layoutSubviews()` method. -* This example is available in the [Examples App](#examples_app). See example complete [source code](https://github.com/mirego/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/Intro/IntroView.swift) +* This example is available in the [Examples App](#examples_app). See example complete [source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/Intro/IntroView.swift)
@@ -127,14 +131,14 @@ This example shows how easily PinLayout can adjust its layout based on the view' * If the container's width is smaller than 500 pixels, the label takes the full width and the UISegmentedControl is placed below it. * If the container's width is greater or equal to 500 pixels, the UISegmentedControl is at the top-right corner and the label takes the remaining horizontal space. - + ```swift let margin: CGFloat = 12 if frame.width < 500 { - textLabel.pin.top().left().right().margin(margin).sizeToFit(.width) + textLabel.pin.top().horizontally().margin(margin).sizeToFit(.width) segmentedControl.pin.below(of: textLabel).right().margin(margin) } else { segmentedControl.pin.top().right().margin(margin) @@ -142,7 +146,7 @@ This example shows how easily PinLayout can adjust its layout based on the view' } ``` -:pushpin: This example is available in the [Examples App](#examples_app). See example complete [source code](https://github.com/mirego/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/AdjustToContainer/Subviews/ChoiceSelectorView.swift) +:pushpin: This example is available in the [Examples App](#examples_app). See example complete [source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/AdjustToContainer/Subviews/ChoiceSelectorView.swift) @@ -153,7 +157,7 @@ This example shows how easily PinLayout can adjust its layout based on the view' * Full control: You're in the middle of the layout process, no magic black box. * Layout one view at a time. Make it simple to code and debug. * Concise syntax. Layout most views using a single line. -* [See the complete list here....](docs/PinLayout_principles.md) +* [**See the complete list here....**](docs/PinLayout_principles.md) # PinLayout's Performance @@ -162,11 +166,11 @@ PinLayout's performance has been measured using the [Layout Framework Benchmark] As you can see in the following chart, PinLayout are faster or equal to manual layouting, and **between 8x and 12x faster than auto layout**, and this for all types of iPhone (5S/6/6S/7/8/X) -[See here for more details, results and explanation of the benchmark](docs/Benchmark.md). +#### [See here for more details, results and explanation of the benchmark](docs/Benchmark.md).

- PinLayout Performance +

@@ -176,75 +180,92 @@ As you can see in the following chart, PinLayout are faster or equal to manual l ### UIKit safeAreaInsets support PinLayout can easily handle iOS 11 `UIView.safeAreaInsets`, but it goes even further by supporting safeAreaInsets for previous iOS releases (including iOS 7/8/9/10) by adding a property `UIView.pin.safeArea`. [See here for more details](#safeAreaInsets) - - ### macOS support PinLayout support macOS 10.9+. :pushpin: In this documentation, any methods with parameters of type UIView or UIEdgeInsets are also supported on macOS, using NSView and NSEdgeInsets. See [macOS Support](#macos_support) for more information. + ### Right to left languages (RTL) support -PinLayout supports left-to-right (LTR) and right-to-left (RTL) languages. [See here for more details](docs/rtl_support.md). +PinLayout supports left-to-right (LTR) and right-to-left (RTL) languages. +#### [See here for more details](docs/rtl_support.md).
- -## Layout using distances from superview’s edges + +## Edges layout PinLayout can position a view’s edge relative to its superview edges. +###### Example: +This example layout the view A to fit its superview frame with a margin of 10 pixels. It pins the top, left, bottom and right edges. + + + + +```swift + viewA.pin.top(10).bottom(10).left(10).right(10) +``` + +Another shorter possible solution using `all()`: + +```swift + view.pin.all(10) +``` + **Methods**: -* **`top(:CGFloat)`** / **`top(:Percent)`** / **`top()`** / **`top(:UIEdgeInsets)`** -The value specifies the top edge distance from the superview's top edge in pixels (or in percentage of its superview's height). `top()` is similar to calling `top(0)`, it position the view top edge directly on its superview top edge. `top(:UIEdgeInsets)` use the `UIEdgeInsets.top` property, is particularly useful with [`UIView.pin.safeArea`](#safeAreaInsets) or `UIView.safeAreaInsets`. +The following methods are used to position a view’s edge relative to its superview edges. + +:pushpin: The offset/margin parameter in the following methods can be either positive and negative. In general cases positive values are used. + +* **`top(_ offset: CGFloat)`** / **`top(_ offset: Percent)`** / **`top()`** / **`top(_ margin: UIEdgeInsets)`** +Position the top edge. The offset specifies the top edge distance from the superview's top edge in pixels (or in percentage of its superview's height). `top()` is similar to calling `top(0)`, it position the view top edge directly on its superview top edge. `top(:UIEdgeInsets)` use the `UIEdgeInsets.top` property, is particularly useful with [safeArea, readable and layout margins](#safeAreaInsets). + +* **`bottom(_ offset: CGFloat)`** / **`bottom(_ offset: Percent)`** / **`bottom()`** / **`bottom(_ margin: UIEdgeInsets)`** +Position the bottom edge. The offset specifies the bottom edge **distance from the superview's bottom edge** in pixels (or in percentage of its superview's height). `bottom()` is similar to calling `bottom(0)`, it position the view bottom edge directly on its superview top edge. `bottom(:UIEdgeInsets)` use the `UIEdgeInsets.bottom` property, it is is particularly useful with [safeArea, readable and layout margins](#safeAreaInsets). -* **`bottom(:CGFloat)`** / **`bottom(:Percent)`** / **`bottom()`** / **`bottom(:UIEdgeInsets)`** -The value specifies the bottom edge **distance from the superview's bottom edge** in pixels (or in percentage of its superview's height). `bottom()` is similar to calling `bottom(0)`, it position the view bottom edge directly on its superview top edge. `bottom(:UIEdgeInsets)` use the `UIEdgeInsets.bottom` property, it is is particularly useful with [`UIView.pin.safeArea`](#safeAreaInsets) or `UIView.safeAreaInsets`. +* **`left(_ offset: CGFloat)`** / **`left(_ offset: Percent)`** / **`left()`** / **`left(_ margin: UIEdgeInsets)`** +Position the left edge. The offset specifies the left edge distance from the superview's left edge in pixels (or in percentage of its superview's width). `left()` is similar to calling `left(0)`, it position the view left edge directly on its superview left edge. `left(:UIEdgeInsets)` use the `UIEdgeInsets.left` property, it is particularly useful with [safeArea, readable and layout margins](#safeAreaInsets). -* **`left(:CGFloat)`** / **`left(:Percent)`** / **`left()`** / **`left(:UIEdgeInsets)`** -The value specifies the left edge distance from the superview's left edge in pixels (or in percentage of its superview's width). `left()` is similar to calling `left(0)`, it position the view left edge directly on its superview left edge. `left(:UIEdgeInsets)` use the `UIEdgeInsets.left` property, it is particularly useful with [`UIView.pin.safeArea`](#safeAreaInsets) or `UIView.safeAreaInsets`. +* **`right(_ offset: CGFloat)`** / **`right(_ offset: Percent)`** / **`right()`** / **`right(_ margin: UIEdgeInsets)`** +Position the right edge. The offset specifies the right edge **distance from the superview's right edge** in pixels (or in percentage of its superview's width). `right()` is similar to calling `right(0)`, it position the view right edge directly on its superview right edge. `right(:UIEdgeInsets)` use the `UIEdgeInsets. right` property, it is particularly useful with [safeArea, readable and layout margins](#safeAreaInsets). -* **`right(:CGFloat)`** / **`right(:Percent)`** / **`right()`** / **`right(:UIEdgeInsets)`** -The value specifies the right edge **distance from the superview's right edge** in pixels (or in percentage of its superview's width). `right()` is similar to calling `right(0)`, it position the view right edge directly on its superview right edge. `right(:UIEdgeInsets)` use the `UIEdgeInsets. right` property, it is particularly useful with [`UIView.pin.safeArea`](#safeAreaInsets) or `UIView.safeAreaInsets`. +* **`vCenter(_ offset: CGFloat)`** / **`vCenter(_ offset: Percent)`** / **`vCenter()`** +Position the vertical center (center.y). The offset specifies the distance vertically of the view's center related to the superview's center in pixels (or in percentage of its superview's height). A positive offset move the view down and a negative value move it up relative to the superview's center. `vCenter()` is similar to calling `vCenter(0)`, it position vertically the view's center directly on its superview vertical center. -* **`vCenter(:CGFloat)`** / **`vCenter(:Percent)`** / **`vCenter()`** -The value specifies the distance vertically of the view's center related to the superview's center in pixels (or in percentage of its superview's height). A positive value move the view down and a negative value move it up relative to the superview's center. `vCenter()` is similar to calling `vCenter(0)`, it position vertically the view's center directly on its superview vertical center. +* **`hCenter(_ offset: CGFloat)`** / **`hCenter(_ offset: Percent)`** / **`hCenter()`** +Position the horizontal center (center.x). The offset specifies the distance horizontally of the view's center related to the superview's center in pixels (or in percentage of its superview's width). A positive offset move the view to the right and a negative offset move it to the left relative to the superview's center. `hCenter()` is similar to calling `hCenter(0)`, it position horizontally the view's center directly on its superview horizontal center. -* **`hCenter(:CGFloat)`** / **`hCenter(:Percent)`** / **`hCenter()`** -The value specifies the distance horizontally of the view's center related to the superview's center in pixels (or in percentage of its superview's width). A positive value move the view to the right and a negative value move it to the left relative to the superview's center. `hCenter()` is similar to calling `hCenter(0)`, it position horizontally the view's center directly on its superview horizontal center. +##### Methods supporting LTR (left-to-right) and RTL (right-to-left) languages. -* **`start(:CGFloat)`** / **`start(:Percent)`** / **`start()`** / **`start(:UIEdgeInsets)`** :left_right_arrow: -In LTR direction the value specifies the left edge distance from the superview's left edge in pixels (or in percentage of its superview's width). -In RTL direction the value specifies the right edge distance from the superview's right edge in pixels (or in percentage of its superview's width). -`start()` is similar to calling `start(0)`. `start(:UIEdgeInsets)` use the `UIEdgeInsets.left` property in LTR direction and `UIEdgeInsets.right` in RTL direction, it is particularly useful with [`UIView.pin.safeArea`](#safeAreaInsets) or `UIView.safeAreaInsets`. +* **`start(_ offset: CGFloat)`** / **`start(_ offset: Percent)`** / **`start()`** / **`start(_ margin: UIEdgeInsets)`** :left_right_arrow: +Position the left or right edge depending of the LTR language direction. In LTR direction the offset specifies the left edge distance from the superview's left edge in pixels (or in percentage of its superview's width). In RTL direction the offset specifies the right edge distance from the superview's right edge in pixels (or in percentage of its superview's width). +`start()` is similar to calling `start(0)`. `start(:UIEdgeInsets)` use the `UIEdgeInsets.left` property in LTR direction and `UIEdgeInsets.right` in RTL direction. -* **`end(:CGFloat)`** / **`end(:Percent)`** / **`end()`** / **`end(:UIEdgeInsets)`** :left_right_arrow: -In LTR direction the value specifies the right edge distance from the superview's right edge in pixels (or in percentage of its superview's width). -In RTL direction the value specifies the left edge distance from the superview's left edge in pixels (or in percentage of its superview's width). `end()` is similar to calling `end(0)`. `end(:UIEdgeInsets)` use the `UIEdgeInsets.right` property in LTR direction and `UIEdgeInsets.left` in RTL direction, it is particularly useful with [`UIView.pin.safeArea`](#safeAreaInsets) or `UIView.safeAreaInsets`. +* **`end(_ offset: CGFloat)`** / **`end(_ offset: Percent)`** / **`end()`** / **`end(_ margin: UIEdgeInsets)`** :left_right_arrow: +Position the left or right edge depending of the LTR language direction. In LTR direction the offset specifies the right edge distance from the superview's right edge in pixels (or in percentage of its superview's width). In RTL direction the offset specifies the left edge distance from the superview's left edge in pixels (or in percentage of its superview's width). `end()` is similar to calling `end(0)`. `end(:UIEdgeInsets)` use the `UIEdgeInsets.right` property in LTR direction and `UIEdgeInsets.left` in RTL direction. **Methods pinning multiple edges**: -* **`all(:CGFloat)`** / **`all(:UIEdgeInsets)`** / **`all()`** -The value/insets specifies the **top, bottom, left and right edges** distance from the superview's corresponding edge in pixels. Similar to calling `view.top(value).bottom(value).left(value).right(value)`. +* **`all(_ margin: CGFloat)`** / **`all()`** / **`all(_ margin: UIEdgeInsets)`** +Position the top, left, bottom and right edges. The margin specifies the **top, bottom, left and right edges** distance from the superview's corresponding edge in pixels. Similar to calling `view.top(value).bottom(value).left(value).right(value)`. `all()` is similar to calling `all(0)`. -`all(:UIEdgeInsets)` is particularly useful with [`UIView.pin.safeArea`](#safeAreaInsets) or `UIView.safeAreaInsets`. +`all(:UIEdgeInsets)` is particularly useful with [safeArea, readable and layout margins](#safeAreaInsets). -* **`horizontally(:CGFloat)`** / **`horizontally(:Percent)`** / -**`horizontally(:UIEdgeInsets)`** / **`horizontally()`** -The value specifies the **left and right edges** on its superview's corresponding edges in pixels (or in percentage of its superview's width). +* **`horizontally(_ margin: CGFloat)`** / **`horizontally(_ margin: Percent)`** / **`horizontally()`** / **`horizontally(_ margin: UIEdgeInsets)`** +Position the left and right edges. The margin specifies the **left and right edges** distance from its superview's corresponding edges in pixels (or in percentage of its superview's width). `horizontally()` is similar to calling `horizontally(0)`. `horizontally(:UIEdgeInsets)` use the UIEdgeInsets's left and right value to pin left and right edges. -* **`vertically(:CGFloat)`** / **`vertically(:Percent)`** -**`vertically(:UIEdgeInsets)`** / **`vertically()`** -The value specifies the **top and bottom edges** on its superview's corresponding edges in pixels (or in percentage of its superview's height). +* **`vertically(_ margin: CGFloat)`** / **`vertically(_ margin: Percent)`** / **`vertically()`** / **`vertically(_ margin: UIEdgeInsets)`** +Position the top and bottom edges. The margin specifies the **top and bottom edges** distance from on its superview's corresponding edges in pixels (or in percentage of its superview's height). `vertically()` is similar to calling `vertically(0)`. `vertically(:UIEdgeInsets)` use the UIEdgeInsets's top and bottom value to pin top and bottom edges. - ###### Usage Examples: ```swift @@ -259,401 +280,547 @@ The value specifies the **top and bottom edges** on its superview's correspondin view.pin.top().horizontally() // The view is pinned at the top edge of its parent and fill it horizontally. ``` +
+ +#### Layout multiple edges relative to superview + +This section describe methods that are similar to methods describe in the previous section [Edges layout](#layout_edges), except that they position 2 edges simultaneously. They can be used as shortcuts to set 2 consecutive edges. + + + ###### Example: -This example layout the view A to fit its superview frame with a margin of 10 pixels. It pins the top, left, bottom and right edges. +This example position the view’s on the top-right corner of its superview’s topRight and set its size to 100 pixels. - + ```swift - viewA.pin.top(10).bottom(10).left(10).right(10) + viewA.pin.topRight().size(100) ``` -Another shorter possible solution using `all()`: +This is equivalent to: ```swift - view.pin.all(10) + viewA.pin.top().right().size(100) ``` +**Methods:** -
+:pushpin: The offset parameter in the following methods can be either positive and negative. In general cases positive values are used. - -## Edges +* **`topLeft(_ offset: CGFloat)`** / **`topLeft()`** +Position the top and left edges. The offset specifies the distance from their superview's corresponding edges in pixels. `topLeft()` is similar to calling `topLeft(0)`. -### PinLayout UIView’s edges +* **`topCenter(_ topOffset: CGFloat)`** / **`topCenter()`** +Position the top and horizontal center (center.x). The offset specifies the top edge distance from the superview's top edge in pixels. `topCenter()` is similar to calling `topCenter(0)`. -PinLayout adds edges properties to UIView/NSView. These properties are used to reference other view’s edges. +* **`topRight(_ offset: CGFloat)`** / **`topRight()`** +Position the top and right edges. The offset specifies the distance from their superview's corresponding edges in pixels. `topRight()` is similar to calling `topRight(0)`. -**PinLayout View’s edges**: -* `UIView.edge.top` -* `UIView.edge.vCenter` -* `UIView.edge.bottom` -* `UIView.edge.left` -* `UIView.edge.hCenter` -* `UIView.edge.right` -* `UIView.edge.start`:left_right_arrow: -* `UIView.edge.end`:left_right_arrow: +* **`centerLeft(_ leftOffset: CGFloat)`** / **`centerLeft()`** +Position the vertical center (center.y) and the left edge. The offset specifies the left edge distance from the superview's left edge in pixels. `centerLeft()` is similar to calling `centerLeft(0)`. - +* **`center(_ offset: CGFloat)`** / **`center()`** +Position the horizontal and vertical center (center.y). The offset specifies an offset from the superview's center in pixels. `center()` is similar to calling `center(0)`. +* **`centerRight(_ rightOffset: CGFloat)`** / **`centerRight()`** +Position the vertical center (center.y) and the right edge. The offset specifies the right edge distance from the superview's right edge in pixels. `centerRight()` is similar to calling `centerRight(0)`. -### Layout using edges -PinLayout has methods to attach a View's edge (top, left, bottom, right, start or end edge) to another view’s edge. +* **`bottomLeft(_ offset: CGFloat)`** / **`bottomLeft()`** +Position the bottom and left edges. The offset specifies the distance from their superview's corresponding edges in pixels. `bottomLeft()` is similar to calling `bottomLeft(0)`. + +* **`bottomCenter(_ bottomOffset: CGFloat)`** / **`bottomCenter()`** +Position the bottom and horizontal center (center.x). The offset specifies the bottom edge distance from the superview's bottom edge in pixels. `bottomCenter()` is similar to calling `bottomCenter(0)`. + +* **`bottomRight(_ offset: CGFloat)`** / **`bottomRight()`** +Position the bottom and right edges. The offset specifies the distance from their superview's corresponding edges in pixels. `bottomRight()` is similar to calling `bottomRight(0)`. + + +##### Methods supporting LTR (left-to-right) and RTL (right-to-left) languages. + +* **`topStart(_ offset: CGFloat)`** / **`topStart()`** :left_right_arrow: +In LTR direction position the top and left edges. +In RTL direction position the top and right edges. + +* **`topEnd(_ offset: CGFloat)`** / **`topEnd()`** :left_right_arrow: +In LTR direction position the top and right edges. +In RTL direction position the top and left edges. + +* **`bottomStart(_ offset: CGFloat)`** / **`bottomStart()`** :left_right_arrow: +In LTR direction position the bottom and left edges. +In RTL direction position the bottom and right edges. + +* **`bottomEnd(_ offset: CGFloat)`** / **`bottomEnd()`** :left_right_arrow: +In LTR direction position the bottom and right edges. +In RTL direction position the bottom and left edges. + +* **`centerStart(_ offset: CGFloat)`** / **`centerStart()`** :left_right_arrow: +In LTR direction position the vertical center (center.y) and the left edge. +In RTL direction position the vertical center (center.y) and the right edge. + +* **`centerEnd(_ offset: CGFloat)`** / **`centerEnd()`** :left_right_arrow: +In LTR direction position the vertical center (center.y) and the right edge. +In RTL direction position the vertical center (center.y) and the left edge. + + +###### Usage Examples: + +```swift + // Position a view at the top left corner with a top and left margin of 10 pixels + view.pin.topLeft(10) + + // Position the 4 edges with a margin of 10 pixels. + view.pin.topLeft(10).bottomRight(10) +``` + +
+ + +## Relative Edges layout + +### Layout using relative positioning + +PinLayout has methods to position relative to other views. The view can be layouted relative to **one or many relative views**. The following methods layout one view's edge (top, bottom, left or right). **Methods:** -* **`top(to edge: ViewEdge)`**: -Position the view's top edge directly on another view’s edge (top/vCenter/bottom). +* **`above(of: UIView)`** / **`above(of: [UIView])`** +Position the view above the specified view(s). One or many relative views can be specified. This method position the view’s bottom edge. -* **`vCenter(to edge: ViewEdge)`**: -Position vertically the view's center directly on another view’s edge (top/vCenter/bottom). +* **`below(of: UIView)`** / **`below(of: [UIView])`** +Position the view below the specified view(s). One or many relative views can be specified. This method position the view’s top edge. -* **`bottom(to edge: ViewEdge)`**: -Position the view's bottom edge directly on another view’s edge (top/vCenter/bottom). +* **`before(of: UIView)`** / **`before(of: [UIView])`** :left_right_arrow: +In LTR direction the view is positioned at the left of the specified view(s). In RTL direction the view is positioned at the right. One or many relative views can be specified. -* **`left(to: edge: ViewEdge)`**: -Position the view's left edge directly on another view’s edge (left/hCenter/right). +* **`after(of: UIView)`** / **`after(of: [UIView])`**:left_right_arrow: +In LTR direction the view is positioned at the right of the specified view(s). In RTL direction the view is positioned at the left. One or many relative views can be specified. -* **`hCenter(to: edge: ViewEdge)`**: -Position horizontally the view's center directly on another view’s edge (left/hCenter/right). +* **`left(of: UIView)`** / **`left(of: [UIView])`** +Position the view left of the specified view(s). Similar to `before(of:)`. One or many relative views can be specified. This method position the view’s right edge. -* **`right(to: edge: ViewEdge)`**: -Position the view's right edge directly on another view’s edge (left/hCenter/right). +* **`right(of: UIView)`** / **`right(of: [UIView])`** +Position the view right of the specified view(s). Similar to `after(of:)`. One or many relative views can be specified. This method position the view’s left edge. -* **`start(to: edge: ViewEdge)`**:left_right_arrow: -In LTR direction it position the view's left edge directly on another view’s edge. -In RTL direction it position the view's right edge directly on another view’s edge. - -* **`end(to: edge: ViewEdge)`**:left_right_arrow: -In LTR direction it position the view's top edge directly on another view’s edge. -In RTL direction it position the view's bottom edge directly on another view’s edge. +:pushpin: **Multiple relative views**: If for example a call to `below(of: [...]) specify multiple relative views, the view will be layouted below *ALL* these views. -:pushpin: These methods can pin a view’s edge to any other view's edge, even if don't have the same direct superview! It works with any views that have at some point the same ancestor. +:pushpin: These methods can pin a view’s relative to any views, even if they don't have the same direct superview! It works with any views that have a shared ancestor. ###### Usage examples: ```swift - view.pin.left(to: view1.edge.right) - view.pin.left(to: view1.edge.right).top(to: view2.edge.right) + view.pin.after(of: view4).before(of: view1).below(of: view3) + view.pin.after(of: view2) + view.pin.below(of: [view2, view3, view4]) ``` -###### Example 1: -This example layout the view B left edge on the view A right edge. It only changes the view B left coordinate. +###### Example: +The following example will position the view C between the view A and B with margins of 10px using relative positioning methods. + + - ```swift - viewB.pin.left(to: viewA.edge.right) + viewC.pin.top().after(of: viewA).before(of: viewB).margin(10) ``` +This is an equivalent solution using [edges](#edge): -###### Example 2: -This example center horizontally the view B inside the view A with a top margin of 10 from the same view. - +```swift + viewC.pin.top().left(to: viewA.edge.right).right(to: viewB.edge.left). margin(10) +``` +This is also an equivalent solution using [horizontallyBetween()](#layout_between_w_alignment). See section [Layout between other views](#layout_between): ```swift - aView.pin.top(to: bView.edge.top).hCenter(to: bView.edge.hCenter).marginTop(10) -``` + viewC.pin.horizontallyBetween(viewA, and: viewB, aligned: .top).marginHorizontal(10) +```
- -## Anchors + +### Layout using Relative Edges and alignment -### PinLayout View’s anchors +PinLayout also has methods to position relative to other views but with also the ability to specify an **alignment**. The view can be layouted relative to **one or many relative views**. -PinLayout adds anchors properties to UIView/NSView. These properties are used to reference other view’s anchors. +This is really similar to [Relative Edges layout](#relative_edges_layout) except that here two edges are being layouted. -**PinLayout View’s anchors**: +**Methods:** -* `UIView.anchor.topLeft` / `UIView.anchor.topCenter` / `UIView.anchor.topRight` -* `UIView.anchor.topStart` / `UIView.anchor.topEnd`:left_right_arrow: -* `UIView.anchor.centerLeft` / `UIView.anchor.centers` / `UIView.anchor.centerRight` -* `UIView.anchor.centerStart` / `UIView.anchor.centerEnd`:left_right_arrow: -* `UIView.anchor.bottomLeft` / `UIView.anchor.bottomCenter` / `UIView.anchor.bottomRight` -* `UIView.anchor.bottomStart` / `UIView.anchor.bottomEnd`:left_right_arrow: +* **`above(of: UIView, aligned: HorizontalAlignment)`** +**`above(of: [UIView], aligned: HorizontalAlignment)`** +Position the view above the specified view(s) and aligned it using the specified HorizontalAlignment. One or many relative views can be specified. + +* **`below(of: UIView, aligned: HorizontalAlignment)`** +**`below(of: [UIView], aligned: HorizontalAlignment)`** +Position the view below the specified view(s) and aligned it using the specified HorizontalAlignment. One or many relative views can be specified. + +* **`before(of: UIView, aligned: VerticalAlignment)`**:left_right_arrow: +**`before(of: [UIView], aligned: VerticalAlignment)`**:left_right_arrow: +In LTR direction the view is positioned at the left of the specified view(s). In RTL direction the view is positioned at the right. One or many relative views can be specified. - +* **`after(of: UIView, aligned: VerticalAlignment)`**:left_right_arrow: +**`after(of: [UIView], aligned: VerticalAlignment)`**:left_right_arrow: +In LTR direction the view is positioned at the right of the specified view(s). In RTL direction the view is positioned at the left. One or many relative views can be specified. +* **`left(of: UIView, aligned: VerticalAlignment)`** +**`left(of: [UIView], aligned: VerticalAlignment)`** +Position the view left of the specified view(s) and aligned it using the specified VerticalAlignment. Similar to `before(of:)`. One or many relative views can be specified. + +* **`right(of: UIView, aligned: VerticalAlignment)`** +**`right(of: [UIView], aligned: VerticalAlignment)`** +Position the view right of the specified view(s) and aligned it using the specified VerticalAlignment. Similar to `after(of:)`. One or many relative views can be specified. -### Layout using anchors -PinLayout can use anchors to position view’s related to other views. +**`HorizontalAlignment` values:** -Following methods position the corresponding view anchor on another view’s anchor. +* **`.left`**: The view's left edge will be left-aligned with the relative view (or the left most view if a list of relative views is specified). +* **`.center`**: The view's will be horizontally centered with the relative view (or the average hCenter if a list of relative views is used). +* **`.right`**: The view's right edge will be right-aligned with the relative view (or the right most view if a list of relative views is specified). +* **`.start`**:left_right_arrow:: +In LTR direction, similar to using `.left`. +In RTL direction, similar to using `.right`. +* **`.end`**:left_right_arrow:: +In LTR direction, similar to using `.right`. +In RTL direction, similar to using `.left`. -**Methods:** +**`VerticalAlignment` values:** -* `topLeft(to anchor: Anchor)` -* `topCenter(to anchor: Anchor)` -* `topRight(to anchor: Anchor)` -* `topStart(to anchor: Anchor)`:left_right_arrow: -* `topEnd(to anchor: Anchor)`:left_right_arrow: -* `centerLeft(to anchor: Anchor)` -* `center(to anchor: Anchor)` -* `centerRight(to anchor: Anchor)` -* `centerStart(to anchor: Anchor)`:left_right_arrow: -* `centerEnd(to anchor: Anchor)`:left_right_arrow: -* `bottomLeft(to anchor: Anchor)` -* `bottomCenter(to anchor: Anchor)` -* `bottomRight(to anchor: Anchor)` -* `bottomStart(to anchor: Anchor)`:left_right_arrow: -* `bottomEnd(to anchor: Anchor)`:left_right_arrow: +* **`.top`**: The view's top edge will be top-aligned with the relative view (or the top most view if a list of relative views is specified). +* **`.center`**: The view's will be vertically centered with the relative view (or the average vCenter if a list of relative views is used). +* **`.bottom`**: The view's bottom edge will be bottom-aligned with the relative view (or the bottom most view if a list of relative views is specified). -:pushpin: These methods can pin a view’s anchor to any other view's anchor, even if don't have the same direct superview! It works with any views that have at some point the same ancestor. +:pushpin: **Multiple relative views**: If for example a call to `below(of: [...], aligned:) specify multiple relative views, the view will be layouted below *ALL* these views. The alignment will be applied using all relative views. + +:pushpin: These methods can layout a view’s relative to any views, even if they don't have the same direct superview/parent! It works with any views that have a shared ancestor. ###### Usage examples: ```swift - view.pin.topCenter(to: view1.anchor.bottomCenter) - view.pin.topLeft(to: view1.anchor.topLeft).bottomRight(to: view1.anchor.center) + view.pin.above(of: view2, aligned: .left) + view.pin.below(of: [view2, view3, view4], aligned: .left) + view.pin.after(of: view2, aligned: .top).before(of: view3, aligned: .bottom) ``` ###### Example: +The following example layout the view B below the view A aligned on its center. -Layout using an anchor. This example pins the view B topLeft anchor on the view A topRight anchor. - - + ```swift - viewB.pin.topLeft(to: viewA.anchor.topRight) -``` + viewB.pin.below(of: viewA, aligned: .center) +``` +This is an equivalent solution using anchors: -
+```swift + viewB.pin.topCenter(to: viewA.anchor.bottomCenter) +``` ###### Example: +The following example layout the view A **below the UIImageView and the UILabel**. +View A should be left aligned to the UIImageView and right aligned to the UILabel, with a top margin of 10 pixels. -Layout using multiple anchors. - -It is also possible to combine two anchors to pin the position and the size of a view. The following example will position the view C between the view A and B with horizontal margins of 10px. + - +```swift + a.pin.below(of: [imageView, label], aligned: .left).right(to: label.edge.right).marginTop(10) +``` +This is an equivalent solutions using other methods: ```swift - viewC.pin.topLeft(to: viewA.anchor.topRight) - .bottomRight(to: viewB.anchor.bottomLeft).marginHorizontal(10) + let maxY = max(imageView.frame.maxY, label.frame.maxY) // Not so nice + a.pin.top(maxY).left(to: imageView.edge.left).right(to: label.edge.right).marginTop(10) +``` + +### Positioning using only visible relative Views + +All PinLayout's relative methods can accept an array of Views (ex: `below(of: [UIView])`). Using these methods its possible to filter the list of relative Views before the list is used by PinLayout. + +You can define your own filter methods, but PinLayout has a filter method called `visible` that can be used to layout a view related to only visible views. This can be really useful when some views may be visible or hidden depending on the situation. + +```swift + view.pin.below(of: visible([ageSwitch, ageField])).horizontally(). ``` +Note that the **Form** example use this filter method, see [Examples App](#examples_app). +
-### Layout using superview’s anchors -PinLayout also has a shorter version that pins a view's anchor **directly** on it's corresponding superview’s anchor. -The following methods position the corresponding view's anchor on another view’s anchor. + +## Layout between other views + +PinLayout has methods to position a view between two other views, either horizontally or vertically. These methods layout 2 edges simultaneously. **Methods:** -* `topLeft()` / `topCenter()` / `topRight()` -* `topStart()` / `topEnd()` :left_right_arrow: -* `centerLeft()` / `center()` / `centerRight()` -* `centerStart()` / `centerEnd()` :left_right_arrow: -* `bottomLeft()` / `bottomCenter()` / `bottomRight()` -* `bottomStart()` / `bottomEnd()` :left_right_arrow: +* **`horizontallyBetween(:UIView, and: UIView)`** +Position the view between the two specified views horizontally. The method layout the view's left and right edges. The order of the reference views is irrelevant. +Note that the layout will be applied only if there is horizontal space between the specified views. + +* **`verticallyBetween(:UIView, and: UIView)`** +Position the view between the two specified views vertically. The method layout the view's top and bottom edges. The order of the reference views is irrelevant. Note that the layout will be applied only if there is vertical space between the specified views. + +:pushpin: These methods can use references to any views, even if they don't have the same direct superview/parent! It works with any views that have a shared ancestor. + +###### Usage examples: +```swift + view.pin.horizontallyBetween(viewA, and: viewB) + view.pin.verticallyBetween(viewC, and: viewD) +``` ###### Example: -For example .topRight() will pin the view’s topRight anchor on its superview’s topRight anchor. +This example position a view between two other views horizontally with a left and right margins of 5 pixels, and set its top edge at 10 pixels. - + ```swift - viewA.pin.topRight() -``` + view.pin.horizontallyBetween(viewA, and: viewB).top(10).marginHorizontal(5) +``` -This is equivalent to: +Note that the same result can also be achieved using an alignment parameter, describe in the [next section](#layout_between_w_alignment): ```swift - viewA.pin.topRight(to: superview.anchor.topRight) - // OR - viewA.pin.top().right() + view.pin.horizontallyBetween(viewA, and: viewB, aligned: .top).marginHorizontal(5) ``` -
+Or using [Relative Edges layout](#relative_edges_layout): + +```swift + view.pin.after(of: viewA).before(of: viewB).top(10).marginHorizontal(5) +``` - -## Relative positioning -### Layout using edges relative positioning + + +### Layout between other views with alignment + +PinLayout has also methods to position a view between two other views, either horizontally or vertically, but with also the ability to specify an **alignment**. -PinLayout also has methods to position relative to other views. The view can be layouted relative to **one or many relative views**. +This is really similar to the [previous section methods](#layout_between) except that here an alignment is specified and **three edges** are being layouted simultaneously. **Methods:** -* **`above(of: UIView)`** / **`above(of: [UIView])`** -Position the view above the specified view(s). One or many relative views can be specified. This method position the view’s bottom edge. -* **`below(of: UIView)`** / **`below(of: [UIView])`** -Position the view below the specified view(s). One or many relative views can be specified. This method position the view’s top edge. -* **`before(of: UIView)`** / **`before(of: [UIView])`** :left_right_arrow: -In LTR direction the view is positioned at the left of the specified view(s). In RTL direction the view is positioned at the right. One or many relative views can be specified. -* **`after(of: UIView)`** / **`after(of: [UIView])`**:left_right_arrow: -In LTR direction the view is positioned at the right of the specified view(s). In RTL direction the view is positioned at the left. One or many relative views can be specified. -* **`left(of: UIView)`** / **`left(of: [UIView])`** -Position the view left of the specified view(s). Similar to `before(of:)`. One or many relative views can be specified. This method position the view’s right edge. -* **`right(of: UIView)`** / **`right(of: [UIView])`** -Position the view right of the specified view(s). Similar to `after(of:)`. One or many relative views can be specified. This method position the view’s left edge. +* **`horizontallyBetween(:UIView, and: UIView, aligned: VerticalAlign)`** +Position the view between the two specified views horizontally and aligned it using the specified VerticalAlign. The view will be aligned related to the first specified reference view. Note that the layout will be applied only if there is horizontal space between the specified views. -:pushpin: **Multiple relative views**: If for example a call to `below(of: [...]) specify multiple relative views, the view will be layouted below *ALL* these views. +* **`verticallyBetween(:UIView, and: UIView, aligned: HorizontalAlign)`** +Position the view between the two specified views vertically and aligned it using the specified HorizontalAlign. The view will be aligned related to the first specified reference view. Note that the layout will be applied only if there is vertical space between the specified views. -:pushpin: These methods **set the position of a view's edge**: top, left, bottom or right. For example `below(of ...)` set the view's top edge, `right(of ...) set the view's left edge, ... +:pushpin: These methods will apply the alignment related to the first specified reference view. If you want to align it using the second reference view, simply swap views parameters. + +:pushpin: These methods can use references to any views, even if they don't have the same direct superview/parent! It works with any views that have a shared ancestor. + +**HorizontalAlignment values:** + +* **`.left`**: The view's left edge will be left-aligned with the first view. +* **`.center`**: The view's will be horizontally centered with the first view. +* **`.right`**: The view's right edge will be right-aligned with the first view. +* **`.start`**:left_right_arrow:: +In LTR direction, similar to using `.left`. +In RTL direction, similar to using `.right`. +* **`.end`**:left_right_arrow:: +In LTR direction, similar to using `.right`. +In RTL direction, similar to using `.left`. -:pushpin: These methods can pin a view’s relative to any views, even if don't have the same direct superview! It works with any views that have at some point the same ancestor. +**VerticalAlignment values:** + +* **`.top`**: The view's top edge will be top-aligned with the first view. +* **`.center`**: The view's will be vertically centered with the first view. +* **`.bottom`**: The view's bottom edge will be bottom-aligned with the first view. ###### Usage examples: ```swift - view.pin.after(of: view4).before(of: view1).below(of: view3) - view.pin.after(of: view2) - view.pin.below(of: [view2, view3, view4]) + view.pin.horizontallyBetween(viewA, and: viewB, aligned: .top) + view.pin.verticallyBetween(viewC, and: viewD, aligned: .center) ``` ###### Example: -The following example will position the view C between the view A and B with margins of 10px using relative positioning methods. +This example position a view between two other views vertically, and center it relative to the first view with an top and bottom margin of 10 pixels. - + -```swift - viewC.pin.top().after(of: viewA).before(of: viewB).margin(10) -``` -This is an equivalent solution using [edges](#edge): ```swift - viewC.pin.top().left(to: viewA.edge.right).right(to: viewB.edge.left).margin(10) + view.pin.verticallyBetween(viewA, and: viewB, aligned: .center).marginVertical(10) ``` -This is also an equivalent solution using [relative positioning and alignment](#relative_positioning_w_alignment) explained in the next section: +
-```swift - viewC.pin.after(of: viewA, aligned: .top).before(of: viewB, aligned: top).marginHorizontal(10) -``` + +## Edges +### PinLayout UIView’s edges -
+PinLayout adds edges properties to UIView/NSView. These properties are used to reference other view’s edges. - -### Layout using relative positioning and alignment +**PinLayout View’s edges**: -PinLayout also has methods to position relative to other views but with also the ability to specify the **alignment**. The view can be layouted relative to **one or many relative views**. +* `UIView.edge.top` +* `UIView.edge.vCenter` +* `UIView.edge.bottom` +* `UIView.edge.left` +* `UIView.edge.hCenter` +* `UIView.edge.right` +* `UIView.edge.start`:left_right_arrow: +* `UIView.edge.end`:left_right_arrow: + -**Methods:** -* **`above(of: UIView, aligned: HorizontalAlignment)`** -**`above(of: [UIView], aligned: HorizontalAlignment)`** -Position the view above the specified view(s) and aligned it using the specified HorizontalAlignment. One or many relative views can be specified. This method is similar to pinning one view’s anchor: bottomLeft, bottomCenter or bottomRight. - -* **`below(of: UIView, aligned: HorizontalAlignment)`** -**`below(of: [UIView], aligned: HorizontalAlignment)`** -Position the view below the specified view(s) and aligned it using the specified HorizontalAlignment. One or many relative views can be specified. This method is similar to pinning one view’s anchor: topLeft, topCenter or topRight. - -* **`before(of: UIView, aligned: HorizontalAlignment)`**:left_right_arrow: -**`before(of: [UIView], aligned: HorizontalAlignment)`**:left_right_arrow: -In LTR direction the view is positioned at the left of the specified view(s). In RTL direction the view is positioned at the right. One or many relative views can be specified. +### Layout using edges -* **`after(of: UIView, aligned: HorizontalAlignment)`**:left_right_arrow: -**`after(of: [UIView], aligned: HorizontalAlignment)`**:left_right_arrow: -In LTR direction the view is positioned at the right of the specified view(s). In RTL direction the view is positioned at the left. One or many relative views can be specified. +PinLayout has methods to attach a View's edge (top, left, bottom, right, start or end edge) to another view’s edge. -* **`left(of: UIView, aligned: VerticalAlignment)`** -**`left(of: [UIView], aligned: HorizontalAlignment)`** -Position the view left of the specified view(s) and aligned it using the specified VerticalAlignment. Similar to `before(of:)`. One or many relative views can be specified. This method is similar to pinning one view’s anchor: topRight, centerRight or bottomRight. - -* **`right(of: UIView, aligned: VerticalAlignment)`** -**`right(of: [UIView], aligned: HorizontalAlignment)`** -Position the view right of the specified view(s) and aligned it using the specified VerticalAlignment. Similar to `after(of:)`. One or many relative views can be specified. This method is similar to pinning one view’s anchor: topLeft, centerLeft or bottomLeft. +**Methods:** + +* **`top(to edge: ViewEdge)`**: +Position the view's top edge directly on another view’s edge (top/vCenter/bottom). +* **`vCenter(to edge: ViewEdge)`**: +Position vertically the view's center directly on another view’s edge (top/vCenter/bottom). -**How alignment is applied:** +* **`bottom(to edge: ViewEdge)`**: +Position the view's bottom edge directly on another view’s edge (top/vCenter/bottom). -* **`HorizontalAlignment.left`**: The view's left edge will be aligned to the left most relative view. -* **`HorizontalAlignment.center`**: The view's hCenter edge will be aligned with the average hCenter of all relative views. -* **`HorizontalAlignment.right`**: The view's right edge will be aligned to the right most relative view. -* **`HorizontalAlignment.start`**:left_right_arrow:: -In LTR direction the view's left edge will be aligned to the left most relative view. -In RTL direction the view's right edge will be aligned to the right most relative view. -* **`HorizontalAlignment.end`**:left_right_arrow:: -In LTR direction the view's right edge will be aligned to the right most relative view. -In RTL direction the view's left edge will be aligned to the right most relative view. -* **`VerticalAlignment.top`**: The view's top edge will be aligned to the top most relative view. -* **`VerticalAlignment.center`**: The view's vCenter edge will be aligned with the average vCenter of all relative views. -* **`VerticalAlignment.bottom`**: The view's bottom edge will be aligned to the bottom most relative view. +* **`left(to: edge: ViewEdge)`**: +Position the view's left edge directly on another view’s edge (left/hCenter/right). -:pushpin: **Multiple relative views**: If for example a call to `below(of: [...], aligned:) specify multiple relative views, the view will be layouted below *ALL* these views. The alignment will be applied using all relative view +* **`hCenter(to: edge: ViewEdge)`**: +Position horizontally the view's center directly on another view’s edge (left/hCenter/right). -:pushpin: These methods **set the position of a view's anchor**: topLeft, topCenter, topRight, centerLeft, .... For example `below(of ..., aligned: .right)` set the view's topRight anchor, `right(of ..., aligned: .center) set the view's centerLeft anchor, ... +* **`right(to: edge: ViewEdge)`**: +Position the view's right edge directly on another view’s edge (left/hCenter/right). -:pushpin: These methods **set the position of a view's edge**: top, left, bottom or right. For example `below(of ...)` set the view's top edge, `right(of ...) set the view's left edge, ... +* **`start(to: edge: ViewEdge)`**:left_right_arrow: +In LTR direction it position the view's left edge directly on another view’s edge. +In RTL direction it position the view's right edge directly on another view’s edge. + +* **`end(to: edge: ViewEdge)`**:left_right_arrow: +In LTR direction it position the view's top edge directly on another view’s edge. +In RTL direction it position the view's bottom edge directly on another view’s edge. +:pushpin: These methods can pin a view’s edge to any other view's edge, even if they don't have the same direct superview! It works with any views that have a shared ancestor. ###### Usage examples: ```swift - view.pin.above(of: view2, aligned: .left) - view.pin.below(of: [view2, view3, view4], aligned: .left) - view.pin.after(of: view2, aligned: .top).before(of: view3, aligned: .bottom) + view.pin.left(to: view1.edge.right) + view.pin.left(to: view1.edge.right).top(to: view2.edge.right) ``` -###### Example: -The following example layout the view B below the view A aligned on its center. +###### Example 1: +This example layout the view B left edge on the view A right edge. It only changes the view B left coordinate. - + +```swift + viewB.pin.left(to: viewA.edge.right) +``` + +###### Example 2: +This example center horizontally the view B inside the view A with a top margin of 10 from the same view. + + + +```swift + aView.pin.top(to: bView.edge.top).hCenter(to: bView.edge.hCenter).marginTop(10) +``` + +
+ + +## Anchors + +### PinLayout View’s anchors + +PinLayout adds anchors properties to UIView/NSView. These properties are used to reference other view’s anchors. + +**PinLayout View’s anchors**: + +* `UIView.anchor.topLeft` / `UIView.anchor.topCenter` / `UIView.anchor.topRight` +* `UIView.anchor.topStart` / `UIView.anchor.topEnd`:left_right_arrow: +* `UIView.anchor.centerLeft` / `UIView.anchor.centers` / `UIView.anchor.centerRight` +* `UIView.anchor.centerStart` / `UIView.anchor.centerEnd`:left_right_arrow: +* `UIView.anchor.bottomLeft` / `UIView.anchor.bottomCenter` / `UIView.anchor.bottomRight` +* `UIView.anchor.bottomStart` / `UIView.anchor.bottomEnd`:left_right_arrow: + + + + +### Layout using anchors + +PinLayout can use anchors to position view’s related to other views. + +Following methods position the corresponding view anchor on another view’s anchor. + +**Methods:** + +* `topLeft(to anchor: Anchor)` +* `topCenter(to anchor: Anchor)` +* `topRight(to anchor: Anchor)` +* `topStart(to anchor: Anchor)`:left_right_arrow: +* `topEnd(to anchor: Anchor)`:left_right_arrow: +* `centerLeft(to anchor: Anchor)` +* `center(to anchor: Anchor)` +* `centerRight(to anchor: Anchor)` +* `centerStart(to anchor: Anchor)`:left_right_arrow: +* `centerEnd(to anchor: Anchor)`:left_right_arrow: +* `bottomLeft(to anchor: Anchor)` +* `bottomCenter(to anchor: Anchor)` +* `bottomRight(to anchor: Anchor)` +* `bottomStart(to anchor: Anchor)`:left_right_arrow: +* `bottomEnd(to anchor: Anchor)`:left_right_arrow: -```swift - viewB.pin.below(of: viewA, aligned: .center) -``` -This is an equivalent solution using anchors: +:pushpin: These methods can pin a view’s anchor to any other view's anchor, even if they don't have the same direct superview! It works with any views that have a shared ancestor. +###### Usage examples: ```swift - viewB.pin.topCenter(to: viewA.anchor.bottomCenter) + view.pin.topCenter(to: view1.anchor.bottomCenter) + view.pin.topLeft(to: view1.anchor.topLeft).bottomRight(to: view1.anchor.center) ``` -###### Example: -The following example layout the view A **below the UIImageView and the UILabel**. -View A should be left aligned to the UIImageView and right aligned to the UILabel, with a top margin of 10 pixels. +###### Example 1: - +Layout using an anchor. This example pins the view B topLeft anchor on the view A topRight anchor. + -```swift - a.pin.below(of: [imageView, label], aligned: .left).right(to: label.edge.right).marginTop(10) -``` -This is an equivalent solutions using other methods: ```swift - let maxY = max(imageView.frame.maxY, label.frame.maxY) // Not so nice - a.pin.top(maxY).left(to: imageView.edge.left).right(to: label.edge.right).marginTop(10) -``` + viewB.pin.topLeft(to: viewA.anchor.topRight) +``` -
+###### Example 2: +This example center the view B on the view A's top-right anchor. -### Positioning using only visible relative Views + -All PinLayout's relative methods can accept an array of Views (ex: `below(of: [UIView])`). Using these methods its possible to filter the list of relative Views before the list is used by PinLayout. -PinLayout has a filter method called `visible` that can be used to layout a view related to only visible views. This can be really useful when some views may be visible or hidden depending on the situation. +```swift + viewB.pin.center(to: viewA.anchor.topRight) +``` -###### Example: -The following example contains a UISwitch. Below a UITextField that is visible only when the UISwitch is set to ON. And then follow another UITextField. This example use the `visible(views: [UIView]) -> [UIView]` filter method that returns only views with `UIView.isHidden` set to false or `UIView.alpha` greater than 0. +###### Example 3: - +Layout using multiple anchors. It is also possible to combine two anchors to pin the position and the size of a view. The following example will position the view C between the view A and B with horizontal margins of 10px. + ```swift - formTitleLabel.pin.topCenter().marginTop(margin) - nameField.pin.below(of: formTitleLabel).horizontally().height(40).margin(margin) - - ageSwitch.pin.below(of: nameField).horizontally().height(40).margin(margin) - ageField.pin.below(of: ageSwitch).horizontally().height(40).margin(margin) - - // Layout the Address UITextField below the last visible view, either ageSwitch or ageField. - addressField.pin.below(of: visibles([ageSwitch, ageField])).horizontally().height(40).margin(margin) + viewC.pin.topLeft(to: viewA.anchor.topRight) + .bottomRight(to: viewB.anchor.bottomLeft).marginHorizontal(10) ``` -Note that this example is extracted from the **Form** example, see [Examples App](#examples_app) +This is an another possible solution using [horizontallyBetween()](#layout_between_w_alignment): + +```swift + viewC.pin.horizontallyBetween(viewA, and: viewB, aligned: .top).height(of: viewA).marginHorizontal(10) +```
@@ -701,18 +868,23 @@ Set the view’s size to match the referenced view’s size
- -### sizeToFit + +### Adjusting size -**Method:** +PinLayout has methods to adjust the view’s size based on their content. -* **`sizeToFit(_ fitType: FitType)`** -The method adjust the view's size based on the view's `sizeThatFits()` method result. - PinLayout will adjust either the view's width or height based on the `fitType` parameter value. - - Notes: - * If margin rules apply, margins will be applied when determining the reference dimension (width/height). - * The resulting size will always respect `minWidth` / `maxWidth` / `minHeight` / `maxHeight`. +The resulting size will always respect [`minWidth`/`maxWidth`/`minHeight`/`maxHeight`](#minmax_width_height_size) values. + + +**Methods:** + +* **`sizeToFit()`** +The method adjust the view's size based on it's content requirements so that it uses the most appropriate amount of space. This fit type has the same effect as calling **sizeToFit()** on a view. The resulting size come from sizeThatFits(..) being called with the current view bounds. Particularly useful for controls/views that have an intrinsic size (label, button, ...). + +* **`sizeToFit(: FitType)`** +The method adjust the view's size based on the result of the method `sizeThatFits(:CGSize)`. + PinLayout will adjust either the view's width or height based on the `fitType` parameter value. + If margins are specified, they will be applied before calling the view's `sizeThatFits(:CGSize)` method. **Parameter `fitType`:** Identify the reference dimension (width / height) that will be used to adjust the view's size. @@ -732,6 +904,9 @@ The method adjust the view's size based on the view's `sizeThatFits()` method re ###### Usage examples: ```swift + // Adjust the view's size based on the result of `UIView.sizeToFit()` and center it. + view.pin.center().sizeToFit() + // Adjust the view's size based on a width of 100 pixels. // The resulting width will always match the pinned property `width(100)`. view.pin.width(100).sizeToFit(.width) @@ -821,49 +996,6 @@ This is an equivalent solutions using the `justify()` method. This method is exp
- - -## Aspect Ratio -Set the view aspect ratio. -AspectRatio solves the problem of knowing one dimension of an element and an aspect ratio, this is particularly useful for images. - -AspectRatio is applied only if a single dimension (either width or height) can be determined, in that case the aspect ratio will be used to compute the other dimension. - -* AspectRatio is defined as the ratio between the width and the height (width / height). -* An aspect ratio of 2 means the width is twice the size of the height. -* AspectRatio respects the min (minWidth/minHeight) and the max (maxWidth/maxHeight) - dimensions of an item. - -**Methods:** - -* **`aspectRatio(_ ratio: CGFloat)`**: -Set the view aspect ratio using a CGFloat. AspectRatio is defined as the ratio between the width and the height (width / height). - -* **`aspectRatio(of view: UIView)`**: -Set the view aspect ratio using another UIView's aspect ratio. - -* **`aspectRatio()`**: -If the layouted view is an UIImageView, this method will set the aspectRatio using the UIImageView's image dimension. For other types of views, this method as no impact. - -###### Usage examples: -```swift - aView.pin.left().width(100%).aspectRatio(2) - imageView.pin.left().width(200).aspectRatio() -``` - -###### Example: -This example layout an UIImageView at the top and center it horizontally, it also adjust its width to 50%. The view’s height will be adjusted automatically using the image aspect ratio. - - - - -```swift - imageView.pin.top().hCenter().width(50%).aspectRatio() -``` - - -
- ## Margins @@ -890,7 +1022,7 @@ Set the top and bottom margins to the specified value. * **`margin(:CGFloat)`** / **`margin(: Percent)`** Apply the value to all margins (top, left, bottom, right), in pixels or in percentage of its superview's width/height. * **`margin(:UIEdgeInsets)`** -Set all margins using an UIEdgeInsets. This method is particularly useful to set all margins using iOS 11 with `UIView.safeAreaInsets` or [`UIView.pin.safeArea`](#safeAreaInsets). +Set all margins using an UIEdgeInsets. This method is particularly useful to set all margins using [safeArea, readable and layout margins](#safeAreaInsets). * **`margin(_ insets: NSDirectionalEdgeInsets) `** Set all margins using an NSDirectionalEdgeInsets. This method is useful to set all margins using iOS 11 `UIView. directionalLayoutMargins` when layouting a view supporting RTL/LTR languages. * **`margin(_ vertical: CGFloat, _ horizontal: CGFloat)`** @@ -1036,8 +1168,67 @@ NOTE: In that in that particular situation, the same results could have been ach
+ + +## Aspect Ratio +Set the view aspect ratio. +AspectRatio solves the problem of knowing one dimension of an element and an aspect ratio, this is particularly useful for images. + +AspectRatio is applied only if a single dimension (either width or height) can be determined, in that case the aspect ratio will be used to compute the other dimension. + +* AspectRatio is defined as the ratio between the width and the height (width / height). +* An aspect ratio of 2 means the width is twice the size of the height. +* AspectRatio respects the min (minWidth/minHeight) and the max (maxWidth/maxHeight) + dimensions of an item. + +**Methods:** + +* **`aspectRatio(_ ratio: CGFloat)`**: +Set the view aspect ratio using a CGFloat. AspectRatio is defined as the ratio between the width and the height (width / height). + +* **`aspectRatio(of view: UIView)`**: +Set the view aspect ratio using another UIView's aspect ratio. + +* **`aspectRatio()`**: +If the layouted view is an UIImageView, this method will set the aspectRatio using the UIImageView's image dimension. For other types of views, this method as no impact. + +###### Usage examples: +```swift + aView.pin.left().width(100%).aspectRatio(2) + imageView.pin.left().width(200).aspectRatio() +``` + +###### Example: +This example layout an UIImageView at the top and center it horizontally, it also adjust its width to 50%. The view’s height will be adjusted automatically using the image aspect ratio. + + + + +```swift + imageView.pin.top().hCenter().width(50%).aspectRatio() +``` + +
+ + -## UIKit safeAreaInsets support +## safeArea, readable, layout and keyboard margins + +UIKit expose 4 kind of areas/guides that can be used to layout views. +PinLayout expose them using these properties: + +1. **`UIView.pin.safeArea`**: Expose UIKit `UIView.safeAreaInsets` / `UIView.safeAreaLayoutGuide`. +2. **`UIView.pin.readableMargins`**: Expose UIKit `UIView.readableContentGuide`. +3. **`UIView.pin.layoutMargins`**: Expose UIKit `UIView.layoutMargins` / `UIView.layoutMarginsGuide`. +4. **`UIView.pin.keyboardArea`**: Expose UIKit `UIView.keyboardLayoutGuide`. [iOS 15+] + +The following image display the 3 areas on an iPad in landscape mode. (safeArea, readableMargins, layoutMargins) + + + +See the **SafeArea & readableMargins** example in the [Examples App](#examples_app). + +### 1. pin.safeArea PinLayout can handle easily iOS 11 `UIView.safeAreaInsets`, but it goes further by supporting safeAreaInsets for previous iOS releases (including iOS 7/8/9/10) by adding a property `UIView.pin.safeArea`. PinLayout also extends the support of `UIView.safeAreaInsetsDidChange()` callback on iOS 7/8/9/10. @@ -1063,8 +1254,6 @@ The safe area of a view represent the area not covered by navigation bars, tab b button.pin.top(view.pin.safeArea) ``` - - ##### UIView.safeAreaInsetsDidChange(): * iOS 11 has also introduced the method [`UIView.safeAreaInsetsDidChange()`](https://developer.apple.com/documentation/uikit/uiview/2891104-safeareainsetsdidchange) which is called when the safe area of the view changes. This method is called only when your app runs on a iOS 11 device. **PinLayout's extend that and support this method also on older iOS releases including iOS 9/10**. @@ -1111,8 +1300,6 @@ The safe area of a view represent the area not covered by navigation bars, tab b } ``` * **disable**: In this mode PinLayout won't call `UIView.safeAreaInsetsDidChange` on iOS 8/9/10. Note that this is the default mode on iOS 8. - -
###### Example using `UIView.pin.safeArea` This example layout 4 subviews inside the safeArea. The UINavigationBar and UITabBar are translucent, so even if the container UIView goes under both, we can use its `UIView.pin.safeArea` to keeps its subviews within the safeArea. @@ -1128,13 +1315,114 @@ This example layout 4 subviews inside the safeArea. The UINavigationBar and UITa This example runs perfectly on a iPhone X (iOS 11), but it also runs on any devices with iOS 7, 8, 9 and 10. -:pushpin: This example is available in the [Examples App](#examples_app). See example complete [source code](https://github.com/mirego/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaView.swift) +:pushpin: This example is available in the [Examples App](#examples_app). See example complete [source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaView.swift)
+ +### 2. pin.readableMargins - +##### Property: +* **`pin.readableMargins: UIEdgeInset`**: +PinLayout's `UIView.pin.readableMargins` property expose UIKit [`UIView.readableContentGuide`](https://developer.apple.com/documentation/uikit/uiview/1622644-readablecontentguide) as an UIEdgeInsets. This is really useful since UIKit only expose the readableContent area to Auto Layout using UILayoutGuide. + +###### Usage examples: +```swift + label.pin.horizontally(pin.readableMargins) // the label fill horizontally the readable area. + view.pin.all(container.pin.readableMargins) // the view fill its parent's readable area. + view.pin.left(pin.readableMargins) +``` + +:pushpin: The [Examples App](#examples_app) contains some examples using `pin.readableMargins`. + +
+ +### 3. pin.layoutmargins + +##### Property: +* **`pin.layoutmargins: UIEdgeInset`** +PinLayout's `UIView.pin.layoutMargins` property expose directly the value of UIKit [`UIView.layoutMargins`](https://developer.apple.com/documentation/uikit/uiview/1622566-layoutmargins). The property exists only to be consistent with the other areas: `pin.safeArea`, `pin.readableMargins` and `pin.layoutmargins`. So its usage is not necessary. + +###### Usage example: +```swift + view.pin.left(container.pin.layoutmargins) + view.pin.left(container.layoutmargins) // Similar to the previous line +``` + +
+ +### 4. pin.keyboardArea: + +##### Property: +* **`pin.keyboardArea: CGRect` [iOS 15+]** +The property expose the `UIKit` value [`UIView.keyboardLayoutGuide`](https://developer.apple.com/documentation/uikit/keyboards_and_input/adjusting_your_layout_with_keyboard_layout_guide). It represents the area (`CGRect`) of the keyboard that is covering the view. Useful to adjust the layout when the keyboard is visible. [iOS 15+] + +##### Usage example: +```swift + container.pin.bottom(view.pin.keyboardArea.top) +``` + + + +## WrapContent + +The following methods are useful to adjust view's width and/or height to wrap all its subviews. These methods also adjust subviews position to create a tight wrap. + +**Methods:** + +* **`wrapContent()`** +**`wrapContent(padding: CGFloat)`** +**`wrapContent(padding: UIEdgeInsets)`** +Adjust the view's width and height to wrap all its subviews. The method also adjusts subviews's position to create a tight wrap. It is also possible to specify an optional padding around all subviews. +* **`wrapContent(:WrapType)`** +**`wrapContent(:WrapType, padding: CGFloat)`** **`wrapContent(:WrapType, padding: UIEdgeInsets)`** +Adjust the view's width AND/OR height to wrap all its subviews. Accept a WrapType parameter to define the wrapping type. It is also possible to specify an optional padding around all subviews. + +**Types:** + +* **`WrapType`** values: + * `.horizontally`: Adjust the view's width and update subviews's horizontal position. + * `.vertically`: Adjust only the view's height and update subviews's vertical position. + * `.all`: Adjust the view's width AND height and update subviews position. This is the default WrapType parameter value `wrapContent()` methods. + +###### Usage examples: +```swift + view.pin.wrapContent().center() // wrap all subviews and centered the view inside its parent. + view.pin.wrapContent(padding: 20) // wrap all subviews with a padding of 20 pixels all around + view.pin.wrapContent(.horizontally) +``` + +###### Example: +This example show the result of different `wrapContent()` method calls. +Here is the initial state: + + + +| Source code | Result | Description | +|---------------------|----------|-------------------| +| `view.pin.wrapContent()` | | Adjust the view's height and width to tight fit its subviews. | +| `view.pin.wrapContent(padding: 10)` | | Adjust the view's height and width and add a padding of 10 pixels around its subviews. | +| `view.pin.wrapContent(.horizontally)` | | Adjust only the view's width. | +| `view.pin.wrapContent(.vertically)` | | Adjust only the view's height. | + + +###### Example: +This example shows how a view (`containerView`) that has subviews (`imageView` and `label`) can be adjusted to the size of its subviews and then centered inside its parent. + + + +```swift + label.pin.below(of: imageView, aligned: .center).marginTop(4) + containerView.pin.wrapContent(padding: 10).center() +``` +* Line 1: Position the label below the imageView aligned on its center with a top margin of 4 pixels. +* Line 2: Adjust the `containerView`'s size and position its subviews to create a tight wrap around them with a padding of 10 pixels all around. The `containerView` is also centered inside its parent (superview). + +
+ + + ## justify() / align() **Methods:** @@ -1178,7 +1466,7 @@ And finally using `justify(.right)`: ```swift - viewA.pin.left().right().maxWidth(200).justify(.right) + viewA.pin.horizontally().maxWidth(200).justify(.right) ``` ###### Example: @@ -1193,6 +1481,66 @@ This example centered horizontally the view B in the space remaining at the righ
+ +## Automatic Sizing (UIView only) +Sizing views as part of the manual layout process is made with `sizeThatFits(_ size: CGSize)` where a view returns its ideal size given his parent size. Implementing sizing code has always been cumbersome because you always end up writing the same code twice, a first time for the layout and the second time for sizing. Sizing usually use the same rules layout does but implemented slightly differently because no subview `frame` should be mutated during sizing. Since `PinLayout` already takes care of the layout, it makes perfect sense to leverage it's layout engine to compute sizes. + +###### Traditional example: +```swift + override func layoutSubviews() { + super.layoutSubviews() + scrollView.pin.all() + imageView.pin.top().horizontally().sizeToFit(.width).margin(margin) + textLabel.pin.below(of: imageView).horizontally().sizeToFit(.width).margin(margin) + scrollView.contentSize = CGSize(width: scrollView.bounds.width, height: textLabel.frame.maxY + margin) + } + + override func sizeThatFits(_ size: CGSize) -> CGSize { + let availableSize = CGSize(width: size.width, height: CGFloat.greatestFiniteMagnitude) + return CGSize(width: size.width, height: + imageView.sizeThatFits(availableSize).height + + margin + + textLabel.sizeThatFits(availableSize).height + + margin + ) + } +``` + +###### Usage examples: +```swift + override func layoutSubviews() { + super.layoutSubviews() + performLayout() + didPerformLayout() + } + + private func performLayout() { + scrollView.pin.all() + imageView.pin.top().horizontally().sizeToFit(.width).margin(margin) + textLabel.pin.below(of: imageView).horizontally().sizeToFit(.width).margin(margin) + } + + private func didPerformLayout() { + scrollView.contentSize = CGSize(width: scrollView.bounds.width, height: textLabel.frame.maxY + margin) + } + + override func sizeThatFits(_ size: CGSize) -> CGSize { + autoSizeThatFits(size, layoutClosure: performLayout) + } +``` + +By calling `autoSizeThatFits` with the given available size and a layout closure, any layouting performed by PinLayout in that closure will be computed without affecting any subview's `frame` in the view hierarchy. On the other hand, any non PinLayout related code will also be executed. For that reason, it is really important to separate your layout code in it's own function to avoid any side effect during sizing, like setting the scroll view's content size in the above exemple or perhaps assigning `itemSize` in a collection view layout. That kind of code that depends on the layout should only be executed when `layoutSubviews()` is called as part of a normal layout pass. + +The resulting size also takes into account the margins applied on subviews, even on the bottom and trailing sides. Automatic sizing makes it really easy to write your layout logic once and add proper sizing behavior with virtually no additional effort. + +An Automatic Sizing example is available in the [Examples App](#examples_app). + +Notes: +1. Automatic Sizing is currently only available on iOS. +2. Automatic Sizing is still in beta, so any comments are welcomed. + +
+ ## UIView's transforms @@ -1273,44 +1621,54 @@ bView.pinFrame.below(of: aView, aligned: .left) ## Warnings ### PinLayout's warnings -In debug, PinLayout will display warnings when pin rules cannot be applied. +PinLayout can display warnings in the console when pin rules cannot be applied or are invalid. -**Warning reasons** +**Here a list of fews warning:** * The newly pinned attributes conflict with other already pinned attributes. Example: `view.pin.left(10).right(10).width(200)` -👉 Layout Conflict: `width(200) won't be applied since it conflicts with the following already set properties: left: 0, right: 10.`
 +👉 Layout Conflict: `width(200) won't be applied since it conflicts with the following already set properties: left: 0, right: 10.` * The newly pinned attributes have already been set to another value. Example: `view.pin.width(100).width(200)` -👉 Layout Conflict: `width(200) won't be applied since it value has already been set to 100.`
 +👉 Layout Conflict: `width(200) won't be applied since it value has already been set to 100.` * The view being layout hasn’t been added yet into a superview Example: `view.pin.width(100)` -👉 Layout Warning: `width(100) won't be applied, the view must be added as a sub-view before being layouted using this method.`
 +👉 Layout Warning: `width(100) won't be applied, the view must be added as a sub-view before being layouted using this method.` * A view is used as a reference, either directly or using its anchors or its edges, but hasn’t been added yet to a superview. Example: `view.pin.left(of: view2)` -👉 Layout Warning: `left(of: view2) won't be applied, the view must be added as a sub-view before being used as a reference.`
 +👉 Layout Warning: `left(of: view2) won't be applied, the view must be added as a sub-view before being used as a reference.` * The width and the height must be positive values. Example: `view.pin.width(-100)` 👉 Layout Warning: `The width (-100) must be greater or equal to 0.` - * `justify(.left|.center|.right)` is used without having set the left and the right coordinates. Example: `view.pin.left().width(250).justify(.center)` 👉 PinLayout Warning: justify(center) won't be applied, the left and right coordinates must be set to justify the view. - * Layout must be executed from the **Main thread**. 👉 PinLayout Warning: Layout must be executed from the Main Thread! +* Layout must be executed from the **Main thread**. +* ... + +### Enabling/Disabling warnings +##### Property: + +* **`Pin.logWarnings: Boolean`** +This property specifies if PinLayout's warnings are displayed in the console. In Debug (#if DEBUG) the default value is true, else its false. The value can be modified at runtime. -### Disabling warnings +#### Enabling/Disabling warnings individually +Few individual warnings can also be enabled/disabled individually: -Warnings can be disabled also in debug mode by setting the boolean Pin.logWarnings to false. +* **`Pin.activeWarnings.noSpaceAvailableBetweenViews: Boolean`** +If true, a warning is displayed if there is no space available between views specified in a call to `horizontallyBetween(...)` or `verticallyBetween(...)` +* **`Pin.activeWarnings. aspectRatioImageNotSet: Boolean`** +If true, a warning is displayed if 'aspectRatio()' is called on a UIImageView without a valid UIImage.
@@ -1347,6 +1705,20 @@ Warnings can be disabled also in debug mode by setting the boolean Pin.logWarnin
+ +## Animations using PinLayout + +PinLayout can easily animates Views. Multiple strategies can be used to animate layout using PinLayout. + +#### [See the section Animations using PinLayout for more details](docs/animations.md) + +The following animation example is available in the [Examples App](#examples_app). + + + +
+ + ## More examples @@ -1361,8 +1733,8 @@ Cell A: * A2 fills the remaining space ```swift - a1.pin.top().bottom().left().width(50) - a2.pin.after(of: a1, aligned: .top).bottom().right().marginLeft(10) + a1.pin.vertically().left().width(50) + a2.pin.after(of: a1, aligned: .top).bottomRight().marginLeft(10) ``` Cell B: @@ -1371,8 +1743,8 @@ Cell B: * B1 fills the remaining space ```swift - b2.pin.top().bottom().right().width(50) - b1.pin.before(of: b2, aligned: .top).bottom().left().marginRight(10) + b2.pin.vertically().right().width(50) + b1.pin.before(of: b2, aligned: .top).bottom().left().marginRight(10) ``` Cell C: @@ -1382,9 +1754,9 @@ Cell C: * C3 fills the remaining right space ```swift - c2.pin.topCenter().width(50).bottom() - c1.pin.before(of: c2, aligned: .top).bottom().left().marginRight(10) - c3.pin.after(of: c2, aligned: .top).bottom().right().marginLeft(10) + c2.pin.vertically().hCenter().width(50) + c1.pin.before(of: c2, aligned: .top).bottom().left().marginRight(10) + c3.pin.after(of: c2, aligned: .top).bottom().right().marginLeft(10) ``` Cell D: @@ -1394,9 +1766,9 @@ Cell D: * D3 fills the remaining space ```swift - d1.pin.topLeft().bottom().width(25%) - d2.pin.after(of: d1, aligned: .top).bottom().width(50%).marginLeft(10) - d3.pin.after(of: d2, aligned: .top).bottom().right().marginLeft(10) + d1.pin.vertically().left().width(25%) + d2.pin.after(of: d1, aligned: .top).bottom().width(50%).marginLeft(10) + d3.pin.after(of: d2, aligned: .top).bottom().right().marginLeft(10) ```
@@ -1414,78 +1786,100 @@ To integrate PinLayout into your Xcode project using CocoaPods, specify it in yo Then, run `pod install`. +### Swift Package Manager (SPM) + +1. From Xcode, select from the menu **File > Swift Packages > Add Package Dependency** +2. Specify the URL `https://github.com/layoutBox/PinLayout` + ### Carthage To integrate PinLayout into your Xcode project using Carthage, specify it in your `Cartfile`: ``` -github "mirego/PinLayout" +github "layoutBox/PinLayout" ``` Then, run `carthage update` to build the framework and drag the built `PinLayout.framework` into your Xcode project. -### Swift Package Manager - -Once you have your Swift package set up, you only need to add PinLayout as a dependency of your `Package.swift`. - -```swift -dependencies: [ - .Package(url: "https://github.com/mirego/PinLayout.git", majorVersion: 1) -] -``` -
## Examples App -There is an Example app that expose some usage example on PinLayout, including: - -* The [introduction example](#intro_usage_example) presented previously in this README. -* UITableView example with variable height cells. -* UICollectionView example. -* An example using PinLayout's [`UIView.pin.safeArea`](#safeAreaInsets) -* An RTL enabled version of the [introduction example](#intro_usage_example) -* An example showing of the right-to-left (RTL) language support. Similar to the Intro example. -* Example showing a form -* Example showing relative positioning. -* Example using Objective-C -* ... -:pushpin: Tap on images to see the example's source code. +The PinLayout's Example App exposes some usage example of PinLayout. + +#### [See the Example App section to get more information](docs/examples.md) + +Included examples: + +* The [introduction example](#intro_usage_example) presented previously in this README. +* Example using a UITableView with variable height cells. +* Example using a UICollectionView with variable height cells. +* Example showing how to animate with PinLayout. +* Example using [`pin.safeArea`, `pin.readableMargins` and `pin.layoutMargins`](#safeAreaInsets) +* Example using [`wrapContent()`](#wrapContent) +* Example using [`autoSizeThatFits`](#automatic_sizing) +* Example showing right-to-left (RTL) language support. +* Example showing a simple form example +* Example showing Relative Edges layout. +* Example using Objective-C +* ...

- PinLayout example - PinLayout example - PinLayout example - PinLayout example - PinLayout example - PinLayout example - PinLayout example - PinLayout example - PinLayout example + + + + + + + + + +

-This app is available in the `Example` folder. Note that you must do a `pod install` before running the example project. -
## macOS Support -PinLayout **support of macOS is not complete**, see here the particularities of the current implementation: +PinLayout can layout **NSView**'s on macOS. All PinLayout's properties and methods are available, with the following exceptions: + +* PinLayout supports **only views that have a parent (superview) using a flipped coordinate system**, i.e. views for which the computed property `var isFlipped: Bool` returns true. In a flipped coordinate system, the origin is in the upper-left corner of the view and y-values extend downward. UIKit use this coordinate system. In a non-flipped coordinate system (default mode), the origin is in the lower-left corner of the view and positive y-values extend upward. See [Apple's documentation for more information about `NSView.isFlipped`](https://developer.apple.com/documentation/appkit/nsview/1483532-isflipped). The support of non-flipped coordinate system will be added soon. -* PinLayout supports ** only views that have a parent (superview) using a flipped coordinate system**, i.e. views for which the computed property `var isFlipped: Bool` returns true. In a flipped coordinate system, the origin is in the upper-left corner of the view and y-values extend downward. UIKit use this coordinate system. In a non-flipped coordinate system (default mode), the origin is in the lower-left corner of the view and positive y-values extend upward. See [Apple's documentation for more information about `NSView.isFlipped`](https://developer.apple.com/documentation/appkit/nsview/1483532-isflipped). The support of non-flipped coordinate system will be added soon. +* [`sizeToFit(:FitType)`](#sizeToFit) is supported only for instances that inherits from NSControl. Support for [`sizeToFit(:FitType)`](#sizeToFit) can be added to your custom NSView subclasses, just make those views conform to the `SizeCalculable` protocol and implement the required `sizeThatFits(:CGSize)` function. -* These methods are currently not supported on macOS, but they will be implemented soon: +* [`NSView.pin.safeArea` and ](#safeAreaInsets) property is not available, AppKit doesn't have an `UIView.safeAreaInsets` equivalent. - * [`sizeToFit(:FitType)`](#sizeToFit) (Coming soon) - * [`aspectRatio()`](#aspect_ratio) with no parameters (Coming soon) +* [`NSView.pin.readableMargins`](#safeAreaInsets) property is not available, AppKit doesn't have an `UIView.readableContentGuide` equivalent. +* [`aspectRatio()`](#aspect_ratio) with no parameters. -* [`UIView.pin.safeArea`](#safeAreaInsets) property is not available, AppKit doesn't have an UIView.safeAreaInsets equivalent. +
+ + + +## CALayer Support + +PinLayout can layouts **CALayer**'s. All PinLayout's properties and methods are available, with the following exceptions: -All other PinLayout's methods and properties are available on macOS! +* [`sizeToFit(:FitType)`](#sizeToFit) is not supported. Support for [`sizeToFit(:FitType)`](#sizeToFit) can be added to your custom CALayer subclasses, just make those layers conform to the `SizeCalculable` protocol and implement the required `sizeThatFits(:CGSize)` function. +* [`CALayer.pin.safeArea` and `CALayer.pin.readableMargins`](#safeAreaInsets) properties are not available. +* [`aspectRatio()`](#aspect_ratio) with no parameters + +###### Usage Examples: + +```swift +aLayer = CALayer() +bLayer = CALayer() +view.layer.addSublayer(aLayer) +view.layer.addSublayer(bLayer) +... + +aLayer.pin.top(10).left(10).width(20%).height(80%) +bLayer.pin.below(of: aLayer, aligned: .left).size(of: aLayer) +```
@@ -1494,7 +1888,7 @@ All other PinLayout's methods and properties are available on macOS! PinLayout layouts views immediately after the line containing `.pin` has been fully executed, thanks to ARC (Automatic Reference Counting) this works perfectly on iOS/tvOS/macOS simulators and devices. But in Xcode Playgrounds, ARC doesn't work as expected, object references are kept much longer. This is a well documented issue and have a little impact on the PinLayout behaviour. -[See here for more details about using PinLayout in Xcode playgrounds](docs/xcode_playground.md) +#### [See here for more details about using PinLayout in Xcode playgrounds](docs/xcode_playground.md)
@@ -1502,7 +1896,7 @@ PinLayout layouts views immediately after the line containing `.pin` has been fu ## PinLayout using Objective-C PinLayout also expose an Objective-C interface slightly different than the Swift interface. -[See here for more details](docs/objective_c.md) +#### [See here for more details](docs/objective_c.md)
@@ -1538,17 +1932,14 @@ All example in the [Examples App](#examples_app) handle correctly the `safeAreaI ## Questions, comments, ideas, suggestions, issues, .... -If you have questions, you can checks already [answered questions here.](https://github.com/mirego/PinLayout/issues?q=is%3Aissue+is%3Aclosed+label%3Aquestion) +If you have questions, you can checks already [answered questions here.](https://github.com/layoutBox/PinLayout/issues?q=is%3Aissue+is%3Aclosed+label%3Aquestion) -For any **comments**, **ideas**, **suggestions**, **issues**, simply open an [issue](https://github.com/mirego/PinLayout/issues). +For any **comments**, **ideas**, **suggestions**, **issues**, simply open an [issue](https://github.com/layoutBox/PinLayout/issues). If you find PinLayout interesting, thanks to **Star** it. You'll be able to retrieve it easily later. If you'd like to contribute, you're welcome! -
- - ## Thanks PinLayout was inspired by other great layout frameworks, including: @@ -1557,12 +1948,12 @@ PinLayout was inspired by other great layout frameworks, including: * Qt: Anchors and edges management. * [SnapKit](https://github.com/SnapKit/SnapKit): Clean interface for anchors. -
- ## History -PinLayout recent history is available in the [CHANGELOG](CHANGELOG.md) also in [GitHub Releases](https://github.com/mirego/PinLayout/releases). +PinLayout recent history is available in the [CHANGELOG](CHANGELOG.md) also in [GitHub Releases](https://github.com/layoutBox/PinLayout/releases). -
+### Recent breaking change + +* `fitSize()` has been removed after being deprecated for 10 months. `sizeToFit(...)` should now be used instead. See [Adjusting size](#adjusting_size). (2018-08-21) ## License MIT License diff --git a/Sources/AutoSizeCalculable.swift b/Sources/AutoSizeCalculable.swift new file mode 100644 index 00000000..69782ff0 --- /dev/null +++ b/Sources/AutoSizeCalculable.swift @@ -0,0 +1,10 @@ +#if os(iOS) || os(tvOS) +import UIKit +#else +import AppKit +#endif + +public protocol AutoSizeCalculable { + func setAutoSizingRect(_ rect: CGRect, margins: PEdgeInsets) + func autoSizeThatFits(_ size: CGSize, layoutClosure: () -> Void) -> CGSize +} diff --git a/Sources/Extensions/CALayer+PinLayout.swift b/Sources/Extensions/CALayer+PinLayout.swift new file mode 100644 index 00000000..7eb92bea --- /dev/null +++ b/Sources/Extensions/CALayer+PinLayout.swift @@ -0,0 +1,88 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import QuartzCore + +extension CALayer: Layoutable { + public typealias PinView = CALayer + + public var superview: CALayer? { + return superlayer + } + + public var subviews: [CALayer] { + return sublayers ?? [] + } + + public var pin: PinLayout { + return PinLayout(view: self, keepTransform: true) + } + + public var pinFrame: PinLayout { + return PinLayout(view: self, keepTransform: false) + } + + public func getRect(keepTransform: Bool) -> CGRect { + if keepTransform { + /* + To adjust the layer's position and size, we don't set the layer's frame directly, because we want to keep the + layer's transform. + By setting the layer's center and bounds we really set the frame of the non-transformed layer, and this keep + the layer's transform. So layer's transforms won't be affected/altered by PinLayout. + */ + let size = bounds.size + // See setRect(...) for details about this calculation. + let origin = CGPoint(x: position.x - (size.width * anchorPoint.x), + y: position.y - (size.height * anchorPoint.y)) + + return CGRect(origin: origin, size: size) + } else { + return frame + } + } + + public func setRect(_ rect: CGRect, keepTransform: Bool) { + let adjustedRect = Coordinates.adjustRectToDisplayScale(rect) + + if keepTransform { + /* + To adjust the layer's position and size, we don't set the layer's frame directly, because we want to keep the + layer's transform. + By setting the layer's center and bounds we really set the frame of the non-transformed layer, and this keep + the layer's transform. So layer's transforms won't be affected/altered by PinLayout. + */ + + // NOTE: The center is offset by the layer.anchorPoint, so we have to take it into account. + position = CGPoint(x: adjustedRect.origin.x + (adjustedRect.width * anchorPoint.x), + y: adjustedRect.origin.y + (adjustedRect.height * anchorPoint.y)) + // NOTE: We must set only the bounds's size and keep the origin. + bounds.size = adjustedRect.size + } else { + frame = adjustedRect + } + } + + public func isLTR() -> Bool { + switch Pin.layoutDirection { + case .auto: return true + case .ltr: return true + case .rtl: return false + } + } +} diff --git a/Sources/Extensions/NSView+PinLayout.swift b/Sources/Extensions/NSView+PinLayout.swift new file mode 100644 index 00000000..ee81f9a0 --- /dev/null +++ b/Sources/Extensions/NSView+PinLayout.swift @@ -0,0 +1,75 @@ +// Copyright (c) 2018 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +#if os(macOS) +import AppKit + +extension NSView: Layoutable { + public typealias PinView = NSView + + public var pin: PinLayout { + return PinLayout(view: self, keepTransform: true) + } + + public var pinFrame: PinLayout { + return PinLayout(view: self, keepTransform: false) + } + + @objc public var pinObjc: PinLayoutObjC { + return PinLayoutObjCImpl(view: self, keepTransform: true) + } + + public func getRect(keepTransform: Bool) -> CGRect { + if let superview = superview, !superview.isFlipped { + var flippedRect = frame + flippedRect.origin.y = superview.frame.height - flippedRect.height - flippedRect.origin.y + return flippedRect + } else { + return frame + } + } + + public func setRect(_ rect: CGRect, keepTransform: Bool) { + let adjustedRect = Coordinates.adjustRectToDisplayScale(rect) + + if let superview = superview, !superview.isFlipped { + var flippedRect = adjustedRect + flippedRect.origin.y = superview.frame.height - flippedRect.height - flippedRect.origin.y + frame = flippedRect + } else { + frame = adjustedRect + } + } + + public func isLTR() -> Bool { + switch Pin.layoutDirection { + case .auto: return self.userInterfaceLayoutDirection == .leftToRight + case .ltr: return true + case .rtl: return false + } + } +} + +extension NSControl: SizeCalculable { + +} + +#endif diff --git a/Sources/Extensions/PEdgeInsets+Operators.swift b/Sources/Extensions/PEdgeInsets+Operators.swift new file mode 100644 index 00000000..6d83b091 --- /dev/null +++ b/Sources/Extensions/PEdgeInsets+Operators.swift @@ -0,0 +1,40 @@ +#if os(iOS) || os(tvOS) +import UIKit +#else +import AppKit +#endif + +public extension PEdgeInsets { + func minInsets(_ insets: PEdgeInsets) -> PEdgeInsets { + return PEdgeInsets(top: minValue(self.top, minValue: insets.top), + left: minValue(self.left, minValue: insets.left), + bottom: minValue(self.bottom, minValue: insets.bottom), + right: minValue(self.right, minValue: insets.right)) + } + + func minInsets(dx: CGFloat, dy: CGFloat) -> PEdgeInsets { + return PEdgeInsets(top: minValue(self.top, minValue: dy), + left: minValue(self.left, minValue: dx), + bottom: minValue(self.bottom, minValue: dy), + right: minValue(self.right, minValue: dx)) + } + + private func minValue(_ value: CGFloat, minValue: CGFloat) -> CGFloat { + return value >= minValue ? value : minValue + } +} + +public func + (lhs: PEdgeInsets, rhs: Int) -> PEdgeInsets { + let rhsf = CGFloat(rhs) + return PEdgeInsets(top: lhs.top + rhsf, + left: lhs.left + rhsf, + bottom: lhs.bottom + rhsf, + right: lhs.right + rhsf) +} + +public func + (lhs: PEdgeInsets, rhs: CGFloat) -> PEdgeInsets { + return PEdgeInsets(top: lhs.top + rhs, + left: lhs.left + rhs, + bottom: lhs.bottom + rhs, + right: lhs.right + rhs) +} diff --git a/Sources/Extensions/UIView+PinLayout.swift b/Sources/Extensions/UIView+PinLayout.swift new file mode 100644 index 00000000..1a9c1445 --- /dev/null +++ b/Sources/Extensions/UIView+PinLayout.swift @@ -0,0 +1,148 @@ +// Copyright (c) 2018 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +#if os(iOS) || os(tvOS) +import UIKit + +extension UIView: Layoutable, SizeCalculable { + public typealias PinView = UIView + + public var pin: PinLayout { + return PinLayout(view: self, keepTransform: true) + } + + public var pinFrame: PinLayout { + return PinLayout(view: self, keepTransform: false) + } + + @objc public var pinObjc: PinLayoutObjC { + return PinLayoutObjCImpl(view: self, keepTransform: true) + } + + public func getRect(keepTransform: Bool) -> CGRect { + guard !Pin.autoSizingInProgress || autoSizingRect == nil else { return autoSizingRect ?? CGRect.zero } + + if keepTransform { + /* + To adjust the view's position and size, we don't set the UIView's frame directly, because we want to keep the + view's transform (UIView.transform). + By setting the view's center and bounds we really set the frame of the non-transformed view, and this keep + the view's transform. So view's transforms won't be affected/altered by PinLayout. + */ + let size = bounds.size + // See setRect(...) for details about this calculation. + let origin = CGPoint(x: center.x - (size.width * layer.anchorPoint.x), + y: center.y - (size.height * layer.anchorPoint.y)) + + return CGRect(origin: origin, size: size) + } else { + return frame + } + } + + public func setRect(_ rect: CGRect, keepTransform: Bool) { + let adjustedRect = Coordinates.adjustRectToDisplayScale(rect) + + if keepTransform { + /* + To adjust the view's position and size, we don't set the UIView's frame directly, because we want to keep the + view's transform (UIView.transform). + By setting the view's center and bounds we really set the frame of the non-transformed view, and this keep + the view's transform. So view's transforms won't be affected/altered by PinLayout. + */ + + // NOTE: The center is offset by the layer.anchorPoint, so we have to take it into account. + center = CGPoint(x: adjustedRect.origin.x + (adjustedRect.width * layer.anchorPoint.x), + y: adjustedRect.origin.y + (adjustedRect.height * layer.anchorPoint.y)) + // NOTE: We must set only the bounds's size and keep the origin. + bounds.size = adjustedRect.size + } else { + frame = adjustedRect + } + } + + public func isLTR() -> Bool { + switch Pin.layoutDirection { + case .auto: + if #available(iOS 10.0, tvOS 10.0, *) { + return effectiveUserInterfaceLayoutDirection == .leftToRight + } else { + return UIView.userInterfaceLayoutDirection(for: semanticContentAttribute) == .leftToRight + } + case .ltr: return true + case .rtl: return false + } + } +} + +extension UIView: AutoSizeCalculable { + private struct pinlayoutAssociatedKeys { + static var pinlayoutAutoSizingRect = UnsafeMutablePointer.allocate(capacity: 1) + static var pinlayoutAutoSizingRectWithMargins = UnsafeMutablePointer.allocate(capacity: 1) + } + + private var autoSizingRect: CGRect? { + get { + return objc_getAssociatedObject(self, &pinlayoutAssociatedKeys.pinlayoutAutoSizingRect) as? CGRect + } + set { + objc_setAssociatedObject(self, &pinlayoutAssociatedKeys.pinlayoutAutoSizingRect, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + private var autoSizingRectWithMargins: CGRect? { + get { + return objc_getAssociatedObject(self, &pinlayoutAssociatedKeys.pinlayoutAutoSizingRectWithMargins) as? CGRect + } + set { + objc_setAssociatedObject(self, &pinlayoutAssociatedKeys.pinlayoutAutoSizingRectWithMargins, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + public func setAutoSizingRect(_ rect: CGRect, margins: PEdgeInsets) { + self.autoSizingRect = Coordinates.adjustRectToDisplayScale(rect) + self.autoSizingRectWithMargins = Coordinates.adjustRectToDisplayScale(rect.inset(by: margins)) + } + + public func autoSizeThatFits(_ size: CGSize, layoutClosure: () -> Void) -> CGSize { + let isAlreadyAutoSizing = Pin.autoSizingInProgress + + if (!isAlreadyAutoSizing) { + Pin.autoSizingInProgress = true + } + + autoSizingRect = CGRect(origin: CGPoint.zero, size: size) + + layoutClosure() + + let boundingRect = subviews.compactMap({ $0.autoSizingRectWithMargins }).reduce(CGRect.zero) { (result: CGRect, autoSizingRect: CGRect) -> CGRect in + return result.union(autoSizingRect) + } + + if !isAlreadyAutoSizing { + Pin.autoSizingInProgress = false + } + + return boundingRect.size + } +} + +#endif diff --git a/Sources/PinLayout+Filters.swift b/Sources/Filters.swift similarity index 100% rename from Sources/PinLayout+Filters.swift rename to Sources/Filters.swift diff --git a/Sources/Impl/Coordinates.swift b/Sources/Impl/Coordinates.swift index 60884e45..c1f0c563 100644 --- a/Sources/Impl/Coordinates.swift +++ b/Sources/Impl/Coordinates.swift @@ -17,78 +17,76 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -import Foundation - #if os(iOS) || os(tvOS) - import UIKit +import UIKit #else - import AppKit +import AppKit #endif -public func _pinlayoutSetUnitTest(displayScale: CGFloat) { - Coordinates.displayScale = displayScale -} +internal var displayScale: CGFloat = getDisplayScale() +internal var onePixelLength: CGFloat = 1 / displayScale -final class Coordinates { - #if os(iOS) || os(tvOS) - internal static var displayScale: CGFloat = UIScreen.main.scale - #elseif os(OSX) - internal static var displayScale: CGFloat = NSScreen.main?.backingScaleFactor ?? 2.0 - #endif - internal static var onePixelLength: CGFloat = 1 / displayScale +public func _pinlayoutSetUnitTest(scale: CGFloat?) { + if let scale = scale { + displayScale = scale + } else { + displayScale = getDisplayScale() + } +} - static func hCenter(_ view: PView, keepTransform: Bool) -> CGFloat { - let rect = getViewRect(view, keepTransform: keepTransform) +final class Coordinates { + static func hCenter(_ view: PinView, keepTransform: Bool) -> CGFloat { + let rect = view.getRect(keepTransform: keepTransform) return rect.minX + (rect.width / 2) } - static func vCenter(_ view: PView, keepTransform: Bool) -> CGFloat { - let rect = getViewRect(view, keepTransform: keepTransform) + static func vCenter(_ view: PinView, keepTransform: Bool) -> CGFloat { + let rect = view.getRect(keepTransform: keepTransform) return rect.minY + (rect.height / 2) } - static func topLeft(_ view: PView, keepTransform: Bool) -> CGPoint { - let rect = getViewRect(view, keepTransform: keepTransform) + static func topLeft(_ view: PinView, keepTransform: Bool) -> CGPoint { + let rect = view.getRect(keepTransform: keepTransform) return CGPoint(x: rect.minX, y: rect.minY) } - static func topCenter(_ view: PView, keepTransform: Bool) -> CGPoint { - let rect = getViewRect(view, keepTransform: keepTransform) + static func topCenter(_ view: PinView, keepTransform: Bool) -> CGPoint { + let rect = view.getRect(keepTransform: keepTransform) return CGPoint(x: rect.minX + (rect.width / 2), y: rect.minY) } - static func topRight(_ view: PView, keepTransform: Bool) -> CGPoint { - let rect = getViewRect(view, keepTransform: keepTransform) + static func topRight(_ view: PinView, keepTransform: Bool) -> CGPoint { + let rect = view.getRect(keepTransform: keepTransform) return CGPoint(x: rect.minX + rect.width, y: rect.minY) } - static func centerLeft(_ view: PView, keepTransform: Bool) -> CGPoint { - let rect = getViewRect(view, keepTransform: keepTransform) + static func centerLeft(_ view: PinView, keepTransform: Bool) -> CGPoint { + let rect = view.getRect(keepTransform: keepTransform) return CGPoint(x: rect.minX, y: rect.minY + (rect.height / 2)) } - static func center(_ view: PView, keepTransform: Bool) -> CGPoint { - let rect = getViewRect(view, keepTransform: keepTransform) + static func center(_ view: PinView, keepTransform: Bool) -> CGPoint { + let rect = view.getRect(keepTransform: keepTransform) return CGPoint(x: rect.minX + (rect.width / 2), y: rect.minY + (rect.height / 2)) } - static func centerRight(_ view: PView, keepTransform: Bool) -> CGPoint { - let rect = getViewRect(view, keepTransform: keepTransform) + static func centerRight(_ view: PinView, keepTransform: Bool) -> CGPoint { + let rect = view.getRect(keepTransform: keepTransform) return CGPoint(x: rect.minX + rect.width, y: rect.minY + (rect.height / 2)) } - static func bottomLeft(_ view: PView, keepTransform: Bool) -> CGPoint { - let rect = getViewRect(view, keepTransform: keepTransform) + static func bottomLeft(_ view: PinView, keepTransform: Bool) -> CGPoint { + let rect = view.getRect(keepTransform: keepTransform) return CGPoint(x: rect.minX, y: rect.minY + rect.height) } - static func bottomCenter(_ view: PView, keepTransform: Bool) -> CGPoint { - let rect = getViewRect(view, keepTransform: keepTransform) + static func bottomCenter(_ view: PinView, keepTransform: Bool) -> CGPoint { + let rect = view.getRect(keepTransform: keepTransform) return CGPoint(x: rect.minX + (rect.width / 2), y: rect.minY + rect.height) } - static func bottomRight(_ view: PView, keepTransform: Bool) -> CGPoint { - let rect = getViewRect(view, keepTransform: keepTransform) + static func bottomRight(_ view: PinView, keepTransform: Bool) -> CGPoint { + let rect = view.getRect(keepTransform: keepTransform) return CGPoint(x: rect.minX + rect.width, y: rect.minY + rect.height) } @@ -99,66 +97,6 @@ final class Coordinates { height: ceilFloatToDisplayScale(rect.size.height)) } - static func setViewRect(_ view: PView, toRect rect: CGRect, keepTransform: Bool) { - let adjustedRect = Coordinates.adjustRectToDisplayScale(rect) - - #if os(iOS) || os(tvOS) - if keepTransform { - /* - To adjust the view's position and size, we don't set the UIView's frame directly, because we want to keep the - view's transform (UIView.transform). - By setting the view's center and bounds we really set the frame of the non-transformed view, and this keep - the view's transform. So view's transforms won't be affected/altered by PinLayout. - */ - - // NOTE: The center is offset by the layer.anchorPoint, so we have to take it into account. - view.center = CGPoint(x: adjustedRect.origin.x + (adjustedRect.width * view.layer.anchorPoint.x), - y: adjustedRect.origin.y + (adjustedRect.height * view.layer.anchorPoint.y)) - // NOTE: We must set only the bounds's size and keep the origin. - view.bounds.size = adjustedRect.size - } else { - view.frame = adjustedRect - } - #else - if let superview = view.superview, !superview.isFlipped { - var flippedRect = adjustedRect - flippedRect.origin.y = superview.frame.height - flippedRect.height - flippedRect.origin.y - view.frame = flippedRect - } else { - view.frame = adjustedRect - } - #endif -} - - static func getViewRect(_ view: PView, keepTransform: Bool) -> CGRect { - #if os(iOS) || os(tvOS) - if keepTransform { - /* - To adjust the view's position and size, we don't set the UIView's frame directly, because we want to keep the - view's transform (UIView.transform). - By setting the view's center and bounds we really set the frame of the non-transformed view, and this keep - the view's transform. So view's transforms won't be affected/altered by PinLayout. - */ - let size = view.bounds.size - // See setUntransformedViewRect(...) for details about this calculation. - let origin = CGPoint(x: view.center.x - (size.width * view.layer.anchorPoint.x), - y: view.center.y - (size.height * view.layer.anchorPoint.y)) - - return CGRect(origin: origin, size: size) - } else { - return view.frame - } - #else - if let superview = view.superview, !superview.isFlipped { - var flippedRect = view.frame - flippedRect.origin.y = superview.frame.height - flippedRect.height - flippedRect.origin.y - return flippedRect - } else { - return view.frame - } - #endif - } - static func roundFloatToDisplayScale(_ pointValue: CGFloat) -> CGFloat { return CGFloat(roundf(Float(pointValue * displayScale))) / displayScale } @@ -167,3 +105,19 @@ final class Coordinates { return CGFloat(ceilf(Float(pointValue * displayScale))) / displayScale } } + +private func getDisplayScale() -> CGFloat { + #if os(iOS) || os(tvOS) + if #available(iOS 13.0, tvOS 13.0, *) { + return max(UITraitCollection.current.displayScale, 1) + } else { + return UIScreen.main.scale + } + #elseif os(OSX) + #if swift(>=4.1) + return NSScreen.main?.backingScaleFactor ?? 2.0 + #else + return NSScreen.main()?.backingScaleFactor ?? 2.0 + #endif + #endif +} diff --git a/Sources/Impl/PinLayoutImpl+Coordinates.swift b/Sources/Impl/PinLayout+Coordinates.swift similarity index 88% rename from Sources/Impl/PinLayoutImpl+Coordinates.swift rename to Sources/Impl/PinLayout+Coordinates.swift index c52449b3..603f27f4 100644 --- a/Sources/Impl/PinLayoutImpl+Coordinates.swift +++ b/Sources/Impl/PinLayout+Coordinates.swift @@ -23,7 +23,7 @@ import AppKit #endif -extension PinLayoutImpl { +extension PinLayout { internal func top(_ context: Context) { setTop(0, context) } @@ -42,9 +42,7 @@ extension PinLayoutImpl { } internal func setTop(_ value: CGFloat, _ context: Context) { - if let _bottom = _bottom, let height = height { - warnConflict(context, ["bottom": _bottom, "height": height]) - } else if let _vCenter = _vCenter { + if let _vCenter = _vCenter { warnConflict(context, ["Vertical Center": _vCenter]) } else if let _top = _top, _top != value { warnPropertyAlreadySet("top", propertyValue: _top, context) @@ -73,9 +71,7 @@ extension PinLayoutImpl { } internal func setLeft(_ value: CGFloat, _ context: Context) { - if let _right = _right, let width = width { - warnConflict(context, ["right": _right, "width": width]) - } else if let _hCenter = _hCenter { + if let _hCenter = _hCenter { warnConflict(context, ["Horizontal Center": _hCenter]) } else if let _left = _left, _left != value { warnPropertyAlreadySet("left", propertyValue: _left, context) @@ -114,13 +110,12 @@ extension PinLayoutImpl { } internal func setRight(_ value: CGFloat, _ context: Context) { - if let _left = _left, let width = width { - warnConflict(context, ["left": _left, "width": width]) - } else if let _hCenter = _hCenter { + if let _hCenter = _hCenter { warnConflict(context, ["Horizontal Center": _hCenter]) } else if let _right = _right, _right != value { if let superview = view.superview { - warnPropertyAlreadySet("right", propertyValue: superview.frame.width - _right, context) + let rect = superview.getRect(keepTransform: keepTransform) + warnPropertyAlreadySet("right", propertyValue: rect.width - _right, context) } else { warnPropertyAlreadySet("right", propertyValue: _right, context) } @@ -159,13 +154,12 @@ extension PinLayoutImpl { } internal func setBottom(_ value: CGFloat, _ context: Context) { - if let _top = _top, let height = height { - warnConflict(context, ["top": _top, "height": height]) - } else if let _vCenter = _vCenter { + if let _vCenter = _vCenter { warnConflict(context, ["Vertical Center": _vCenter]) } else if let _bottom = _bottom, _bottom != value { if let superview = view.superview { - warnPropertyAlreadySet("bottom", propertyValue: superview.frame.height - _bottom, context) + let rect = superview.getRect(keepTransform: keepTransform) + warnPropertyAlreadySet("bottom", propertyValue: rect.height - _bottom, context) } else { warnPropertyAlreadySet("bottom", propertyValue: _bottom, context) } @@ -334,16 +328,16 @@ extension PinLayoutImpl { } internal func validateWidth(_ width: CGFloat, context: Context) -> Bool { - if width < 0 { + guard width >= 0, width.isFinite else { warnWontBeApplied("the width (\(width)) must be greater than or equal to zero.", context) return false - } else { - return true } + + return true } internal func validateComputedWidth(_ width: CGFloat?) -> Bool { - if let width = width, width < 0 { + if let width = width, !width.isFinite || width < 0 { return false } else { return true @@ -351,40 +345,34 @@ extension PinLayoutImpl { } internal func validateHeight(_ height: CGFloat, context: Context) -> Bool { - if height < 0 { + guard height >= 0, height.isFinite else { warnWontBeApplied("the height (\(height)) must be greater than or equal to zero.", context) return false - } else { - return true } + + return true } internal func validateComputedHeight(_ height: CGFloat?) -> Bool { - if let height = height, height < 0 { + if let height = height, !height.isFinite || height < 0 { return false } else { return true } } - internal func setSize(_ size: CGSize, _ context: Context) -> PinLayout { - setWidth(size.width, { return "\(context())'s width" }) - setHeight(size.height, { return "\(context())'s height" }) - return self - } - - fileprivate func computeCoordinates(_ point: CGPoint, _ layoutSuperview: PView, _ referenceSuperview: PView) -> CGPoint { + private func computeCoordinates(_ point: CGPoint, _ layoutSuperview: PinView, _ referenceSuperview: PinView) -> CGPoint { if layoutSuperview == referenceSuperview { return point // same superview => no coordinates conversion required. - } else if referenceSuperview == layoutSuperview.superview { - let layoutSuperviewRect = Coordinates.getViewRect(layoutSuperview, keepTransform: keepTransform) + } else if referenceSuperview == layoutSuperview.superview as? PinView { + let layoutSuperviewRect = layoutSuperview.getRect(keepTransform: keepTransform) return CGPoint(x: point.x - layoutSuperviewRect.origin.x, y: point.y - layoutSuperviewRect.origin.y) // TOOD: Handle all cases. computeCoordinates should compute coordinates using only untransformed // coordinates, but UIView.convert(...) below use transformed coordinates! // Currently we only support 1 and 2 levels. } else { - return referenceSuperview.convert(point, to: layoutSuperview) + return referenceSuperview.convert(point, to: layoutSuperview as? PinView.PinView) } } @@ -392,23 +380,19 @@ extension PinLayoutImpl { guard let layoutSuperview = layoutSuperview(context) else { return nil } var results: [CGPoint] = [] anchors.forEach({ (anchor) in - let anchor = anchor as! AnchorImpl + let anchor = anchor as! AnchorImpl if let referenceSuperview = referenceSuperview(anchor.view, context) { results.append(computeCoordinates(anchor.point(keepTransform: keepTransform), layoutSuperview, referenceSuperview)) } }) - guard results.count > 0 else { - warnWontBeApplied("no valid references", context) - return nil - } - + guard results.count > 0 else { return nil } return results } internal func computeCoordinate(forEdge edge: HorizontalEdge, _ context: Context) -> CGFloat? { - let edge = edge as! HorizontalEdgeImpl + let edge = edge as! HorizontalEdgeImpl guard let layoutSuperview = layoutSuperview(context) else { return nil } guard let referenceSuperview = referenceSuperview(edge.view, context) else { return nil } @@ -417,7 +401,7 @@ extension PinLayoutImpl { } internal func computeCoordinate(forEdge edge: VerticalEdge, _ context: Context) -> CGFloat? { - let edge = edge as! VerticalEdgeImpl + let edge = edge as! VerticalEdgeImpl guard let layoutSuperview = layoutSuperview(context) else { return nil } guard let referenceSuperview = referenceSuperview(edge.view, context) else { return nil } diff --git a/Sources/Impl/PinLayoutImpl+Layouting.swift b/Sources/Impl/PinLayout+Layouting.swift similarity index 63% rename from Sources/Impl/PinLayoutImpl+Layouting.swift rename to Sources/Impl/PinLayout+Layouting.swift index 1204a848..d684ce85 100644 --- a/Sources/Impl/PinLayoutImpl+Layouting.swift +++ b/Sources/Impl/PinLayout+Layouting.swift @@ -23,8 +23,18 @@ import AppKit #endif -// MARK - UIView's frame computation methods -extension PinLayoutImpl { +// MARK: UIView's frame computation methods +extension PinLayout { + /** + The method will execute PinLayout commands immediately. This method is **required only if your + source codes should also work in Xcode Playgrounds**. Outside of playgrounds, PinLayout executes + this method implicitly, it is not necessary to call it. + + Examples: + ```swift + view.pin.top(20).width(100).layout() + ``` + */ public func layout() { apply() } @@ -35,10 +45,10 @@ extension PinLayoutImpl { isLayouted = true } - private func apply(onView view: PView) { + private func apply(onView view: PinView) { displayLayoutWarnings() - var newRect = Coordinates.getViewRect(view, keepTransform: keepTransform) + var newRect = view.getRect(keepTransform: keepTransform) handlePinEdges() @@ -71,10 +81,10 @@ extension PinLayoutImpl { newRect.origin.x = left + _marginLeft } else if let right = _right { // Only right is set - newRect.origin.x = right - view.bounds.width - _marginRight + newRect.origin.x = right - newRect.width - _marginRight } else if let _hCenter = _hCenter { // Only hCenter is set - newRect.origin.x = (_hCenter - (view.bounds.width / 2)) + _marginLeft - _marginRight + newRect.origin.x = (_hCenter - (newRect.width / 2)) + _marginLeft - _marginRight } else if let width = newSize.width { // Only width is set newRect.size.width = width @@ -107,30 +117,29 @@ extension PinLayoutImpl { newRect.origin.y = top + _marginTop } else if let bottom = _bottom { // Only bottom is set - newRect.origin.y = bottom - view.bounds.height - _marginBottom + newRect.origin.y = bottom - newRect.height - _marginBottom } else if let _vCenter = _vCenter { // Only vCenter is set - newRect.origin.y = (_vCenter - (view.bounds.height / 2)) + _marginTop - _marginBottom + newRect.origin.y = (_vCenter - (newRect.height / 2)) + _marginTop - _marginBottom } else if let height = newSize.height { // Only height is set newRect.size.height = height } if !validateComputedWidth(newRect.size.width) { - newRect.size.width = view.bounds.width + newRect.size.width = view.getRect(keepTransform: keepTransform).width } if !validateComputedHeight(newRect.size.height) { - newRect.size.height = view.bounds.height + newRect.size.height = view.getRect(keepTransform: keepTransform).height + } + + if Pin.autoSizingInProgress, let autoSizeCalculable = view as? AutoSizeCalculable { + let marginInsets = PEdgeInsets(top: -_marginTop, left: -_marginLeft, bottom: -_marginBottom, right: -_marginRight) + autoSizeCalculable.setAutoSizingRect(newRect, margins: marginInsets) + } else { + view.setRect(newRect, keepTransform: keepTransform) } - - /* - To adjust the view's position and size, we don't set the UIView's frame directly, because we want to keep the - view's transform (UIView.transform). - By setting the view's center and bounds we really set the frame of the non-transformed view, and this keep - the view's transform. So view's transforms won't be affected/altered by PinLayout. - */ - Coordinates.setViewRect(view, toRect: newRect, keepTransform: keepTransform) } private func handlePinEdges() { @@ -183,157 +192,137 @@ extension PinLayoutImpl { } } } - - private func computeSize() -> Size { - var width = computeWidth() - var height = computeHeight() - #if os(macOS) - assert(!legacyFitSize && fitType == nil) - #endif + private func computeSize() -> Size { + var size = resolveSize() - if legacyFitSize { - return computeLegacyFitSize(width: width, height: height) - } else if let fitType = fitType { - var fitWidth = CGFloat.greatestFiniteMagnitude - var fitHeight = CGFloat.greatestFiniteMagnitude - - // Apply min/max width/height before calling sizeThatFits() ... and reapply them after. - switch fitType { - case .width, .widthFlexible: - if let width = applyMinMax(toWidth: width) { - fitWidth = width - } else { - fitWidth = view.bounds.width - } - case .height, .heightFlexible: - if let height = applyMinMax(toHeight: height) { - fitHeight = height - } else { - fitHeight = view.bounds.height - } + if let adjustSizeType = adjustSizeType { + switch adjustSizeType { + case .fitTypeWidth, .fitTypeHeight, .fitTypeWidthFlexible, .fitTypeHeightFlexible, .fitTypeContent: + size = computeSizeToFit(adjustSizeType: adjustSizeType, size: size) + case .aspectRatio(let ratio): + size = computeAspectRatio(ratio, size: size) } + } - #if os(iOS) || os(tvOS) - let sizeThatFits = view.sizeThatFits(CGSize(width: fitWidth, height: fitHeight)) - #else - let sizeThatFits: CGSize - if #available(OSX 10.10, *) { - if let control = view as? NSControl { - sizeThatFits = control.sizeThatFits(CGSize(width: fitWidth, height: fitHeight)) - } else { - sizeThatFits = view.intrinsicContentSize - } - } else { - sizeThatFits = view.intrinsicContentSize - } - #endif + return validateAndApplyMinMax(toSize: size) + } - if fitWidth != .greatestFiniteMagnitude { - width = fitType.isFlexible ? sizeThatFits.width : fitWidth - } else { - width = sizeThatFits.width - } - - if fitHeight != .greatestFiniteMagnitude { - height = fitType.isFlexible ? sizeThatFits.height : fitHeight - } else { - height = sizeThatFits.height - } - } else if let aspectRatio = _aspectRatio { - if width == nil && height == nil { - warn("aspectRatio won't be applied, neither the width nor the height can be determined.") - } else { - if width != nil && height != nil { - // warn, both are specified - warn("aspectRatio won't be applied, the width and the height are already defined by other PinLayout's properties.") - } else if let width = width, let adjWidth = applyMinMax(toWidth: width) { - height = adjWidth / aspectRatio - } else if let height = height, let adjHeight = applyMinMax(toHeight: height) { - width = adjHeight * aspectRatio - } - } - } + private func resolveSize() -> Size { + var size = Size() - width = applyMinMax(toWidth: width) - height = applyMinMax(toHeight: height) - - if !validateComputedWidth(width) { - width = nil + // Width + if let width = width { + size.width = width + } else if let left = _left, let right = _right { + size.width = right - left - _marginLeft - _marginRight + } else if shouldKeepViewDimension { + // No width has been specified (and won't be computed by a sizeToFit) => use the current view's width + size.width = view.getRect(keepTransform: keepTransform).width } - - if !validateComputedHeight(height) { - height = nil + + // Height + if let height = height { + size.height = height + } else if let top = _top, let bottom = _bottom { + size.height = bottom - top - _marginTop - _marginBottom + } else if shouldKeepViewDimension { + size.height = view.getRect(keepTransform: keepTransform).height } - - return (width, height) + + return size } - private func computeLegacyFitSize(width: CGFloat?, height: CGFloat?) -> Size { - var width = width - var height = height + private func computeSizeToFit(adjustSizeType: AdjustSizeType, size: Size) -> Size { + guard let sizeCalculableView = view as? SizeCalculable else { + assertionFailure("Should not occurs, protocol conformance is checked before assigning adjustSizeType") + return size + } - if width == nil && height == nil { - warn("fitSize() won't be applied, neither the width nor the height can be determined.") - } else { - var fitWidth = CGFloat.greatestFiniteMagnitude - var fitHeight = CGFloat.greatestFiniteMagnitude + var fitWidth = CGFloat.greatestFiniteMagnitude + var fitHeight = CGFloat.greatestFiniteMagnitude + var size = size - if let width = applyMinMax(toWidth: width) { + // Apply min/max width/height before calling sizeThatFits() ... and reapply them after. + switch adjustSizeType { + case .fitTypeWidth, .fitTypeWidthFlexible: + if let width = applyMinMax(toWidth: size.width) { fitWidth = width + } else { + fitWidth = view.getRect(keepTransform: keepTransform).width } - if let height = applyMinMax(toHeight: height) { + case .fitTypeHeight, .fitTypeHeightFlexible: + if let height = applyMinMax(toHeight: size.height) { fitHeight = height + } else { + fitHeight = view.getRect(keepTransform: keepTransform).height } + case .fitTypeContent: + let fitSize = view.getRect(keepTransform: keepTransform).size + fitWidth = fitSize.width + fitHeight = fitSize.height + default: + assertionFailure("Should not occured") + } - #if os(iOS) || os(tvOS) - let sizeThatFits = view.sizeThatFits(CGSize(width: fitWidth, height: fitHeight)) - #else - let sizeThatFits = view.intrinsicContentSize - #endif + let sizeThatFits = sizeCalculableView.sizeThatFits(CGSize(width: fitWidth, height: fitHeight)) - if fitWidth != .greatestFiniteMagnitude && (sizeThatFits.width > fitWidth) { - width = fitWidth + switch adjustSizeType { + case .fitTypeWidth, .fitTypeWidthFlexible, .fitTypeHeight, .fitTypeHeightFlexible, .aspectRatio(_): + if fitWidth != .greatestFiniteMagnitude { + size.width = adjustSizeType.isFlexible ? sizeThatFits.width : fitWidth } else { - width = sizeThatFits.width + size.width = sizeThatFits.width } - if fitHeight != .greatestFiniteMagnitude && (sizeThatFits.height > fitHeight) { - height = fitHeight + if fitHeight != .greatestFiniteMagnitude { + size.height = adjustSizeType.isFlexible ? sizeThatFits.height : fitHeight } else { - height = sizeThatFits.height + size.height = sizeThatFits.height } + case .fitTypeContent: + size = Size(width: sizeThatFits.width, height: sizeThatFits.height) + } - width = applyMinMax(toWidth: width) - height = applyMinMax(toHeight: height) - - if !validateComputedWidth(width) { - width = nil - } + return size + } - if !validateComputedHeight(height) { - height = nil - } + private func computeAspectRatio(_ aspectRatio: CGFloat, size: Size) -> Size { + guard size.width != nil || size.height != nil else { + warn("aspectRatio won't be applied, neither the width nor the height can be determined.") + return size + } + guard size.width == nil || size.height == nil else { + warn("aspectRatio won't be applied, the width and the height are already defined by other PinLayout's properties.") + return size } - return (width, height) + if let width = size.width, let adjustedWidth = applyMinMax(toWidth: width) { + return Size(width: adjustedWidth, height: adjustedWidth / aspectRatio) + } else if let height = size.height, let adjustedHeight = applyMinMax(toHeight: height) { + return Size(width: adjustedHeight * aspectRatio, height: adjustedHeight) + } else { + assertionFailure("Should not occurs, all cases should be handled by guards above") + return size + } } - - private func computeWidth() -> CGFloat? { - var newWidth: CGFloat? - - if let width = width { - newWidth = width - } else if let left = _left, let right = _right { - newWidth = right - left - _marginLeft - _marginRight - } else if shouldKeepViewDimension { - // No width has been specified (and won't be computed by a sizeToFit) => use the current view's width - newWidth = view.bounds.width + + private func validateAndApplyMinMax(toSize size: Size) -> Size { + var size = size + size.width = applyMinMax(toWidth: size.width) + size.height = applyMinMax(toHeight: size.height) + + if !validateComputedWidth(size.width) { + size.width = nil } - - return newWidth + + if !validateComputedHeight(size.height) { + size.height = nil + } + + return size } - + private func applyMinMax(toWidth width: CGFloat?) -> CGFloat? { var result = width @@ -355,7 +344,7 @@ extension PinLayoutImpl { let remainingWidth = containerWidth - rect.width var justifyType = HorizontalAlign.left - if let justify = justify { + if let justify = justify, justify != .none { justifyType = justify } @@ -381,25 +370,13 @@ extension PinLayoutImpl { } else { rect.origin.x = left + _marginLeft } + case .none: + break } return rect } - private func computeHeight() -> CGFloat? { - var newHeight: CGFloat? - - if let height = height { - newHeight = height - } else if let top = _top, let bottom = _bottom { - newHeight = bottom - top - _marginTop - _marginBottom - } else if shouldKeepViewDimension { - newHeight = view.bounds.height - } - - return newHeight - } - private func applyMinMax(toHeight height: CGFloat?) -> CGFloat? { var result = height @@ -421,7 +398,7 @@ extension PinLayoutImpl { let remainingHeight = containerHeight - rect.height var alignType = VerticalAlign.top - if let align = align { + if let align = align, align != .none { alignType = align } @@ -434,6 +411,8 @@ extension PinLayoutImpl { rect.origin.y = top + _marginTop + remainingHeight / 2 case .bottom: rect.origin.y = bottom - _marginBottom - rect.height + case .none: + break } return rect @@ -443,4 +422,3 @@ extension PinLayoutImpl { return view.isLTR() } } - diff --git a/Sources/Impl/PinLayoutImpl+Warning.swift b/Sources/Impl/PinLayout+Warning.swift similarity index 74% rename from Sources/Impl/PinLayoutImpl+Warning.swift rename to Sources/Impl/PinLayout+Warning.swift index e7958bd1..352c610f 100644 --- a/Sources/Impl/PinLayoutImpl+Warning.swift +++ b/Sources/Impl/PinLayout+Warning.swift @@ -23,61 +23,30 @@ import AppKit #endif -fileprivate var numberFormatter: NumberFormatter = { +private var numberFormatter: NumberFormatter = { let numberFormatter = NumberFormatter() numberFormatter.numberStyle = .decimal numberFormatter.decimalSeparator = "." return numberFormatter }() -internal func pinLayoutDisplayConsoleWarning(_ text: String, _ view: PView) { - var displayText = "\n👉 \(text)" - - let x = numberFormatter.string(from: NSNumber(value: Float(view.frame.origin.x)))! - let y = numberFormatter.string(from: NSNumber(value: Float(view.frame.origin.y)))! - let width = numberFormatter.string(from: NSNumber(value: Float(view.frame.size.width)))! - let height = numberFormatter.string(from: NSNumber(value: Float(view.frame.size.height)))! - let viewName = "\(type(of: view))" - displayText += "\n(Layouted view info: Type: \(viewName), Frame: x: \(x), y: \(y), width: \(width), height: \(height))" - - var currentView = view - var hierarchy: [String] = [] - while let parent = currentView.superview { - hierarchy.insert("\(type(of: parent))", at: 0) - currentView = parent - } - if hierarchy.count > 0 { - #if swift(>=4.1) - displayText += ", Superviews: \(hierarchy.compactMap({ $0 }).joined(separator: " -> "))" - #else - displayText += ", Superviews: \(hierarchy.flatMap({ $0 }).joined(separator: " -> "))" - #endif - } - - displayText += ", Tag: \(view.tag))\n" - - print(displayText) - Pin.lastWarningText = text -} - - -extension PinLayoutImpl { +extension PinLayout { internal func pointContext(method: String, point: CGPoint) -> String { return "\(method)(to: CGPoint(x: \(point.x), y: \(point.y)))" } internal func relativeEdgeContext(method: String, edge: VerticalEdge) -> String { - let edge = edge as! VerticalEdgeImpl + let edge = edge as! VerticalEdgeImpl return "\(method)(to: .\(edge.type.rawValue), of: \(viewDescription(edge.view)))" } internal func relativeEdgeContext(method: String, edge: HorizontalEdge) -> String { - let edge = edge as! HorizontalEdgeImpl + let edge = edge as! HorizontalEdgeImpl return "\(method)(to: .\(edge.type.rawValue), of: \(viewDescription(edge.view))" } internal func relativeAnchorContext(method: String, anchor: Anchor) -> String { - let anchor = anchor as! AnchorImpl + let anchor = anchor as! AnchorImpl return "\(method)(to: .\(anchor.type.rawValue), of: \(viewDescription(anchor.view)))" } @@ -145,16 +114,47 @@ extension PinLayoutImpl { } } - internal func viewDescription(_ view: PView) -> String { - return "(\(viewName(view)), Frame: \(view.frame))" + internal func viewDescription(_ view: PinView) -> String { + let rect = view.getRect(keepTransform: keepTransform) + return "(\(viewName(view)), Frame: \(rect))" } - internal func viewName(_ view: PView) -> String { + internal func viewName(_ view: PinView) -> String { return "\(type(of: view))" } internal func insetsDescription(_ insets: PEdgeInsets) -> String { return "UIEdgeInsets(top: \(insets.top), left: \(insets.left), bottom: \(insets.bottom), right: \(insets.right))" } -} + internal func pinLayoutDisplayConsoleWarning(_ text: String, _ view: PinView) { + var displayText = "\n👉 \(text)" + + let rect = view.getRect(keepTransform: keepTransform) + let x = numberFormatter.string(from: NSNumber(value: Float(rect.origin.x)))! + let y = numberFormatter.string(from: NSNumber(value: Float(rect.origin.y)))! + let width = numberFormatter.string(from: NSNumber(value: Float(rect.size.width)))! + let height = numberFormatter.string(from: NSNumber(value: Float(rect.size.height)))! + let viewName = "\(type(of: view))" + displayText += "\n(Layouted view info: Type: \(viewName), Frame: x: \(x), y: \(y), width: \(width), height: \(height))" + + var currentView = view + var hierarchy: [String] = [] + while let parent = currentView.superview { + hierarchy.insert("\(type(of: parent))", at: 0) + currentView = parent as! PinView + } + if hierarchy.count > 0 { + #if swift(>=4.1) + displayText += ", Superviews: \(hierarchy.compactMap({ $0 }).joined(separator: " -> "))" + #else + displayText += ", Superviews: \(hierarchy.flatMap({ $0 }).joined(separator: " -> "))" + #endif + } + + displayText += ", Debug description: \(view.debugDescription))\n" + + print(displayText) + Pin.lastWarningText = text + } +} diff --git a/Sources/Impl/PinLayoutImpl+Relative.swift b/Sources/Impl/PinLayoutImpl+Relative.swift deleted file mode 100644 index 4005b304..00000000 --- a/Sources/Impl/PinLayoutImpl+Relative.swift +++ /dev/null @@ -1,394 +0,0 @@ -// Copyright (c) 2017 Luc Dion -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#if os(iOS) || os(tvOS) - import UIKit -#else - import AppKit -#endif - -extension PinLayoutImpl { - - // - // above(of ...) - // - @discardableResult - func above(of relativeView: PView) -> PinLayout { - func context() -> String { return "above(of: \(relativeView))" } - return above(relativeViews: [relativeView], aligned: nil, context: context) - } - - @discardableResult - func above(of relativeViews: [PView]) -> PinLayout { - func context() -> String { return "above(of: \(relativeViews))" } - return above(relativeViews: relativeViews, aligned: nil, context: context) - } - - @discardableResult - func above(of relativeView: PView, aligned: HorizontalAlign) -> PinLayout { - func context() -> String { return "above(of: \(relativeView), aligned: \(aligned))" } - return above(relativeViews: [relativeView], aligned: aligned, context: context) - } - - @discardableResult - func above(of relativeViews: [PView], aligned: HorizontalAlign) -> PinLayout { - func context() -> String { return "above(of: \(relativeViews), aligned: \(aligned))" } - return above(relativeViews: relativeViews, aligned: aligned, context: context) - } - - // - // below(of ...) - // - @discardableResult - func below(of relativeView: PView) -> PinLayout { - func context() -> String { return "below(of: \(relativeView))" } - return below(relativeViews: [relativeView], aligned: nil, context: context) - } - - @discardableResult - func below(of relativeViews: [PView]) -> PinLayout { - func context() -> String { return "below(of: \(relativeViews))" } - return below(relativeViews: relativeViews, aligned: nil, context: context) - } - - @discardableResult - func below(of relativeView: PView, aligned: HorizontalAlign) -> PinLayout { - func context() -> String { return "below(of: \(relativeView), aligned: \(aligned))" } - return below(relativeViews: [relativeView], aligned: aligned, context: context) - } - - @discardableResult - func below(of relativeViews: [PView], aligned: HorizontalAlign) -> PinLayout { - func context() -> String { return "below(of: \(relativeViews), aligned: \(aligned))" } - return below(relativeViews: relativeViews, aligned: aligned, context: context) - } - - // - // left(of ...) - // - @discardableResult - func left(of relativeView: PView) -> PinLayout { - func context() -> String { return "left(of: \(relativeView))" } - return left(relativeViews: [relativeView], aligned: nil, context: context) - } - - @discardableResult - func left(of relativeViews: [PView]) -> PinLayout { - func context() -> String { return "left(of: \(relativeViews))" } - return left(relativeViews: relativeViews, aligned: nil, context: context) - } - - @discardableResult - func left(of relativeView: PView, aligned: VerticalAlign) -> PinLayout { - func context() -> String { return "left(of: \(relativeView), aligned: \(aligned))" } - return left(relativeViews: [relativeView], aligned: aligned, context: context) - } - - @discardableResult - func left(of relativeViews: [PView], aligned: VerticalAlign) -> PinLayout { - func context() -> String { return "left(of: \(relativeViews), aligned: \(aligned))" } - return left(relativeViews: relativeViews, aligned: aligned, context: context) - } - - // - // right(of ...) - // - @discardableResult - func right(of relativeView: PView) -> PinLayout { - func context() -> String { return "right(of: \(relativeView))" } - return right(relativeViews: [relativeView], aligned: nil, context: context) - } - - @discardableResult - func right(of relativeViews: [PView]) -> PinLayout { - func context() -> String { return "right(of: \(relativeViews))" } - return right(relativeViews: relativeViews, aligned: nil, context: context) - } - - @discardableResult - func right(of relativeView: PView, aligned: VerticalAlign) -> PinLayout { - func context() -> String { return "right(of: \(relativeView), aligned: \(aligned))" } - return right(relativeViews: [relativeView], aligned: aligned, context: context) - } - - @discardableResult - func right(of relativeViews: [PView], aligned: VerticalAlign) -> PinLayout { - func context() -> String { return "right(of: \(relativeViews), aligned: \(aligned))" } - return right(relativeViews: relativeViews, aligned: aligned, context: context) - } - - @discardableResult - func before(of relativeView: PView) -> PinLayout { - func context() -> String { return "before(of: \(relativeView))" } - if isLTR() { - return left(relativeViews: [relativeView], aligned: nil, context: context) - } else { - return right(relativeViews: [relativeView], aligned: nil, context: context) - } - } - - @discardableResult - func before(of relativeViews: [PView]) -> PinLayout { - func context() -> String { return "before(of: \(relativeViews))" } - if isLTR() { - return left(relativeViews: relativeViews, aligned: nil, context: context) - } else { - return right(relativeViews: relativeViews, aligned: nil, context: context) - } - } - - @discardableResult - func before(of relativeView: PView, aligned: VerticalAlign) -> PinLayout { - func context() -> String { return "before(of: \(relativeView), aligned: \(aligned))" } - if isLTR() { - return left(relativeViews: [relativeView], aligned: aligned, context: context) - } else { - return right(relativeViews: [relativeView], aligned: aligned, context: context) - } - } - - @discardableResult - func before(of relativeViews: [PView], aligned: VerticalAlign) -> PinLayout { - func context() -> String { return "before(of: \(relativeViews), aligned: \(aligned))" } - if isLTR() { - return left(relativeViews: relativeViews, aligned: aligned, context: context) - } else { - return right(relativeViews: relativeViews, aligned: aligned, context: context) - } - } - - @discardableResult - func after(of relativeView: PView) -> PinLayout { - func context() -> String { return "after(of: \(relativeView))" } - if isLTR() { - return right(relativeViews: [relativeView], aligned: nil, context: context) - } else { - return left(relativeViews: [relativeView], aligned: nil, context: context) - } - } - - @discardableResult - func after(of relativeViews: [PView]) -> PinLayout { - func context() -> String { return "after(of: \(relativeViews))" } - if isLTR() { - return right(relativeViews: relativeViews, aligned: nil, context: context) - } else { - return left(relativeViews: relativeViews, aligned: nil, context: context) - } - } - - @discardableResult - func after(of relativeView: PView, aligned: VerticalAlign) -> PinLayout { - func context() -> String { return "after(of: \(relativeView))" } - if isLTR() { - return right(relativeViews: [relativeView], aligned: aligned, context: context) - } else { - return left(relativeViews: [relativeView], aligned: aligned, context: context) - } - } - - @discardableResult - func after(of relativeViews: [PView], aligned: VerticalAlign) -> PinLayout { - func context() -> String { return "after(of: \(relativeViews), aligned: \(aligned))" } - if isLTR() { - return right(relativeViews: relativeViews, aligned: aligned, context: context) - } else { - return left(relativeViews: relativeViews, aligned: aligned, context: context) - } - } -} - -// MARK: fileprivate -extension PinLayoutImpl { - @discardableResult - fileprivate func above(relativeViews: [PView], aligned: HorizontalAlign?, context: Context) -> PinLayout { - guard let relativeViews = validateRelativeViews(relativeViews, context: context) else { return self } - - let anchors: [Anchor] - if let aligned = aligned { - switch aligned { - case .left: anchors = relativeViews.map({ $0.anchor.topLeft }) - case .center: anchors = relativeViews.map({ $0.anchor.topCenter }) - case .right: anchors = relativeViews.map({ $0.anchor.topRight }) - case .start: anchors = isLTR() ? relativeViews.map({ $0.anchor.topLeft }) : relativeViews.map({ $0.anchor.topRight }) - case .end: anchors = isLTR() ? relativeViews.map({ $0.anchor.topRight }) : relativeViews.map({ $0.anchor.topLeft }) - } - } else { - anchors = relativeViews.map({ $0.anchor.topLeft }) - } - - if let coordinatesList = computeCoordinates(forAnchors: anchors, context) { - setBottom(getTopMostCoordinate(list: coordinatesList), context) - applyHorizontalAlignment(aligned, coordinatesList: coordinatesList, context: context) - } - return self - } - - @discardableResult - fileprivate func below(relativeViews: [PView], aligned: HorizontalAlign?, context: Context) -> PinLayout { - guard let relativeViews = validateRelativeViews(relativeViews, context: context) else { return self } - - let anchors: [Anchor] - if let aligned = aligned { - switch aligned { - case .left: anchors = relativeViews.map({ $0.anchor.bottomLeft }) - case .center: anchors = relativeViews.map({ $0.anchor.bottomCenter }) - case .right: anchors = relativeViews.map({ $0.anchor.bottomRight }) - case .start: anchors = isLTR() ? relativeViews.map({ $0.anchor.bottomLeft }) : relativeViews.map({ $0.anchor.bottomRight }) - case .end: anchors = isLTR() ? relativeViews.map({ $0.anchor.bottomRight }) : relativeViews.map({ $0.anchor.bottomLeft }) - } - } else { - anchors = relativeViews.map({ $0.anchor.bottomLeft }) - } - - if let coordinatesList = computeCoordinates(forAnchors: anchors, context) { - setTop(getBottomMostCoordinate(list: coordinatesList), context) - applyHorizontalAlignment(aligned, coordinatesList: coordinatesList, context: context) - } - return self - } - - fileprivate func left(relativeViews: [PView], aligned: VerticalAlign?, context: Context) -> PinLayout { - guard let relativeViews = validateRelativeViews(relativeViews, context: context) else { return self } - - let anchors: [Anchor] - if let aligned = aligned { - switch aligned { - case .top: anchors = relativeViews.map({ $0.anchor.topLeft }) - case .center: anchors = relativeViews.map({ $0.anchor.centerLeft }) - case .bottom: anchors = relativeViews.map({ $0.anchor.bottomLeft }) - } - } else { - anchors = relativeViews.map({ $0.anchor.topLeft }) - } - - if let coordinatesList = computeCoordinates(forAnchors: anchors, context) { - setRight(getLeftMostCoordinate(list: coordinatesList), context) - applyVerticalAlignment(aligned, coordinatesList: coordinatesList, context: context) - } - return self - } - - fileprivate func right(relativeViews: [PView], aligned: VerticalAlign?, context: Context) -> PinLayout { - guard let relativeViews = validateRelativeViews(relativeViews, context: context) else { return self } - - let anchors: [Anchor] - if let aligned = aligned { - switch aligned { - case .top: anchors = relativeViews.map({ $0.anchor.topRight }) - case .center: anchors = relativeViews.map({ $0.anchor.centerRight }) - case .bottom: anchors = relativeViews.map({ $0.anchor.bottomRight }) - } - } else { - anchors = relativeViews.map({ $0.anchor.topRight }) - } - - if let coordinatesList = computeCoordinates(forAnchors: anchors, context) { - setLeft(getRightMostCoordinate(list: coordinatesList), context) - applyVerticalAlignment(aligned, coordinatesList: coordinatesList, context: context) - } - return self - } - - fileprivate func applyHorizontalAlignment(_ aligned: HorizontalAlign?, coordinatesList: [CGPoint], context: Context) { - if let aligned = aligned { - switch aligned { - case .left: setLeft(getLeftMostCoordinate(list: coordinatesList), context) - case .center: setHorizontalCenter(getAverageHCenterCoordinate(list: coordinatesList), context) - case .right: setRight(getRightMostCoordinate(list: coordinatesList), context) - - case .start: - isLTR() ? setLeft(getLeftMostCoordinate(list: coordinatesList), context) : - setRight(getRightMostCoordinate(list: coordinatesList), context) - case .end: - isLTR() ? setRight(getRightMostCoordinate(list: coordinatesList), context) : - setLeft(getLeftMostCoordinate(list: coordinatesList), context) - } - } - } - - fileprivate func applyVerticalAlignment(_ aligned: VerticalAlign?, coordinatesList: [CGPoint], context: Context) { - if let aligned = aligned { - switch aligned { - case .top: setTop(getTopMostCoordinate(list: coordinatesList), context) - case .center: setVerticalCenter(getAverageVCenterCoordinate(list: coordinatesList), context) - case .bottom: setBottom(getBottomMostCoordinate(list: coordinatesList), context) - } - } - } - - fileprivate func getTopMostCoordinate(list: [CGPoint]) -> CGFloat { - assert(list.count > 0) - let firstCoordinate = list[0].y - return list.dropFirst().reduce(firstCoordinate, { (bestCoordinate, otherCoordinates) -> CGFloat in - return (otherCoordinates.y < bestCoordinate) ? otherCoordinates.y : bestCoordinate - }) - } - - fileprivate func getBottomMostCoordinate(list: [CGPoint]) -> CGFloat { - assert(list.count > 0) - let firstCoordinate = list[0].y - return list.dropFirst().reduce(firstCoordinate, { (bestCoordinate, otherCoordinates) -> CGFloat in - return (otherCoordinates.y > bestCoordinate) ? otherCoordinates.y : bestCoordinate - }) - } - - fileprivate func getLeftMostCoordinate(list: [CGPoint]) -> CGFloat { - assert(list.count > 0) - let firstCoordinate = list[0].x - return list.dropFirst().reduce(firstCoordinate, { (bestCoordinate, otherCoordinates) -> CGFloat in - return (otherCoordinates.x < bestCoordinate) ? otherCoordinates.x : bestCoordinate - }) - } - - fileprivate func getRightMostCoordinate(list: [CGPoint]) -> CGFloat { - assert(list.count > 0) - let firstCoordinate = list[0].x - return list.dropFirst().reduce(firstCoordinate, { (bestCoordinate, otherCoordinates) -> CGFloat in - return (otherCoordinates.x > bestCoordinate) ? otherCoordinates.x : bestCoordinate - }) - } - - fileprivate func getAverageHCenterCoordinate(list: [CGPoint]) -> CGFloat { - assert(list.count > 0) - let sum = list.reduce(0, { (result, point) -> CGFloat in - return result + point.x - }) - return sum / CGFloat(list.count) - } - - fileprivate func getAverageVCenterCoordinate(list: [CGPoint]) -> CGFloat { - assert(list.count > 0) - let sum = list.reduce(0, { (result, point) -> CGFloat in - return result + point.y - }) - return sum / CGFloat(list.count) - } - - fileprivate func validateRelativeViews(_ relativeViews: [PView], context: Context) -> [PView]? { - guard let _ = layoutSuperview(context) else { return nil } - guard relativeViews.count > 0 else { - warnWontBeApplied("At least one view must be visible (i.e. UIView.isHidden != true) ", context) - return nil - } - - return relativeViews - } -} - diff --git a/Sources/Impl/PinLayoutImpl.swift b/Sources/Impl/PinLayoutImpl.swift deleted file mode 100644 index 5b16dca1..00000000 --- a/Sources/Impl/PinLayoutImpl.swift +++ /dev/null @@ -1,1183 +0,0 @@ -// Copyright (c) 2017 Luc Dion -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -import Foundation - -#if os(iOS) || os(tvOS) - import UIKit - typealias PView = UIView - typealias PEdgeInsets = UIEdgeInsets -#else - import AppKit - typealias PView = NSView - typealias PEdgeInsets = NSEdgeInsets -#endif - -class PinLayoutImpl: PinLayout { - internal let view: PView - internal let keepTransform: Bool - - internal var _top: CGFloat? // offset from superview's top edge - internal var _left: CGFloat? // offset from superview's left edge - internal var _bottom: CGFloat? // offset from superview's top edge - internal var _right: CGFloat? // offset from superview's left edge - - internal var _hCenter: CGFloat? - internal var _vCenter: CGFloat? - - internal var width: CGFloat? - internal var minWidth: CGFloat? - internal var maxWidth: CGFloat? - internal var height: CGFloat? - internal var minHeight: CGFloat? - internal var maxHeight: CGFloat? - - internal var fitType: FitType? - internal var legacyFitSize = false - internal var _aspectRatio: CGFloat? - - internal var shouldKeepViewDimension: Bool { - return fitType == nil && !legacyFitSize && _aspectRatio == nil - } - - internal var marginTop: CGFloat? - internal var marginLeft: CGFloat? - internal var marginBottom: CGFloat? - internal var marginRight: CGFloat? - internal var shouldPinEdges = false - - internal var justify: HorizontalAlign? - internal var align: VerticalAlign? - - internal var _marginTop: CGFloat { return marginTop ?? 0 } - internal var _marginLeft: CGFloat { return marginLeft ?? 0 } - internal var _marginBottom: CGFloat { return marginBottom ?? 0 } - internal var _marginRight: CGFloat { return marginRight ?? 0 } - - internal var isLayouted = false - - init(view: PView, keepTransform: Bool) { - self.view = view - self.keepTransform = keepTransform - - #if os(iOS) || os(tvOS) - Pin.initPinLayout() - #endif - } - - deinit { - if !isLayouted && Pin.logMissingLayoutCalls { - warn("PinLayout commands have been issued without calling the 'layout()' method to complete the layout. (These warnings can be disabled by setting Pin.logMissingLayoutCalls to false)") - } - apply() - } - - #if os(iOS) || os(tvOS) - var safeArea: PEdgeInsets { - if #available(iOS 11.0, tvOS 11.0, *) { - return view.safeAreaInsets - } else { - return view.pinlayoutComputeSafeAreaInsets() - } - } - #endif - - // - // top, left, bottom, right - // - @discardableResult func top() -> PinLayout { - top({ return "top()" }) - return self - } - - @discardableResult - func top(_ value: CGFloat) -> PinLayout { - return top(value, { return "top(\(value))" }) - } - - @discardableResult - func top(_ percent: Percent) -> PinLayout { - func context() -> String { return "top(\(percent.description))" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - setTop(percent.of(layoutSuperviewRect.height), context) - return self - } - - @discardableResult - func top(_ insets: PEdgeInsets) -> PinLayout { - return top(insets.top, { return "top(\(insetsDescription(insets))" }) - } - - @discardableResult - func left() -> PinLayout { - return left({ return "left()" }) - } - - @discardableResult - func left(_ value: CGFloat) -> PinLayout { - return left(value, { return "left(\(value))" }) - } - - @discardableResult - func left(_ percent: Percent) -> PinLayout { - return left(percent, { return "left(\(percent.description))" }) - } - - @discardableResult - func left(_ insets: PEdgeInsets) -> PinLayout { - return left(insets.left, { return "left(\(insetsDescription(insets))" }) - } - - @discardableResult - func start() -> PinLayout { - func context() -> String { return "start()" } - return isLTR() ? left(context) : right(context) - } - - @discardableResult - func start(_ value: CGFloat) -> PinLayout { - func context() -> String { return "start(\(value))" } - return isLTR() ? left(value, context) : right(value, context) - } - - @discardableResult - func start(_ percent: Percent) -> PinLayout { - func context() -> String { return "start(\(percent.description))" } - return isLTR() ? left(percent, context) : right(percent, context) - } - - @discardableResult - func start(_ insets: PEdgeInsets) -> PinLayout { - func context() -> String { return "start(\(insetsDescription(insets))" } - return isLTR() ? left(insets.left, context) : right(insets.right, context) - } - - @discardableResult func bottom() -> PinLayout { - return bottom({ return "bottom()" }) - } - - @discardableResult - func bottom(_ value: CGFloat) -> PinLayout { - return bottom(value, { return "bottom(\(value))" }) - } - - @discardableResult - func bottom(_ percent: Percent) -> PinLayout { - return bottom(percent, { return "bottom(\(percent.description))" }) - } - - @discardableResult - func bottom(_ insets: PEdgeInsets) -> PinLayout { - return bottom(insets.bottom, { return "bottom(\(insetsDescription(insets))" }) - } - - @discardableResult func right() -> PinLayout { - return right({ return "right()" }) - } - - @discardableResult - func right(_ value: CGFloat) -> PinLayout { - return right(value, { return "right(\(value))" }) - } - - @discardableResult - func right(_ percent: Percent) -> PinLayout { - return right(percent, { return "right(\(percent.description))" }) - } - - @discardableResult - func right(_ insets: PEdgeInsets) -> PinLayout { - return right(insets.right, { return "right(\(insetsDescription(insets))" }) - } - - @discardableResult func end() -> PinLayout { - func context() -> String { return "end()" } - return isLTR() ? right(context) : left(context) - } - - @discardableResult - func end(_ value: CGFloat) -> PinLayout { - func context() -> String { return "end(\(value))" } - return isLTR() ? right(value, context) : left(value, context) - } - - @discardableResult - func end(_ percent: Percent) -> PinLayout { - func context() -> String { return "end(\(percent.description))" } - return isLTR() ? right(percent, context) : left(percent, context) - } - - @discardableResult - func end(_ insets: PEdgeInsets) -> PinLayout { - func context() -> String { return "end(\(insetsDescription(insets))" } - return isLTR() ? right(insets.right, context) : left(insets.left, context) - } - - @discardableResult - func hCenter() -> PinLayout { - func context() -> String { return "hCenter()" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - setHorizontalCenter(layoutSuperviewRect.width / 2, context) - return self - } - - @discardableResult - func hCenter(_ value: CGFloat) -> PinLayout { - func context() -> String { return "hCenter(\(value))" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - setHorizontalCenter((layoutSuperviewRect.width / 2) + value, context) - return self - } - - @discardableResult - func hCenter(_ percent: Percent) -> PinLayout { - func context() -> String { return "hCenter(\(percent.description))" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - setHorizontalCenter((layoutSuperviewRect.width / 2) + percent.of(layoutSuperviewRect.width), context) - return self - } - - @discardableResult - func vCenter() -> PinLayout { - func context() -> String { return "vCenter()" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - setVerticalCenter(layoutSuperviewRect.height / 2, context) - return self - } - - @discardableResult - func vCenter(_ value: CGFloat) -> PinLayout { - func context() -> String { return "vCenter(\(value))" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - setVerticalCenter((layoutSuperviewRect.height / 2) + value, context) - return self - } - - @discardableResult - func vCenter(_ percent: Percent) -> PinLayout { - func context() -> String { return "vCenter(\(percent.description))" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - setVerticalCenter((layoutSuperviewRect.height / 2) + percent.of(layoutSuperviewRect.height), context) - return self - } - - @discardableResult - func all() -> PinLayout { - top({ "all() top coordinate" }) - bottom({ "all() bottom coordinate" }) - right({ "all() right coordinate" }) - left({ "all() left coordinate" }) - return self - } - - @discardableResult - func all(_ value: CGFloat) -> PinLayout { - top(value, { "all(\(value)) top coordinate" }) - bottom(value, { "all(\(value)) bottom coordinate" }) - left(value, { "all(\(value)) left coordinate" }) - right(value, { "all(\(value)) right coordinate" }) - return self - } - - func all(_ insets: PEdgeInsets) -> PinLayout { - top(insets.top, { "all(\(insets)) top coordinate" }) - bottom(insets.bottom, { "all(\(insets)) bottom coordinate" }) - left(insets.left, { "all(\(insets)) left coordinate" }) - right(insets.right, { "all(\(insets)) right coordinate" }) - return self - } - - @discardableResult - func horizontally() -> PinLayout { - right({ "horizontally() right coordinate" }) - left({ "horizontally() left coordinate" }) - return self - } - - @discardableResult - func horizontally(_ value: CGFloat) -> PinLayout { - left(value, { return "horizontally(\(value)) left coordinate" }) - right(value, { return "horizontally(\(value)) right coordinate" }) - return self - } - - @discardableResult - func horizontally(_ percent: Percent) -> PinLayout { - left(percent, { return "horizontally(\(percent.description)) left coordinate" }) - right(percent, { return "horizontally(\(percent.description)) right coordinate" }) - return self - } - - @discardableResult - func horizontally(_ insets: PEdgeInsets) -> PinLayout { - left(insets.left, { return "horizontally(\(insets)) left coordinate" }) - right(insets.right, { return "horizontally(\(insets)) right coordinate" }) - return self - } - - @discardableResult - func vertically() -> PinLayout { - top({ "vertically() top coordinate" }) - bottom({ "vertically() bottom coordinate" }) - return self - } - - @discardableResult - func vertically(_ value: CGFloat) -> PinLayout { - top(value, { return "vertically(\(value)) top coordinate" }) - bottom(value, { return "vertically(\(value)) bottom coordinate" }) - return self - } - - @discardableResult - func vertically(_ percent: Percent) -> PinLayout { - top(percent, { return "vertically(\(percent.description)) top coordinate" }) - bottom(percent, { return "vertically(\(percent.description)) bottom coordinate" }) - return self - } - - @discardableResult - func vertically(_ insets: PEdgeInsets) -> PinLayout { - top(insets.top, { return "vertically(\(insets)) top coordinate" }) - bottom(insets.bottom, { return "vertically(\(insets)) bottom coordinate" }) - return self - } - - // - // top, left, bottom, right - // - @discardableResult - func top(to edge: VerticalEdge) -> PinLayout { - func context() -> String { return relativeEdgeContext(method: "top", edge: edge) } - if let coordinate = computeCoordinate(forEdge: edge, context) { - setTop(coordinate, context) - } - return self - } - - @discardableResult - func vCenter(to edge: VerticalEdge) -> PinLayout { - func context() -> String { return relativeEdgeContext(method: "vCenter", edge: edge) } - if let coordinate = computeCoordinate(forEdge: edge, context) { - setVerticalCenter(coordinate, context) - } - return self - } - - @discardableResult - func bottom(to edge: VerticalEdge) -> PinLayout { - func context() -> String { return relativeEdgeContext(method: "bottom", edge: edge) } - if let coordinate = computeCoordinate(forEdge: edge, context) { - setBottom(coordinate, context) - } - return self - } - - @discardableResult - func left(to edge: HorizontalEdge) -> PinLayout { - func context() -> String { return relativeEdgeContext(method: "left", edge: edge) } - if let coordinate = computeCoordinate(forEdge: edge, context) { - setLeft(coordinate, context) - } - return self - } - - @discardableResult - func hCenter(to edge: HorizontalEdge) -> PinLayout { - func context() -> String { return relativeEdgeContext(method: "hCenter", edge: edge) } - if let coordinate = computeCoordinate(forEdge: edge, context) { - setHorizontalCenter(coordinate, context) - } - return self - } - - @discardableResult - func right(to edge: HorizontalEdge) -> PinLayout { - func context() -> String { return relativeEdgeContext(method: "right", edge: edge) } - if let coordinate = computeCoordinate(forEdge: edge, context) { - setRight(coordinate, context) - } - return self - } - - @discardableResult - func start(to edge: HorizontalEdge) -> PinLayout { - func context() -> String { return relativeEdgeContext(method: "start", edge: edge) } - if let coordinate = computeCoordinate(forEdge: edge, context) { - setStart(coordinate, context) - } - return self - } - - @discardableResult - func end(to edge: HorizontalEdge) -> PinLayout { - func context() -> String { return relativeEdgeContext(method: "end", edge: edge) } - if let coordinate = computeCoordinate(forEdge: edge, context) { - setEnd(coordinate, context) - } - return self - } - - // - // topLeft, topCenter, topRight, - // centerLeft, center, centerRight, - // bottomLeft, bottomCenter, bottomRight, - // - @discardableResult - func topLeft(to anchor: Anchor) -> PinLayout { - func context() -> String { return relativeAnchorContext(method: "topLeft", anchor: anchor) } - if let coordinatesList = computeCoordinates(forAnchors: [anchor], context) { - setTopLeft(coordinatesList[0], context) - } - return self - } - - @discardableResult - func topLeft() -> PinLayout { - return topLeft({ return "topLeft()" }) - } - - fileprivate func topLeft(_ context: Context) -> PinLayout { - setTopLeft(CGPoint(x: 0, y: 0), { return "topLeft()" }) - return self - } - - @discardableResult - func topStart(to anchor: Anchor) -> PinLayout { - func context() -> String { return relativeAnchorContext(method: "topStart", anchor: anchor) } - if let coordinatesList = computeCoordinates(forAnchors: [anchor], context) { - setTop(coordinatesList[0].y, context) - setStart(coordinatesList[0].x, context) - } - return self - } - - @discardableResult - func topStart() -> PinLayout { - func context() -> String { return "topStart()" } - return isLTR() ? topLeft(context) : topRight(context) - } - - @discardableResult - func topCenter(to anchor: Anchor) -> PinLayout { - func context() -> String { return relativeAnchorContext(method: "topCenter", anchor: anchor) } - if let coordinatesList = computeCoordinates(forAnchors: [anchor], context) { - setTopCenter(coordinatesList[0], context) - } - return self - } - - @discardableResult - func topCenter() -> PinLayout { - func context() -> String { return "topCenter()" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - setTopCenter(CGPoint(x: layoutSuperviewRect.width / 2, y: 0), context) - return self - } - - @discardableResult - func topRight(to anchor: Anchor) -> PinLayout { - func context() -> String { return relativeAnchorContext(method: "topRight", anchor: anchor) } - if let coordinatesList = computeCoordinates(forAnchors: [anchor], context) { - setTopRight(coordinatesList[0], context) - } - return self - } - - @discardableResult - func topRight() -> PinLayout { - return topRight({ return "topRight()" }) - } - - fileprivate func topRight(_ context: Context) -> PinLayout { - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - setTopRight(CGPoint(x: layoutSuperviewRect.width, y: 0), context) - return self - } - - @discardableResult - func topEnd(to anchor: Anchor) -> PinLayout { - func context() -> String { return relativeAnchorContext(method: "topEnd", anchor: anchor) } - if let coordinatesList = computeCoordinates(forAnchors: [anchor], context) { - setTop(coordinatesList[0].y, context) - setEnd(coordinatesList[0].x, context) - } - return self - } - - @discardableResult - func topEnd() -> PinLayout { - func context() -> String { return "topEnd()" } - return isLTR() ? topRight(context) : topLeft(context) - } - - @discardableResult - func centerLeft(to anchor: Anchor) -> PinLayout { - func context() -> String { return relativeAnchorContext(method: "centerLeft", anchor: anchor) } - if let coordinatesList = computeCoordinates(forAnchors: [anchor], context) { - setCenterLeft(coordinatesList[0], context) - } - return self - } - - @discardableResult - func centerLeft() -> PinLayout { - return centerLeft({ return "centerLeft()" }) - } - - fileprivate func centerLeft(_ context: Context) -> PinLayout { - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - setCenterLeft(CGPoint(x: 0, y: layoutSuperviewRect.height / 2), context) - return self - } - - @discardableResult - func centerStart(to anchor: Anchor) -> PinLayout { - func context() -> String { return relativeAnchorContext(method: "centerStart", anchor: anchor) } - if let coordinatesList = computeCoordinates(forAnchors: [anchor], context) { - setVerticalCenter(coordinatesList[0].y, context) - setStart(coordinatesList[0].x, context) - } - return self - } - - @discardableResult - func centerStart() -> PinLayout { - func context() -> String { return "centerStart()" } - return isLTR() ? centerLeft(context) : centerRight(context) - } - - @discardableResult - func center(to anchor: Anchor) -> PinLayout { - func context() -> String { return relativeAnchorContext(method: "center", anchor: anchor) } - if let coordinatesList = computeCoordinates(forAnchors: [anchor], context) { - setCenter(coordinatesList[0], context) - } - return self - } - - @discardableResult - func center() -> PinLayout { - func context() -> String { return "center()" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - setCenter(CGPoint(x: layoutSuperviewRect.width / 2, y: layoutSuperviewRect.height / 2), context) - return self - } - - @discardableResult - func centerRight(to anchor: Anchor) -> PinLayout { - func context() -> String { return relativeAnchorContext(method: "centerRight", anchor: anchor) } - if let coordinatesList = computeCoordinates(forAnchors: [anchor], context) { - setCenterRight(coordinatesList[0], context) - } - return self - } - - @discardableResult - func centerRight() -> PinLayout { - return centerRight({ return "centerRight()" }) - } - - @discardableResult - fileprivate func centerRight(_ context: Context) -> PinLayout { - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - setCenterRight(CGPoint(x: layoutSuperviewRect.width, y: layoutSuperviewRect.height / 2), context) - return self - } - - @discardableResult - func centerEnd(to anchor: Anchor) -> PinLayout { - func context() -> String { return relativeAnchorContext(method: "centerEnd", anchor: anchor) } - if let coordinatesList = computeCoordinates(forAnchors: [anchor], context) { - setVerticalCenter(coordinatesList[0].y, context) - setEnd(coordinatesList[0].x, context) - } - return self - } - - @discardableResult - func centerEnd() -> PinLayout { - func context() -> String { return "centerEnd()" } - return isLTR() ? centerRight(context) : centerLeft(context) - } - - @discardableResult - func bottomLeft(to anchor: Anchor) -> PinLayout { - func context() -> String { return relativeAnchorContext(method: "bottomLeft", anchor: anchor) } - if let coordinatesList = computeCoordinates(forAnchors: [anchor], context) { - setBottomLeft(coordinatesList[0], context) - } - return self - } - - @discardableResult - func bottomLeft() -> PinLayout { - return bottomLeft({ return "bottomLeft()" }) - } - - @discardableResult - fileprivate func bottomLeft(_ context: Context) -> PinLayout { - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - setBottomLeft(CGPoint(x: 0, y: layoutSuperviewRect.height), context) - return self - } - - @discardableResult - func bottomStart(to anchor: Anchor) -> PinLayout { - func context() -> String { return relativeAnchorContext(method: "bottomStart", anchor: anchor) } - if let coordinatesList = computeCoordinates(forAnchors: [anchor], context) { - setBottom(coordinatesList[0].y, context) - setStart(coordinatesList[0].x, context) - } - return self - } - - @discardableResult - func bottomStart() -> PinLayout { - func context() -> String { return "bottomStart()" } - return isLTR() ? bottomLeft(context) : bottomRight(context) - } - - @discardableResult - func bottomCenter(to anchor: Anchor) -> PinLayout { - func context() -> String { return relativeAnchorContext(method: "bottomCenter", anchor: anchor) } - if let coordinatesList = computeCoordinates(forAnchors: [anchor], context) { - setBottomCenter(coordinatesList[0], context) - } - return self - } - - @discardableResult - func bottomCenter() -> PinLayout { - func context() -> String { return "bottomCenter()" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - setBottomCenter(CGPoint(x: layoutSuperviewRect.width / 2, y: layoutSuperviewRect.height), context) - return self - } - - @discardableResult - func bottomRight(to anchor: Anchor) -> PinLayout { - func context() -> String { return relativeAnchorContext(method: "bottomRight", anchor: anchor) } - if let coordinatesList = computeCoordinates(forAnchors: [anchor], context) { - setBottomRight(coordinatesList[0], context) - } - return self - } - - @discardableResult - func bottomRight() -> PinLayout { - return bottomRight({ return "bottomRight()" }) - } - - @discardableResult - fileprivate func bottomRight(_ context: Context) -> PinLayout { - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - setBottomRight(CGPoint(x: layoutSuperviewRect.width, y: layoutSuperviewRect.height), context) - return self - } - - @discardableResult - func bottomEnd(to anchor: Anchor) -> PinLayout { - func context() -> String { return relativeAnchorContext(method: "bottomEnd", anchor: anchor) } - if let coordinatesList = computeCoordinates(forAnchors: [anchor], context) { - setBottom(coordinatesList[0].y, context) - setEnd(coordinatesList[0].x, context) - } - return self - } - - @discardableResult - func bottomEnd() -> PinLayout { - func context() -> String { return "bottomEnd()" } - return isLTR() ? bottomRight(context) : bottomLeft(context) - } - - // - // width, height - // - @discardableResult - func width(_ width: CGFloat) -> PinLayout { - return setWidth(width, { return "width(\(width))" }) - } - - @discardableResult - func width(_ percent: Percent) -> PinLayout { - func context() -> String { return "width(\(percent.description))" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - return setWidth(percent.of(layoutSuperviewRect.width), context) - } - - @discardableResult - func width(of view: PView) -> PinLayout { - return setWidth(view.bounds.width, { return "width(of: \(viewDescription(view)))" }) - } - - @discardableResult - func minWidth(_ width: CGFloat) -> PinLayout { - setMinWidth(width, { return "minWidth(\(width))" }) - return self - } - - @discardableResult - func minWidth(_ percent: Percent) -> PinLayout { - func context() -> String { return "minWidth(\(percent.description))" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - return setMinWidth(percent.of(layoutSuperviewRect.width), context) - } - - @discardableResult - func maxWidth(_ width: CGFloat) -> PinLayout { - setMaxWidth(width, { return "maxWidth(\(width))" }) - return self - } - - @discardableResult - func maxWidth(_ percent: Percent) -> PinLayout { - func context() -> String { return "maxWidth(\(percent.description))" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - return setMaxWidth(percent.of(layoutSuperviewRect.width), context) - } - - @discardableResult - func height(_ height: CGFloat) -> PinLayout { - return setHeight(height, { return "height(\(height))" }) - } - - @discardableResult - func height(_ percent: Percent) -> PinLayout { - func context() -> String { return "height(\(percent.description))" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - return setHeight(percent.of(layoutSuperviewRect.height), context) - } - - @discardableResult - func height(of view: PView) -> PinLayout { - return setHeight(view.bounds.height, { return "height(of: \(viewDescription(view)))" }) - } - - @discardableResult - func minHeight(_ height: CGFloat) -> PinLayout { - setMinHeight(height, { return "minHeight(\(height))" }) - return self - } - - @discardableResult - func minHeight(_ percent: Percent) -> PinLayout { - func context() -> String { return "minHeight(\(percent.description))" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - return setMinHeight(percent.of(layoutSuperviewRect.height), context) - } - - @discardableResult - func maxHeight(_ height: CGFloat) -> PinLayout { - setMaxHeight(height, { return "maxHeight(\(height))" }) - return self - } - - @discardableResult - func maxHeight(_ percent: Percent) -> PinLayout { - func context() -> String { return "maxHeight(\(percent.description))" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - return setMaxHeight(percent.of(layoutSuperviewRect.height), context) - } - - // - // size, sizeToFit - // - @discardableResult - func size(_ size: CGSize) -> PinLayout { - return setSize(size, { return "size(CGSize(width: \(size.width), height: \(size.height)))" }) - } - - @discardableResult - func size(_ sideLength: CGFloat) -> PinLayout { - return setSize(CGSize(width: sideLength, height: sideLength), { return "size(sideLength: \(sideLength))" }) - } - - @discardableResult - func size(_ percent: Percent) -> PinLayout { - func context() -> String { return "size(\(percent.description))" } - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - let size = CGSize(width: percent.of(layoutSuperviewRect.width), height: percent.of(layoutSuperviewRect.height)) - return setSize(size, context) - } - - @discardableResult - func size(of view: PView) -> PinLayout { - func context() -> String { return "size(of \(viewDescription(view)))" } - return setSize(view.bounds.size, context) - } - -// @discardableResult -// func wrapSubViews() -> PinLayout { -// let neededWidth = view.subviews.max(by: { subview1, subview2 in subview1.frame.maxX < subview2.frame.maxX })?.frame.maxX ?? 0 -// let neededHeight = view.subviews.max(by: { subview1, subview2 in subview1.frame.maxY < subview2.frame.maxY })?.frame.maxY ?? 0 -// -// return setSize(CGSize(width: neededWidth, height: neededHeight), { return "wrapSubViews()" }) -// } - - @discardableResult - func aspectRatio(_ ratio: CGFloat) -> PinLayout { - return setAspectRatio(ratio, context: { "aspectRatio(\(ratio))" }) - } - - @discardableResult - func aspectRatio(of view: PView) -> PinLayout { - return setAspectRatio(view.bounds.width / view.bounds.height, context: { "aspectRatio(of: \(viewDescription(view)))" }) - } - - #if os(iOS) || os(tvOS) - @discardableResult - func aspectRatio() -> PinLayout { - func context() -> String { return "aspectRatio()" } - if let imageView = view as? UIImageView { - if let imageSize = imageView.image?.size { - setAspectRatio(imageSize.width / imageSize.height, context: context) - } else { - warnWontBeApplied("the layouted UIImageView's image hasn't been set", context) - } - } else { - warnWontBeApplied("the layouted must be an UIImageView() to use the aspectRatio() method without parameter.", context) - } - return self - } - #endif - - @discardableResult - func sizeToFit(_ fitType: FitType) -> PinLayout { - return setFitSize(fitType: fitType, { return "sizeToFit(\(fitType.name))" }) - } - - #if os(iOS) || os(tvOS) - @discardableResult - func fitSize() -> PinLayout { - return setFitSize(fitType: nil, { return "fitSize()" }) - } - #endif - - @discardableResult - func justify(_ value: HorizontalAlign) -> PinLayout { - justify = value - return self - } - - @discardableResult - func align(_ value: VerticalAlign) -> PinLayout { - align = value - return self - } - - // - // Margins - // - @discardableResult - func marginTop(_ value: CGFloat) -> PinLayout { - marginTop = value - return self - } - - @discardableResult - func marginTop(_ percent: Percent) -> PinLayout { - func context() -> String { return "marginTop(\(percent.description))" } - return marginTop(percent, context) - } - - @discardableResult - private func marginTop(_ percent: Percent, _ context: Context) -> Self { - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - marginTop = percent.of(layoutSuperviewRect.height) - return self - } - - @discardableResult - func marginLeft(_ value: CGFloat) -> PinLayout { - marginLeft = value - return self - } - - @discardableResult - func marginLeft(_ percent: Percent) -> PinLayout { - func context() -> String { return "marginLeft(\(percent.description))" } - return marginLeft(percent, context) - } - - @discardableResult - private func marginLeft(_ percent: Percent, _ context: Context) -> Self { - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - marginLeft = percent.of(layoutSuperviewRect.width) - return self - } - - @discardableResult - func marginBottom(_ value: CGFloat) -> PinLayout { - marginBottom = value - return self - } - - @discardableResult - func marginBottom(_ percent: Percent) -> PinLayout { - func context() -> String { return "marginBottom(\(percent.description))" } - return marginBottom(percent, context) - } - - @discardableResult - private func marginBottom(_ percent: Percent, _ context: Context) -> Self { - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - marginBottom = percent.of(layoutSuperviewRect.height) - return self - } - - @discardableResult - func marginRight(_ value: CGFloat) -> PinLayout { - marginRight = value - return self - } - - @discardableResult - func marginRight(_ percent: Percent) -> PinLayout { - func context() -> String { return "marginRight(\(percent.description))" } - return marginRight(percent, context) - } - - @discardableResult - private func marginRight(_ percent: Percent, _ context: Context) -> Self { - guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } - marginRight = percent.of(layoutSuperviewRect.width) - return self - } - - @discardableResult - func marginStart(_ value: CGFloat) -> PinLayout { - return isLTR() ? marginLeft(value) : marginRight(value) - } - - @discardableResult - func marginStart(_ percent: Percent) -> PinLayout { - func context() -> String { return "marginStart(\(percent.description))" } - return isLTR() ? marginLeft(percent, context) : marginRight(percent, context) - } - - @discardableResult - func marginEnd(_ value: CGFloat) -> PinLayout { - return isLTR() ? marginRight(value) : marginLeft(value) - } - - @discardableResult - func marginEnd(_ percent: Percent) -> PinLayout { - func context() -> String { return "marginEnd(\(percent.description))" } - return isLTR() ? marginRight(percent, context) : marginLeft(percent, context) - } - - @discardableResult - func marginHorizontal(_ value: CGFloat) -> PinLayout { - marginLeft = value - marginRight = value - return self - } - - @discardableResult - func marginHorizontal(_ percent: Percent) -> PinLayout { - func context() -> String { return "marginHorizontal(\(percent.description))" } - return marginHorizontal(percent, context) - } - - @discardableResult - private func marginHorizontal(_ percent: Percent, _ context: Context) -> Self { - return marginLeft(percent, context).marginRight(percent, context) - } - - @discardableResult - func marginVertical(_ value: CGFloat) -> PinLayout { - marginTop = value - marginBottom = value - return self - } - - @discardableResult - func marginVertical(_ percent: Percent) -> PinLayout { - func context() -> String { return "marginVertical(\(percent.description))" } - return marginVertical(percent, context) - } - - @discardableResult - private func marginVertical(_ percent: Percent, _ context: Context) -> Self { - return marginTop(percent, context).marginBottom(percent, context) - } - - @discardableResult - func margin(_ insets: PEdgeInsets) -> PinLayout { - marginTop = insets.top - marginBottom = insets.bottom - marginLeft = insets.left - marginRight = insets.right - return self - } - - #if os(iOS) || os(tvOS) - @available(tvOS 11.0, iOS 11.0, *) - @discardableResult - func margin(_ directionalInsets: NSDirectionalEdgeInsets) -> PinLayout { - marginTop = directionalInsets.top - marginBottom = directionalInsets.bottom - marginStart(directionalInsets.leading) - marginEnd(directionalInsets.trailing) - return self - } - #endif - - @discardableResult - func margin(_ value: CGFloat) -> PinLayout { - marginTop = value - marginLeft = value - marginBottom = value - marginRight = value - return self - } - - @discardableResult - func margin(_ percent: Percent) -> PinLayout { - func context() -> String { return "margin(\(percent.description))" } - return marginTop(percent, context) - .marginLeft(percent, context) - .marginBottom(percent, context) - .marginRight(percent, context) - } - - @discardableResult - func margin(_ top: CGFloat, _ left: CGFloat, _ bottom: CGFloat, _ right: CGFloat) -> PinLayout { - marginTop = top - marginLeft = left - marginBottom = bottom - marginRight = right - return self - } - - @discardableResult - func margin(_ top: Percent, _ left: Percent, _ bottom: Percent, _ right: Percent) -> PinLayout { - func context() -> String { - return "margin(top: \(top.description), left: \(left.description), bottom: \(bottom.description), right: \(right.description)" - } - return marginTop(top, context) - .marginLeft(left, context) - .marginBottom(bottom, context) - .marginRight(right, context) - } - - @discardableResult func margin(_ vertical: CGFloat, _ horizontal: CGFloat) -> PinLayout { - marginTop = vertical - marginLeft = horizontal - marginBottom = vertical - marginRight = horizontal - return self - } - - @discardableResult - func margin(_ vertical: Percent, _ horizontal: Percent) -> PinLayout { - func context() -> String { return "margin(vertical: \(vertical.description), horizontal: \(horizontal.description)"} - return marginVertical(vertical, context).marginHorizontal(horizontal, context) - } - - @discardableResult func margin(_ top: CGFloat, _ horizontal: CGFloat, _ bottom: CGFloat) -> PinLayout { - marginTop = top - marginLeft = horizontal - marginBottom = bottom - marginRight = horizontal - return self - } - - @discardableResult - func margin(_ top: Percent, _ horizontal: Percent, _ bottom: Percent) -> PinLayout { - func context() -> String { return "margin(top: \(top.description), horizontal: \(horizontal.description), bottom: \(bottom.description)"} - return marginTop(top, context).marginHorizontal(horizontal, context).marginBottom(bottom, context) - } - - @discardableResult - func pinEdges() -> PinLayout { - shouldPinEdges = true - return self - } -} - -// -// MARK: Private methods -// -extension PinLayoutImpl { - internal func setFitSize(fitType: FitType?, _ context: Context) -> PinLayout { - if let aspectRatio = _aspectRatio { - warnConflict(context, ["aspectRatio": aspectRatio]) - } else if fitType != nil && legacyFitSize { - warn("PinLayout Conflict: \(context()) won't be applied since it conflicts with fitSize().") - } else if let currentFitType = self.fitType, currentFitType != fitType { - warn("PinLayout Conflict: \(context()) won't be applied since it conflicts with sizeToFit(\(currentFitType.name)).") - } else { - if fitType == nil { - legacyFitSize = true - } else { - self.fitType = fitType - } - } - return self - } - - @discardableResult - internal func setAspectRatio(_ ratio: CGFloat, context: Context) -> PinLayout { - if let fitType = fitType { - warn("PinLayout Conflict: \(context()) won't be applied since it conflicts with sizeToFit(\(fitType.name)).") - } else if legacyFitSize { - warn("PinLayout Conflict: \(context()) won't be applied since it conflicts with fitSize().") - } else if ratio <= 0 { - warnWontBeApplied("the aspectRatio (\(ratio)) must be greater than zero.", context) - } else { - _aspectRatio = ratio - } - return self - } - - internal func layoutSuperviewRect(_ context: Context) -> CGRect? { - if let superview = view.superview { - return Coordinates.getViewRect(superview, keepTransform: keepTransform) - } else { - // Disable this warning: Using XIB, layoutSubview() is called even before views have been - // added, and there is no way to modify that strange behaviour of UIKit. - //warnWontBeApplied("the view must be added as a sub-view before being layouted using this method.", context) - return nil - } - } - - internal func layoutSuperview(_ context: Context) -> PView? { - if let superview = view.superview { - return superview - } else { - // Disable this warning: Using XIB, layoutSubview() is called even before views have been - // added, and there is no way to modify that strange behaviour of UIKit. - //warnWontBeApplied("the view must be added as a sub-view before being layouted using this method.", context) - return nil - } - } - - internal func referenceSuperview(_ referenceView: PView, _ context: Context) -> PView? { - if let superview = referenceView.superview { - return superview - } else { - warnWontBeApplied("the reference view \(viewDescription(referenceView)) must be added as a sub-view before being used as a reference.", context) - return nil - } - } -} - diff --git a/Sources/Impl/PinSafeArea.swift b/Sources/Impl/PinSafeArea.swift index ba57f4c1..68c58912 100644 --- a/Sources/Impl/PinSafeArea.swift +++ b/Sources/Impl/PinSafeArea.swift @@ -58,14 +58,7 @@ internal class PinSafeArea { // noop on iOS 11, `UIView.safeAreaInsetsDidChange` is natively supported } else { guard currentSafeAreaInsetsDidChangeMode == nil else { return } - if #available(iOS 9.0, tvOS 9.0, *) { - PinSafeArea.safeAreaInsetsDidChangeMode = .always - } else { - // Due to an issue with the keyboard on iOS 8, we don't activate - // the support of `UIView.safeAreaInsetsDidChange` on iOS 8. The developper can still - // activate it using `Pin.enableSafeArea(true)` - PinSafeArea.safeAreaInsetsDidChangeMode = .disable - } + PinSafeArea.safeAreaInsetsDidChangeMode = .always } } @@ -112,13 +105,13 @@ internal class PinSafeArea { } } - fileprivate static func extractMethodFrom(owner: AnyObject, selector: Selector) -> (() -> Void)? { + typealias Function = @convention(c) (AnyObject, Selector) -> Void + private static func extractMethodFrom(owner: AnyObject, selector: Selector) -> (() -> Void)? { guard let method = owner is AnyClass ? class_getClassMethod((owner as! AnyClass), selector) : class_getInstanceMethod(type(of: owner), selector) else { return nil } let implementation = method_getImplementation(method) - typealias Function = @convention(c) (AnyObject, Selector) -> Void let function = unsafeBitCast(implementation, to: Function.self) return { @@ -142,7 +135,7 @@ struct PinLayoutSwizzling { // ReactiveCocoa, Aspect, ..., and PinLayout. // To fix this issue, simply call `Pin.initPinLayout()` BEFORE initializing "New Relic" with // NewRelic.start(withApplicationToken:"APP_TOKEN"). - // See here for more information regarding this issue https://github.com/mirego/PinLayout/issues/130 + // See here for more information regarding this issue https://github.com/layoutBox/PinLayout/issues/130 viewWillLayoutSubviews(calledViewController, selector) } PinLayoutSwizzling.pinlayoutViewWillLayoutSubviews(viewController: calledViewController) @@ -159,19 +152,26 @@ struct PinLayoutSwizzling { self.originalImplementation = nil } - static fileprivate func pinlayoutViewWillLayoutSubviews(viewController: UIViewController) { + static private func pinlayoutViewWillLayoutSubviews(viewController: UIViewController) { if #available(iOS 11.0, tvOS 11.0, *) { assertionFailure() } if let view = viewController.view { - let safeAreaInsets = UIEdgeInsets(top: viewController.topLayoutGuide.length, left: 0, + let safeAreaInsets: UIEdgeInsets + + if #available(iOS 11.0, tvOS 11.0, *) { + safeAreaInsets = UIEdgeInsets(top: viewController.view.safeAreaInsets.top, left: 0, + bottom: viewController.view.safeAreaInsets.bottom, right: 0) + } else { + safeAreaInsets = UIEdgeInsets(top: viewController.topLayoutGuide.length, left: 0, bottom: viewController.bottomLayoutGuide.length, right: 0) + } // Set children safeArea up to 3 level, to limit the performance issue of computing this compatibilitySafeAreaInsets PinSafeArea.setViewSafeAreaInsets(view: view, insets: safeAreaInsets, recursiveLevel: 3) } } - static fileprivate func swizzleViewWillLayoutSubviews(_ class_: AnyClass, to block: @escaping ViewWillLayoutSubviewsBlock) -> IMP? { + static private func swizzleViewWillLayoutSubviews(_ class_: AnyClass, to block: @escaping ViewWillLayoutSubviewsBlock) -> IMP? { let selector = #selector(UIViewController.viewWillLayoutSubviews) let method = class_getInstanceMethod(class_, selector) let newImplementation = imp_implementationWithBlock(unsafeBitCast(block, to: AnyObject.self)) @@ -201,8 +201,13 @@ extension UIView { // UIViewController.viewWillLayoutSubviews hasn't been swizzled, we can return an insets // only if the view is a UIViewController's view. if let viewController = self.next as? UIViewController { - return UIEdgeInsets(top: viewController.topLayoutGuide.length, left: 0, - bottom: viewController.bottomLayoutGuide.length, right: 0) + if #available(iOS 11.0, tvOS 11.0, *) { + return UIEdgeInsets(top: viewController.view.safeAreaInsets.top, left: 0, + bottom: viewController.view.safeAreaInsets.bottom, right: 0) + } else { + return UIEdgeInsets(top: viewController.topLayoutGuide.length, left: 0, + bottom: viewController.bottomLayoutGuide.length, right: 0) + } } else { return .zero } @@ -225,7 +230,7 @@ extension UIView { internal func pinlayoutComputeSafeAreaInsets() -> UIEdgeInsets { if #available(iOS 11.0, tvOS 11.0, *) { assertionFailure() } - if let _ = self.next as? UIViewController { + if self.next as? UIViewController != nil { // The UIView is the UIViewController's UIView, its compatibilitySafeAreaInsets don't need to be recomputed. return self.pinlayoutSafeAreaInsets } else { @@ -261,4 +266,3 @@ extension UIView { } } #endif - diff --git a/Sources/Impl/TypesImpl.swift b/Sources/Impl/TypesImpl.swift index 4418fadd..2c07cd91 100644 --- a/Sources/Impl/TypesImpl.swift +++ b/Sources/Impl/TypesImpl.swift @@ -26,34 +26,16 @@ import Foundation #endif typealias Context = () -> String -typealias Size = (width: CGFloat?, height: CGFloat?) - -extension HorizontalAlign { - var description: String { - switch self { - case .left: return "left" - case .center: return "center" - case .right: return "right" - case .start: return "start" - case .end: return "end" - } - } -} -extension VerticalAlign { - var description: String { - switch self { - case .top: return "top" - case .center: return "center" - case .bottom: return "bottom" - } - } +struct Size { + var width: CGFloat? + var height: CGFloat? } - -class EdgeListImpl: EdgeList { - internal let view: PView - init(view: PView) { +class EdgeListImpl: EdgeList { + internal let view: PinView + + init(view: PinView) { self.view = view } @@ -70,18 +52,18 @@ class EdgeListImpl: EdgeList { var end: HorizontalEdge { return view.isLTR() ? right : left } } -class HorizontalEdgeImpl: HorizontalEdge { +class HorizontalEdgeImpl: HorizontalEdge { enum EdgeType: String { case left case hCenter case right } - let view: PView + let view: PinView let type: EdgeType func x(keepTransform: Bool) -> CGFloat { - let rect = Coordinates.getViewRect(view, keepTransform: keepTransform) + let rect = view.getRect(keepTransform: keepTransform) switch type { case .left: return rect.origin.x @@ -90,24 +72,24 @@ class HorizontalEdgeImpl: HorizontalEdge { } } - internal init(view: PView, type: EdgeType) { + internal init(view: PinView, type: EdgeType) { self.view = view self.type = type } } -class VerticalEdgeImpl: VerticalEdge { +class VerticalEdgeImpl: VerticalEdge { enum EdgeType: String { case top case vCenter case bottom } - internal let view: PView + internal let view: PinView internal let type: EdgeType func y(keepTransform: Bool) -> CGFloat { - let rect = Coordinates.getViewRect(view, keepTransform: keepTransform) + let rect = view.getRect(keepTransform: keepTransform) switch type { case .top: return rect.origin.y @@ -117,16 +99,16 @@ class VerticalEdgeImpl: VerticalEdge { } } - internal init(view: PView, type: EdgeType) { + internal init(view: PinView, type: EdgeType) { self.view = view self.type = type } } -class AnchorListImpl: AnchorList { - internal let view: PView +class AnchorListImpl: AnchorList { + internal let view: PinView - internal init(view: PView) { + internal init(view: PinView) { self.view = view } @@ -161,8 +143,8 @@ enum AnchorType: String { case bottomRight } -class AnchorImpl: Anchor { - let view: PView +class AnchorImpl: Anchor { + let view: PinView let type: AnchorType func point(keepTransform: Bool) -> CGPoint { @@ -179,34 +161,8 @@ class AnchorImpl: Anchor { } } - fileprivate init(view: PView, type: AnchorType) { + fileprivate init(view: PinView, type: AnchorType) { self.view = view self.type = type } } - -extension CGFloat { - public var description: String { - if self.truncatingRemainder(dividingBy: 1) == 0.0 { - return "\(Int(self))" - } else { - return "\(self)" - } - } -} - -internal extension FitType { - var name: String { - switch self { - case .width: return ".width" - case .height: return ".height" - case .widthFlexible: return ".widthFlexible" - case .heightFlexible: return ".heightFlexible" - } - } - - var isFlexible: Bool { - return self == .widthFlexible || self == .heightFlexible - } -} - diff --git a/Sources/Layoutable+PinLayout.swift b/Sources/Layoutable+PinLayout.swift new file mode 100644 index 00000000..711e4902 --- /dev/null +++ b/Sources/Layoutable+PinLayout.swift @@ -0,0 +1,30 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +extension Layoutable { + public var anchor: AnchorList { + return AnchorListImpl(view: self as! PinView) + } + + public var edge: EdgeList { + return EdgeListImpl(view: self as! PinView) + } +} diff --git a/Sources/Impl/UIView+LTR.swift b/Sources/Layoutable.swift similarity index 58% rename from Sources/Impl/UIView+LTR.swift rename to Sources/Layoutable.swift index 4931f65a..fa4e9963 100644 --- a/Sources/Impl/UIView+LTR.swift +++ b/Sources/Layoutable.swift @@ -17,37 +17,22 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -import Foundation - #if os(iOS) || os(tvOS) import UIKit - -extension UIView { - func isLTR() -> Bool { - switch Pin.layoutDirection { - case .auto: - if #available(iOS 9.0, *) { - return UIView.userInterfaceLayoutDirection(for: semanticContentAttribute) == .leftToRight - } else if let shared = UIApplication.value(forKey: "sharedApplication") as? UIApplication { - return shared.userInterfaceLayoutDirection == .leftToRight - } else { - return true - } - case .ltr: return true - case .rtl: return false - } - } -} #else import AppKit +#endif + +public protocol Layoutable: AnyObject, Equatable, CustomDebugStringConvertible { + associatedtype PinView: Layoutable -extension NSView { - func isLTR() -> Bool { - switch Pin.layoutDirection { - case .auto: return self.userInterfaceLayoutDirection == .leftToRight - case .ltr: return true - case .rtl: return false - } - } + var superview: PinView? { get } + var subviews: [PinView] { get } + + func getRect(keepTransform: Bool) -> CGRect + func setRect(_ rect: CGRect, keepTransform: Bool) + + func convert(_ point: CGPoint, to view: PinView?) -> CGPoint + + func isLTR() -> Bool } -#endif diff --git a/Sources/NSView+PinLayout.swift b/Sources/NSView+PinLayout.swift deleted file mode 100644 index 6e564a13..00000000 --- a/Sources/NSView+PinLayout.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// NSView+PinLayout.swift -// PinLayout -// -// Created by Luc Dion on 2018-04-13. -// Copyright © 2018 mcswiftlayyout.mirego.com. All rights reserved. -// - -import Foundation - -#if os(macOS) -import AppKit - -public extension NSView { - public var pin: PinLayout { - return PinLayoutImpl(view: self, keepTransform: true) - } - - public var pinFrame: PinLayout { - return PinLayoutImpl(view: self, keepTransform: false) - } - - public var anchor: AnchorList { - return AnchorListImpl(view: self) - } - - public var edge: EdgeList { - return EdgeListImpl(view: self) - } -} -#endif diff --git a/Sources/ObjectiveC/PinLayoutObjC.swift b/Sources/ObjectiveC/PinLayoutObjC.swift index f7d9ade7..1e21646a 100644 --- a/Sources/ObjectiveC/PinLayoutObjC.swift +++ b/Sources/ObjectiveC/PinLayoutObjC.swift @@ -21,182 +21,228 @@ import Foundation #if os(iOS) || os(tvOS) import UIKit - -// MARK: - PinLayout UIView's extension -public extension UIView { - // Expose PinLayout's objective-c interface. - @objc public var pinObjc: PinLayoutObjC { - return PinLayoutObjCImpl(view: self, keepTransform: true) - } -} +#else +import AppKit +#endif /** We must have a different interface for objective-c. The PinLayout's Swift interface use some feature not available to objective-c, including overloading. */ @objc public protocol PinLayoutObjC { + #if os(iOS) || os(tvOS) + var safeArea: PEdgeInsets { get } + #endif + + typealias POVoid = () -> PinLayoutObjC? + typealias POValue = (_ value: CGFloat) -> PinLayoutObjC? + typealias POEdgeInsets = (_ insets: PEdgeInsets) -> PinLayoutObjC? + typealias POVEdge = (_ edge: VerticalEdge) -> PinLayoutObjC? + typealias POHEdge = (_ edge: HorizontalEdge) -> PinLayoutObjC? + typealias POAnchor = (_ anchor: Anchor) -> PinLayoutObjC? + typealias POSize = (_ size: CGSize) -> PinLayoutObjC? + typealias POHAlign = (_ hAlign: HorizontalAlign) -> PinLayoutObjC? + typealias POVAlign = (_ hAlign: VerticalAlign) -> PinLayoutObjC? + typealias POWrapType = (_ type: WrapType) -> PinLayoutObjC? + typealias POWrapTypePadding = (_ type: WrapType, _ padding: CGFloat) -> PinLayoutObjC? + typealias POWrapTypeInsets = (_ type: WrapType, _ insets: PEdgeInsets) -> PinLayoutObjC? + typealias POFitType = (_ type: Fit) -> PinLayoutObjC? + /** With the Objective-C interface, you must call the \"layout\" method to ensure the view is layouted correctly. Ex: - [[[textLabel.pin_objc top] left] layout];" - [[[textLabel.pin_objc top] left] layout];" + textLabel.pinObjc.top().left().layout() */ - func layout() - - @discardableResult func top() -> PinLayoutObjC - @discardableResult func top(_ value: CGFloat) -> PinLayoutObjC - @discardableResult func top(percent: CGFloat) -> PinLayoutObjC - - @discardableResult func left() -> PinLayoutObjC - @discardableResult func left(_ value: CGFloat) -> PinLayoutObjC - @discardableResult func left(percent: CGFloat) -> PinLayoutObjC - - @discardableResult func bottom() -> PinLayoutObjC - @discardableResult func bottom(_ value: CGFloat) -> PinLayoutObjC - @discardableResult func bottom(percent: CGFloat) -> PinLayoutObjC - - @discardableResult func right() -> PinLayoutObjC - @discardableResult func right(_ value: CGFloat) -> PinLayoutObjC - @discardableResult func right(percent: CGFloat) -> PinLayoutObjC - - @discardableResult func hCenter() -> PinLayoutObjC - @discardableResult func hCenter(_ value: CGFloat) -> PinLayoutObjC - @discardableResult func hCenter(percent: CGFloat) -> PinLayoutObjC - - @discardableResult func vCenter() -> PinLayoutObjC - @discardableResult func vCenter(_ value: CGFloat) -> PinLayoutObjC - @discardableResult func vCenter(percent: CGFloat) -> PinLayoutObjC - + var layout: POVoid { get } + + var top: POVoid { get } + var topValue: POValue { get } + var topPercent: POValue { get } + var topInsets: POEdgeInsets { get } + + var left: POVoid { get } + var leftValue: POValue { get } + var leftPercent: POValue { get } + var leftInsets: POEdgeInsets { get } + + var bottom: POVoid { get } + var bottomValue: POValue { get } + var bottomPercent: POValue { get } + var bottomInsets: POEdgeInsets { get } + + var right: POVoid { get } + var rightValue: POValue { get } + var rightPercent: POValue { get } + var rightInsets: POEdgeInsets { get } + + var hCenter: POVoid { get } + var hCenterValue: POValue { get } + var hCenterPercent: POValue { get } + + var vCenter: POVoid { get } + var vCenterValue: POValue { get } + var vCenterPercent: POValue { get } + // RTL support - @discardableResult func start() -> PinLayoutObjC - @discardableResult func start(_ value: CGFloat) -> PinLayoutObjC - @discardableResult func start(percent: CGFloat) -> PinLayoutObjC - @discardableResult func end() -> PinLayoutObjC - @discardableResult func end(_ value: CGFloat) -> PinLayoutObjC - @discardableResult func end(percent: CGFloat) -> PinLayoutObjC - + var start: POVoid { get } + var startValue: POValue { get } + var startPercent: POValue { get } + var startInsets: POEdgeInsets { get } + + var end: POVoid { get } + var endValue: POValue { get } + var endPercent: POValue { get } + var endInsets: POEdgeInsets { get } + // Pin multiple edges at once. - @discardableResult func all() -> PinLayoutObjC - @discardableResult func horizontally() -> PinLayoutObjC - @discardableResult func vertically() -> PinLayoutObjC - + var all: POVoid { get } + var horizontally: POVoid { get } + var vertically: POVoid { get } + // // MARK: Layout using edges // - @discardableResult func top(to edge: VerticalEdge) -> PinLayoutObjC - @discardableResult func vCenter(to edge: VerticalEdge) -> PinLayoutObjC - @discardableResult func bottom(to edge: VerticalEdge) -> PinLayoutObjC - @discardableResult func left(to edge: HorizontalEdge) -> PinLayoutObjC - @discardableResult func hCenter(to edge: HorizontalEdge) -> PinLayoutObjC - @discardableResult func right(to edge: HorizontalEdge) -> PinLayoutObjC + var topToEdge: POVEdge { get } + var vCenterToEdge: POVEdge { get } + var bottomToEdge: POVEdge { get } + var leftToEdge: POHEdge { get } + var hCenterToEdge: POHEdge { get } + var rightToEdge: POHEdge { get } + // RTL support - @discardableResult func start(to edge: HorizontalEdge) -> PinLayoutObjC - @discardableResult func end(to edge: HorizontalEdge) -> PinLayoutObjC - + var startToEdge: POHEdge { get } + var endToEdge: POHEdge { get } + // // MARK: Layout using anchors // - @discardableResult func topLeft(to anchor: Anchor) -> PinLayoutObjC - @discardableResult func topLeft() -> PinLayoutObjC - @discardableResult func topStart(to anchor: Anchor) -> PinLayoutObjC - @discardableResult func topStart() -> PinLayoutObjC - - @discardableResult func topCenter(to anchor: Anchor) -> PinLayoutObjC - @discardableResult func topCenter() -> PinLayoutObjC - - @discardableResult func topRight(to anchor: Anchor) -> PinLayoutObjC - @discardableResult func topRight() -> PinLayoutObjC - @discardableResult func topEnd(to anchor: Anchor) -> PinLayoutObjC - @discardableResult func topEnd() -> PinLayoutObjC - - @discardableResult func centerLeft(to anchor: Anchor) -> PinLayoutObjC - @discardableResult func centerLeft() -> PinLayoutObjC - @discardableResult func centerStart(to anchor: Anchor) -> PinLayoutObjC - @discardableResult func centerStart() -> PinLayoutObjC - - @discardableResult func center(to anchor: Anchor) -> PinLayoutObjC - @discardableResult func center() -> PinLayoutObjC - - @discardableResult func centerRight(to anchor: Anchor) -> PinLayoutObjC - @discardableResult func centerRight() -> PinLayoutObjC - @discardableResult func centerEnd(to anchor: Anchor) -> PinLayoutObjC - @discardableResult func centerEnd() -> PinLayoutObjC - - @discardableResult func bottomLeft(to anchor: Anchor) -> PinLayoutObjC - @discardableResult func bottomLeft() -> PinLayoutObjC - @discardableResult func bottomStart(to anchor: Anchor) -> PinLayoutObjC - @discardableResult func bottomStart() -> PinLayoutObjC - - @discardableResult func bottomCenter(to anchor: Anchor) -> PinLayoutObjC - @discardableResult func bottomCenter() -> PinLayoutObjC - - @discardableResult func bottomRight(to anchor: Anchor) -> PinLayoutObjC - @discardableResult func bottomRight() -> PinLayoutObjC - @discardableResult func bottomEnd(to anchor: Anchor) -> PinLayoutObjC - @discardableResult func bottomEnd() -> PinLayoutObjC - + var topLeftToAnchor: POAnchor { get } + var topLeft: POVoid { get } + var topStartToAnchor: POAnchor { get } + var topStart: POVoid { get } + + var topCenterToAnchor: POAnchor { get } + var topCenter: POVoid { get } + + var topRightToAnchor: POAnchor { get } + var topRight: POVoid { get } + var topEndToAnchor: POAnchor { get } + var topEnd: POVoid { get } + + var centerLeftToAnchor: POAnchor { get } + var centerLeft: POVoid { get } + var centerStartToAnchor: POAnchor { get } + var centerStart: POVoid { get } + + var centerToAnchor: POAnchor { get } + var center: POVoid { get } + + var centerRightToAnchor: POAnchor { get } + var centerRight: POVoid { get } + var centerEndToAnchor: POAnchor { get } + var centerEnd: POVoid { get } + + var bottomLeftToAnchor: POAnchor { get } + var bottomLeft: POVoid { get } + var bottomStartToAnchor: POAnchor { get } + var bottomStart: POVoid { get } + + var bottomCenterToAnchor: POAnchor { get } + var bottomCenter: POVoid { get } + + var bottomRightToAnchor: POAnchor { get } + var bottomRight: POVoid { get } + var bottomEndToAnchor: POAnchor { get } + var bottomEnd: POVoid { get } + // // MARK: Layout using relative positioning // - @discardableResult func above(of: UIView) -> PinLayoutObjC - @discardableResult func above(ofViews: [UIView]) -> PinLayoutObjC - @discardableResult func above(of: UIView, aligned: HorizontalAlign) -> PinLayoutObjC - @discardableResult func above(ofViews: [UIView], aligned: HorizontalAlign) -> PinLayoutObjC - - @discardableResult func below(of: UIView) -> PinLayoutObjC - @discardableResult func below(ofViews: [UIView]) -> PinLayoutObjC - @discardableResult func below(of: UIView, aligned: HorizontalAlign) -> PinLayoutObjC - @discardableResult func below(ofViews: [UIView], aligned: HorizontalAlign) -> PinLayoutObjC - - @discardableResult func left(of: UIView) -> PinLayoutObjC - @discardableResult func left(ofViews: [UIView]) -> PinLayoutObjC - @discardableResult func left(of: UIView, aligned: VerticalAlign) -> PinLayoutObjC - @discardableResult func left(ofViews: [UIView], aligned: VerticalAlign) -> PinLayoutObjC - - @discardableResult func right(of: UIView) -> PinLayoutObjC - @discardableResult func right(ofViews: [UIView]) -> PinLayoutObjC - @discardableResult func right(of: UIView, aligned: VerticalAlign) -> PinLayoutObjC - @discardableResult func right(ofViews: [UIView], aligned: VerticalAlign) -> PinLayoutObjC + #if os(iOS) || os(tvOS) + typealias POView = (_ view: UIView) -> PinLayoutObjC? + typealias POViews = (_ views: [UIView]) -> PinLayoutObjC? + typealias POViewHAligned = (_ view: UIView, _ aligned: HorizontalAlign) -> PinLayoutObjC? + typealias POViewsHAligned = (_ views: [UIView], _ aligned: HorizontalAlign) -> PinLayoutObjC? + typealias POViewVAligned = (_ view: UIView, _ aligned: VerticalAlign) -> PinLayoutObjC? + typealias POViewsVAligned = (_ views: [UIView], _ aligned: VerticalAlign) -> PinLayoutObjC? + #elseif os(macOS) + typealias POView = (_ view: NSView) -> PinLayoutObjC? + typealias POViews = (_ views: [NSView]) -> PinLayoutObjC? + typealias POViewHAligned = (_ view: NSView, _ aligned: HorizontalAlign) -> PinLayoutObjC? + typealias POViewsHAligned = (_ views: [NSView], _ aligned: HorizontalAlign) -> PinLayoutObjC? + typealias POViewVAligned = (_ view: NSView, _ aligned: VerticalAlign) -> PinLayoutObjC? + typealias POViewsVAligned = (_ views: [NSView], _ aligned: VerticalAlign) -> PinLayoutObjC? + #endif + + var aboveOf: POView { get } + var aboveOfViews: POViews { get } + var aboveOfAligned: POViewHAligned { get } + var aboveOfViewsAligned: POViewsHAligned { get } + + var belowOf: POView { get } + var belowOfViews: POViews { get } + var belowOfAligned: POViewHAligned { get } + var belowOfViewsAligned: POViewsHAligned { get } + var leftOf: POView { get } + var leftOfViews: POViews { get } + var leftOfAligned: POViewVAligned { get } + var leftOfViewsAligned: POViewsVAligned { get } + + var rightOf: POView { get } + var rightOfViews: POViews { get } + var rightOfAligned: POViewVAligned { get } + var rightOfViewsAligned: POViewsVAligned { get } // RTL support - @discardableResult func before(of: UIView) -> PinLayoutObjC - @discardableResult func before(ofViews: [UIView]) -> PinLayoutObjC - @discardableResult func before(of: UIView, aligned: VerticalAlign) -> PinLayoutObjC - @discardableResult func before(ofViews: [UIView], aligned: VerticalAlign) -> PinLayoutObjC - @discardableResult func after(of: UIView) -> PinLayoutObjC - @discardableResult func after(ofViews: [UIView]) -> PinLayoutObjC - @discardableResult func after(of: UIView, aligned: VerticalAlign) -> PinLayoutObjC - @discardableResult func after(ofViews: [UIView], aligned: VerticalAlign) -> PinLayoutObjC + var beforeOf: POView { get } + var beforeOfViews: POViews { get } + var beforeOfAligned: POViewVAligned { get } + var beforeOfViewsAligned: POViewsVAligned { get } + var afterOf: POView { get } + var afterOfViews: POViews { get } + var afterOfAligned: POViewVAligned { get } + var afterOfViewsAligned: POViewsVAligned { get } // // MARK: justify / align // - @discardableResult func justify(_: HorizontalAlign) -> PinLayoutObjC - @discardableResult func align(_: VerticalAlign) -> PinLayoutObjC + var justify: POHAlign { get } + var align: POVAlign { get } // // MARK: Width, height and size // - @discardableResult func width(_ width: CGFloat) -> PinLayoutObjC - @discardableResult func width(percent: CGFloat) -> PinLayoutObjC - @discardableResult func width(of view: UIView) -> PinLayoutObjC - @discardableResult func minWidth(_ width: CGFloat) -> PinLayoutObjC - @discardableResult func minWidth(percent: CGFloat) -> PinLayoutObjC - @discardableResult func maxWidth(_ width: CGFloat) -> PinLayoutObjC - @discardableResult func maxWidth(percent: CGFloat) -> PinLayoutObjC - - @discardableResult func height(_ height: CGFloat) -> PinLayoutObjC - @discardableResult func height(percent: CGFloat) -> PinLayoutObjC - @discardableResult func height(of view: UIView) -> PinLayoutObjC - @discardableResult func minHeight(_ height: CGFloat) -> PinLayoutObjC - @discardableResult func minHeight(percent: CGFloat) -> PinLayoutObjC - @discardableResult func maxHeight(_ height: CGFloat) -> PinLayoutObjC - @discardableResult func maxHeight(percent: CGFloat) -> PinLayoutObjC - - @discardableResult func size(_ size: CGSize) -> PinLayoutObjC - @discardableResult func size(length: CGFloat) -> PinLayoutObjC - @discardableResult func size(percent: CGFloat) -> PinLayoutObjC - @discardableResult func size(of view: UIView) -> PinLayoutObjC + var width: POValue { get } + var widthPercent: POValue { get } + var widthOf: POView { get } + + var minWidth: POValue { get } + var minWidthPercent: POValue { get } + var maxWidth: POValue { get } + var maxWidthPercent: POValue { get } + + var height: POValue { get } + var heightPercent: POValue { get } + var heightOf: POView { get } + + var minHeight: POValue { get } + var minHeightPercent: POValue { get } + var maxHeight: POValue { get } + var maxHeightPercent: POValue { get } + + var size: POSize { get } + var sizeLength: POValue { get } + var sizePercent: POValue { get } + var sizeOf: POView{ get } + + // + // MARK: wrapContent + var wrapContent: POVoid { get } + var wrapContentPadding: POValue { get } + var wrapContentInsets: POEdgeInsets { get } + var wrapContentType: POWrapType { get } + var wrapContentTypePadding: POWrapTypePadding { get } + var wrapContentTypeInsets: POWrapTypeInsets { get } /** Set the view aspect ratio. @@ -209,7 +255,7 @@ public extension UIView { * AspectRatio respects the min (minWidth/minHeight) and the max (maxWidth/maxHeight) dimensions of an item. */ - @discardableResult func aspectRatio(_ ratio: CGFloat) -> PinLayoutObjC + var aspectRatioValue: POValue { get } /** Set the view aspect ratio using another UIView's aspect ratio. @@ -220,17 +266,21 @@ public extension UIView { * AspectRatio respects the min (minWidth/minHeight) and the max (maxWidth/maxHeight) dimensions of an item. */ - @discardableResult func aspectRatio(of view: UIView) -> PinLayoutObjC + var aspectRatioOf: POView { get } + /** If the layouted view is an UIImageView, this method will set the aspectRatio using the UIImageView's image dimension. For other types of views, this method as no impact. */ - @discardableResult func aspectRatio() -> PinLayoutObjC + #if os(iOS) || os(tvOS) + var aspectRatio: POVoid { get } + #endif - @discardableResult func sizeToFit(_ fitType: Fit) -> PinLayoutObjC - @discardableResult func fitSize() -> PinLayoutObjC + var sizeToFit: POVoid { get } + + var sizeToFitType: POFitType { get } // // MARK: Margins @@ -239,22 +289,22 @@ public extension UIView { /** Set the top margin. */ - @discardableResult func marginTop(_ value: CGFloat) -> PinLayoutObjC + var marginTop: POValue { get } /** Set the left margin. */ - @discardableResult func marginLeft(_ value: CGFloat) -> PinLayoutObjC + var marginLeft: POValue { get } /** Set the bottom margin. */ - @discardableResult func marginBottom(_ value: CGFloat) -> PinLayoutObjC + var marginBottom: POValue { get } /** Set the right margin. */ - @discardableResult func marginRight(_ value: CGFloat) -> PinLayoutObjC + var marginRight: POValue { get } // RTL support /** @@ -264,7 +314,7 @@ public extension UIView { * In LTR direction, start margin specify the **left** margin. * In RTL direction, start margin specify the **right** margin. */ - @discardableResult func marginStart(_ value: CGFloat) -> PinLayoutObjC + var marginStart: POValue { get } /** Set the end margin. @@ -273,23 +323,23 @@ public extension UIView { * In LTR direction, end margin specify the **right** margin. * In RTL direction, end margin specify the **left** margin. */ - @discardableResult func marginEnd(_ value: CGFloat) -> PinLayoutObjC + var marginEnd: POValue { get } /** Set the left, right, start and end margins to the specified value. */ - @discardableResult func marginHorizontal(_ value: CGFloat) -> PinLayoutObjC + var marginHorizontal: POValue { get } /** Set the top and bottom margins to the specified value. */ - @discardableResult func marginVertical(_ value: CGFloat) -> PinLayoutObjC + var marginVertical: POValue { get } /** Set all margins using UIEdgeInsets. This method is particularly useful to set all margins using iOS 11 `UIView.safeAreaInsets`. */ - @discardableResult func margin(insets: UIEdgeInsets) -> PinLayoutObjC + var marginInsets: POEdgeInsets { get } /** Set margins using NSDirectionalEdgeInsets. @@ -297,28 +347,28 @@ public extension UIView { Available only on iOS 11 and higher. */ - //@available(iOS 11.0, *) - //@discardableResult func margin(_ directionalInsets: NSDirectionalEdgeInsets) -> PinLayoutObjC + // @available(iOS 11.0, *) + // @discardableResult func margin(_ directionalInsets: NSDirectionalEdgeInsets) -> PinLayoutObjC /** Set all margins to the specified value. */ - @discardableResult func margin(_ value: CGFloat) -> PinLayoutObjC + var margin: POValue { get } /** Set individually vertical margins (top, bottom) and horizontal margins (left, right, start, end). */ - @discardableResult func margin(vertical: CGFloat, horizontal: CGFloat) -> PinLayoutObjC + var marginVH: (_ vertical: CGFloat, _ horizontal: CGFloat) -> PinLayoutObjC? { get } /** Set individually top, horizontal margins and bottom margin. */ - @discardableResult func margin(top: CGFloat, horizontal: CGFloat, bottom: CGFloat) -> PinLayoutObjC + var marginTHB: (_ top: CGFloat, _ horizontal: CGFloat, _ bottom: CGFloat) -> PinLayoutObjC? { get } /** Set individually top, left, bottom and right margins. */ - @discardableResult func margin(top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat) -> PinLayoutObjC + var marginTLBR: (_ top: CGFloat, _ left: CGFloat, _ bottom: CGFloat, _ right: CGFloat) -> PinLayoutObjC? { get } /// Normally if only either left or right has been specified, PinLayout will MOVE the view to apply left or right margins. /// This is also true even if the width has been set. @@ -326,14 +376,13 @@ public extension UIView { /// moving the view. /// /// - Returns: PinLayout - @discardableResult func pinEdges() -> PinLayoutObjC + var pinEdges: POVoid { get } } - + @objc public enum Fit: Int { case width case height case widthFlexible case heightFlexible + case content } - -#endif diff --git a/Sources/ObjectiveC/PinLayoutObjCImpl.swift b/Sources/ObjectiveC/PinLayoutObjCImpl.swift index 89f7cc78..0b98f912 100644 --- a/Sources/ObjectiveC/PinLayoutObjCImpl.swift +++ b/Sources/ObjectiveC/PinLayoutObjCImpl.swift @@ -19,12 +19,49 @@ #if os(iOS) || os(tvOS) import UIKit +#else +import AppKit +#endif -@objc public class PinLayoutObjCImpl: NSObject, PinLayoutObjC { - fileprivate var impl: PinLayoutImpl? - - init(view: UIView, keepTransform: Bool) { - impl = PinLayoutImpl(view: view, keepTransform: keepTransform) +#if os(iOS) || os(tvOS) +@objc extension UIView { + public var anchor: AnchorList { + return AnchorListImpl(view: self) + } + + public var edge: EdgeList { + return EdgeListImpl(view: self) + } +} +#else +@objc extension NSView { + public var anchor: AnchorList { + return AnchorListImpl(view: self) + } + + public var edge: EdgeList { + return EdgeListImpl(view: self) + } +} +#endif + +@objc class PinLayoutObjCImpl: NSObject, PinLayoutObjC { + #if os(iOS) || os(tvOS) + typealias PView = UIView + #else + typealias PView = NSView + #endif + + private var impl: PinLayout? + + #if os(iOS) || os(tvOS) + var safeArea: PEdgeInsets { + return impl?.safeArea ?? .zero + } + #endif + + init(view: PView, keepTransform: Bool) { + impl = PinLayout(view: view, keepTransform: keepTransform) } deinit { @@ -32,670 +69,1002 @@ import UIKit impl?.warn("With the Objective-C interface, you must call the \"layout\" method to ensure the view is layouted correctly (ex: [[[textLabel.pin_objc top] left] layout];") } } + + var layout: POVoid { + return { [weak self] in + _ = self?.impl?.layout() + self?.impl = nil + return self + } + } - public func layout() { - // With objective-c PinLayoutObjCImpl instance are sometimes deallocated only after the context has been quit. For this reason - // developpers must call the layout: method implicetely. - impl?.layout() - impl = nil + var top: POVoid { + return { [weak self] in + _ = self?.impl?.top() + return self + } } - public func top() -> PinLayoutObjC { - impl?.top() - return self + var topValue: POValue { + return { [weak self] value in + _ = self?.impl?.top(value) + return self + } } - public func top(_ value: CGFloat) -> PinLayoutObjC { - impl?.top(value) - return self + var topPercent: POValue { + return { [weak self] percent in + _ = self?.impl?.top(percent%) + return self + } } - public func top(percent: CGFloat) -> PinLayoutObjC { - impl?.top(percent%) - return self + var topInsets: POEdgeInsets { + return { [weak self] insets in + _ = self?.impl?.top(insets) + return self + } } - public func left() -> PinLayoutObjC { - impl?.left() - return self + var left: POVoid { + return { [weak self] in + _ = self?.impl?.left() + return self + } } - public func left(_ value: CGFloat) -> PinLayoutObjC { - impl?.left(value) - return self + var leftValue: POValue { + return { [weak self] value in + _ = self?.impl?.left(value) + return self + } } - public func left(percent: CGFloat) -> PinLayoutObjC { - impl?.left(percent%) - return self + var leftPercent: POValue { + return { [weak self] percent in + _ = self?.impl?.left(percent%) + return self + } } - public func bottom() -> PinLayoutObjC { - impl?.bottom() - return self + var leftInsets: POEdgeInsets { + return { [weak self] insets in + _ = self?.impl?.left(insets) + return self + } } - public func bottom(_ value: CGFloat) -> PinLayoutObjC { - impl?.bottom(value) - return self + var bottom: POVoid { + return { [weak self] in + _ = self?.impl?.bottom() + return self + } } - public func bottom(percent: CGFloat) -> PinLayoutObjC { - impl?.bottom(percent%) - return self + var bottomValue: POValue { + return { [weak self] value in + _ = self?.impl?.bottom(value) + return self + } } - public func right() -> PinLayoutObjC { - impl?.right() - return self + var bottomPercent: POValue { + return { [weak self] percent in + _ = self?.impl?.bottom(percent%) + return self + } } - public func right(_ value: CGFloat) -> PinLayoutObjC { - impl?.right(value) - return self + var bottomInsets: POEdgeInsets { + return { [weak self] insets in + _ = self?.impl?.bottom(insets) + return self + } } - public func right(percent: CGFloat) -> PinLayoutObjC { - impl?.right(percent%) - return self + var right: POVoid { + return { [weak self] in + _ = self?.impl?.right() + return self + } } - public func hCenter() -> PinLayoutObjC { - impl?.hCenter() - return self + var rightValue: POValue { + return { [weak self] value in + _ = self?.impl?.right(value) + return self + } } - public func hCenter(_ value: CGFloat) -> PinLayoutObjC { - impl?.hCenter(value) - return self + var rightPercent: POValue { + return { [weak self] percent in + _ = self?.impl?.right(percent%) + return self + } } - public func hCenter(percent: CGFloat) -> PinLayoutObjC { - impl?.hCenter(percent%) - return self + var rightInsets: POEdgeInsets { + return { [weak self] insets in + _ = self?.impl?.right(insets) + return self + } } - public func vCenter() -> PinLayoutObjC { - impl?.vCenter() - return self + var hCenter: POVoid { + return { [weak self] in + _ = self?.impl?.hCenter() + return self + } } - public func vCenter(_ value: CGFloat) -> PinLayoutObjC { - impl?.vCenter(value) - return self + var hCenterValue: POValue { + return { [weak self] value in + _ = self?.impl?.hCenter(value) + return self + } } - public func vCenter(percent: CGFloat) -> PinLayoutObjC { - impl?.vCenter(percent%) - return self + var hCenterPercent: POValue { + return { [weak self] percent in + _ = self?.impl?.hCenter(percent%) + return self + } } - public func start() -> PinLayoutObjC { - impl?.start() - return self + var vCenter: POVoid { + return { [weak self] in + _ = self?.impl?.vCenter() + return self + } } - public func start(_ value: CGFloat) -> PinLayoutObjC { - impl?.start(value) - return self + var vCenterValue: POValue { + return { [weak self] value in + _ = self?.impl?.vCenter(value) + return self + } } - public func start(percent: CGFloat) -> PinLayoutObjC { - impl?.start(percent%) - return self + var vCenterPercent: POValue { + return { [weak self] percent in + _ = self?.impl?.vCenter(percent%) + return self + } } - public func end() -> PinLayoutObjC { - impl?.end() - return self + var start: POVoid { + return { [weak self] in + _ = self?.impl?.start() + return self + } } - public func end(_ value: CGFloat) -> PinLayoutObjC { - impl?.end(value) - return self + var startValue: POValue { + return { [weak self] value in + _ = self?.impl?.start(value) + return self + } } - public func end(percent: CGFloat) -> PinLayoutObjC { - impl?.end(percent%) - return self + var startPercent: POValue { + return { [weak self] percent in + _ = self?.impl?.start(percent%) + return self + } } - public func top(to edge: VerticalEdge) -> PinLayoutObjC { - impl?.top(to: edge) - return self + var startInsets: POEdgeInsets { + return { [weak self] insets in + _ = self?.impl?.start(insets) + return self + } } - public func vCenter(to edge: VerticalEdge) -> PinLayoutObjC { - impl?.vCenter(to: edge) - return self + var end: POVoid { + return { [weak self] in + _ = self?.impl?.end() + return self + } } - public func bottom(to edge: VerticalEdge) -> PinLayoutObjC { - impl?.bottom(to: edge) - return self + var endValue: POValue { + return { [weak self] value in + _ = self?.impl?.end(value) + return self + } } - public func left(to edge: HorizontalEdge) -> PinLayoutObjC { - impl?.left(to: edge) - return self + var endPercent: POValue { + return { [weak self] percent in + _ = self?.impl?.end(percent%) + return self + } } - public func hCenter(to edge: HorizontalEdge) -> PinLayoutObjC { - impl?.hCenter(to: edge) - return self + var endInsets: POEdgeInsets { + return { [weak self] insets in + _ = self?.impl?.end(insets) + return self + } } - public func right(to edge: HorizontalEdge) -> PinLayoutObjC { - impl?.right(to: edge) - return self + var all: POVoid { + return { [weak self] in + _ = self?.impl?.all() + return self + } } - public func start(to edge: HorizontalEdge) -> PinLayoutObjC { - impl?.start(to: edge) - return self + var horizontally: POVoid { + return { [weak self] in + _ = self?.impl?.horizontally() + return self + } } - public func end(to edge: HorizontalEdge) -> PinLayoutObjC { - impl?.end(to: edge) - return self + var vertically: POVoid { + return { [weak self] in + _ = self?.impl?.vertically() + return self + } } - public func all() -> PinLayoutObjC { - impl?.all() - return self + var topToEdge: POVEdge { + return { [weak self] edge in + _ = self?.impl?.top(to: edge) + return self + } } - public func horizontally() -> PinLayoutObjC { - impl?.horizontally() - return self + var vCenterToEdge: POVEdge { + return { [weak self] edge in + _ = self?.impl?.vCenter(to: edge) + return self + } } - - public func horizontally(_ value: CGFloat) -> PinLayoutObjC { - impl?.horizontally(value) - return self + + var bottomToEdge: POVEdge { + return { [weak self] edge in + _ = self?.impl?.bottom(to: edge) + return self + } } - public func vertically() -> PinLayoutObjC { - impl?.vertically() - return self + var leftToEdge: POHEdge { + return { [weak self] edge in + _ = self?.impl?.left(to: edge) + return self + } } - - public func vertically(_ value: CGFloat) -> PinLayoutObjC { - impl?.vertically(value) - return self + + var hCenterToEdge: POHEdge { + return { [weak self] edge in + _ = self?.impl?.hCenter(to: edge) + return self + } } - public func topLeft(to anchor: Anchor) -> PinLayoutObjC { - impl?.topLeft(to: anchor) - return self + var rightToEdge: POHEdge { + return { [weak self] edge in + _ = self?.impl?.right(to: edge) + return self + } } - public func topLeft() -> PinLayoutObjC { - impl?.topLeft() - return self + var startToEdge: POHEdge { + return { [weak self] edge in + _ = self?.impl?.start(to: edge) + return self + } } - public func topStart(to anchor: Anchor) -> PinLayoutObjC { - impl?.topStart(to: anchor) - return self + var endToEdge: POHEdge { + return { [weak self] edge in + _ = self?.impl?.end(to: edge) + return self + } } - public func topStart() -> PinLayoutObjC { - impl?.topStart() - return self + var topLeftToAnchor: POAnchor { + return { [weak self] anchor in + _ = self?.impl?.topLeft(to: anchor) + return self + } } - public func topCenter(to anchor: Anchor) -> PinLayoutObjC { - impl?.topCenter(to: anchor) - return self + var topLeft: POVoid { + return { [weak self] in + _ = self?.impl?.topLeft() + return self + } } - public func topCenter() -> PinLayoutObjC { - impl?.topCenter() - return self + var topStartToAnchor: POAnchor { + return { [weak self] anchor in + _ = self?.impl?.topStart(to: anchor) + return self + } } - public func topRight(to anchor: Anchor) -> PinLayoutObjC { - impl?.topRight(to: anchor) - return self + var topStart: POVoid { + return { [weak self] in + _ = self?.impl?.topStart() + return self + } } - public func topRight() -> PinLayoutObjC { - impl?.topRight() - return self + var topCenterToAnchor: POAnchor { + return { [weak self] anchor in + _ = self?.impl?.topCenter(to: anchor) + return self + } } - public func topEnd(to anchor: Anchor) -> PinLayoutObjC { - impl?.topEnd(to: anchor) - return self + var topCenter: POVoid { + return { [weak self] in + _ = self?.impl?.topCenter() + return self + } } - public func topEnd() -> PinLayoutObjC { - impl?.topEnd() - return self + var topRightToAnchor: POAnchor { + return { [weak self] anchor in + _ = self?.impl?.topRight(to: anchor) + return self + } } - public func centerLeft(to anchor: Anchor) -> PinLayoutObjC { - impl?.centerLeft(to: anchor) - return self + var topRight: POVoid { + return { [weak self] in + _ = self?.impl?.topRight() + return self + } } - public func centerLeft() -> PinLayoutObjC { - impl?.centerLeft() - return self + var topEndToAnchor: POAnchor { + return { [weak self] anchor in + _ = self?.impl?.topEnd(to: anchor) + return self + } } - public func centerStart(to anchor: Anchor) -> PinLayoutObjC { - impl?.centerStart(to: anchor) - return self + var topEnd: POVoid { + return { [weak self] in + _ = self?.impl?.topEnd() + return self + } } - public func centerStart() -> PinLayoutObjC { - impl?.centerStart() - return self + var centerLeftToAnchor: POAnchor { + return { [weak self] anchor in + _ = self?.impl?.centerLeft(to: anchor) + return self + } } - public func center(to anchor: Anchor) -> PinLayoutObjC { - impl?.center(to: anchor) - return self + var centerLeft: POVoid { + return { [weak self] in + _ = self?.impl?.centerLeft() + return self + } } - public func center() -> PinLayoutObjC { - impl?.center() - return self + var centerStartToAnchor: POAnchor { + return { [weak self] anchor in + _ = self?.impl?.centerStart(to: anchor) + return self + } } - public func centerRight(to anchor: Anchor) -> PinLayoutObjC { - impl?.centerRight(to: anchor) - return self + var centerStart: POVoid { + return { [weak self] in + _ = self?.impl?.centerStart() + return self + } } - public func centerRight() -> PinLayoutObjC { - impl?.centerRight() - return self + var centerToAnchor: POAnchor { + return { [weak self] anchor in + _ = self?.impl?.center(to: anchor) + return self + } } - public func centerEnd(to anchor: Anchor) -> PinLayoutObjC { - impl?.centerEnd(to: anchor) - return self + var center: POVoid { + return { [weak self] in + _ = self?.impl?.center() + return self + } } - public func centerEnd() -> PinLayoutObjC { - impl?.centerEnd() - return self + var centerRightToAnchor: POAnchor { + return { [weak self] anchor in + _ = self?.impl?.centerRight(to: anchor) + return self + } } - public func bottomLeft(to anchor: Anchor) -> PinLayoutObjC { - impl?.bottomLeft(to: anchor) - return self + var centerRight: POVoid { + return { [weak self] in + _ = self?.impl?.centerRight() + return self + } } - public func bottomLeft() -> PinLayoutObjC { - impl?.bottomLeft() - return self + var centerEndToAnchor: POAnchor { + return { [weak self] anchor in + _ = self?.impl?.centerEnd(to: anchor) + return self + } } - public func bottomStart(to anchor: Anchor) -> PinLayoutObjC { - impl?.bottomStart(to: anchor) - return self + var centerEnd: POVoid { + return { [weak self] in + _ = self?.impl?.centerEnd() + return self + } } - public func bottomStart() -> PinLayoutObjC { - impl?.bottomStart() - return self + var bottomLeftToAnchor: POAnchor { + return { [weak self] anchor in + _ = self?.impl?.bottomLeft(to: anchor) + return self + } } - public func bottomCenter(to anchor: Anchor) -> PinLayoutObjC { - impl?.bottomCenter(to: anchor) - return self + var bottomLeft: POVoid { + return { [weak self] in + _ = self?.impl?.bottomLeft() + return self + } } - public func bottomCenter() -> PinLayoutObjC { - impl?.bottomCenter() - return self + var bottomStartToAnchor: POAnchor { + return { [weak self] anchor in + _ = self?.impl?.bottomStart(to: anchor) + return self + } } - public func bottomRight(to anchor: Anchor) -> PinLayoutObjC { - impl?.bottomRight(to: anchor) - return self + var bottomStart: POVoid { + return { [weak self] in + _ = self?.impl?.bottomStart() + return self + } } - public func bottomRight() -> PinLayoutObjC { - impl?.bottomRight() - return self + var bottomCenterToAnchor: POAnchor { + return { [weak self] anchor in + _ = self?.impl?.bottomCenter(to: anchor) + return self + } } - public func bottomEnd(to anchor: Anchor) -> PinLayoutObjC { - impl?.bottomEnd(to: anchor) - return self + var bottomCenter: POVoid { + return { [weak self] in + _ = self?.impl?.bottomCenter() + return self + } } - public func bottomEnd() -> PinLayoutObjC { - impl?.bottomEnd() - return self + var bottomRightToAnchor: POAnchor { + return { [weak self] anchor in + _ = self?.impl?.bottomRight(to: anchor) + return self + } } - public func above(of view: UIView) -> PinLayoutObjC { - impl?.above(of: view) - return self + var bottomRight: POVoid { + return { [weak self] in + _ = self?.impl?.bottomRight() + return self + } } - public func above(ofViews views: [UIView]) -> PinLayoutObjC { - impl?.above(of: views) - return self + var bottomEndToAnchor: POAnchor { + return { [weak self] anchor in + _ = self?.impl?.bottomEnd(to: anchor) + return self + } } - public func above(of view: UIView, aligned: HorizontalAlign) -> PinLayoutObjC { - impl?.above(of: view, aligned: aligned) - return self + var bottomEnd: POVoid { + return { [weak self] in + _ = self?.impl?.bottomEnd() + return self + } } - public func above(ofViews views: [UIView], aligned: HorizontalAlign) -> PinLayoutObjC { - impl?.above(of: views, aligned: aligned) - return self + var aboveOf: POView { + return { [weak self] view in + _ = self?.impl?.above(of: view) + return self + } } - public func below(of view: UIView) -> PinLayoutObjC { - impl?.below(of: view) - return self + var aboveOfViews: POViews { + return { [weak self] views in + _ = self?.impl?.above(of: views) + return self + } } - public func below(ofViews views: [UIView]) -> PinLayoutObjC { - impl?.below(of: views) - return self + var aboveOfAligned: POViewHAligned { + return { [weak self] view, aligned in + _ = self?.impl?.above(of: view, aligned: aligned) + return self + } } - public func below(of view: UIView, aligned: HorizontalAlign) -> PinLayoutObjC { - impl?.below(of: view, aligned: aligned) - return self + var aboveOfViewsAligned: POViewsHAligned { + return { [weak self] views, aligned in + _ = self?.impl?.above(of: views, aligned: aligned) + return self + } } - public func below(ofViews views: [UIView], aligned: HorizontalAlign) -> PinLayoutObjC { - impl?.below(of: views, aligned: aligned) - return self + var belowOf: POView { + return { [weak self] view in + _ = self?.impl?.below(of: view) + return self + } } - public func left(of view: UIView) -> PinLayoutObjC { - impl?.left(of: view) - return self + var belowOfViews: POViews { + return { [weak self] views in + _ = self?.impl?.below(of: views) + return self + } } - public func left(ofViews views: [UIView]) -> PinLayoutObjC { - impl?.left(of: views) - return self + var belowOfAligned: POViewHAligned { + return { [weak self] view, aligned in + _ = self?.impl?.below(of: view, aligned: aligned) + return self + } } - public func left(of view: UIView, aligned: VerticalAlign) -> PinLayoutObjC { - impl?.left(of: view, aligned: aligned) - return self + var belowOfViewsAligned: POViewsHAligned { + return { [weak self] views, aligned in + _ = self?.impl?.below(of: views, aligned: aligned) + return self + } } - public func left(ofViews views: [UIView], aligned: VerticalAlign) -> PinLayoutObjC { - impl?.left(of: views, aligned: aligned) - return self + var leftOf: POView { + return { [weak self] view in + _ = self?.impl?.left(of: view) + return self + } } - public func right(of view: UIView) -> PinLayoutObjC { - impl?.right(of: view) - return self + var leftOfViews: POViews { + return { [weak self] views in + _ = self?.impl?.left(of: views) + return self + } } - public func right(ofViews views: [UIView]) -> PinLayoutObjC { - impl?.right(of: views) - return self + var leftOfAligned: POViewVAligned { + return { [weak self] view, aligned in + _ = self?.impl?.left(of: view, aligned: aligned) + return self + } } - public func right(of view: UIView, aligned: VerticalAlign) -> PinLayoutObjC { - impl?.right(of: view, aligned: aligned) - return self + var leftOfViewsAligned: POViewsVAligned { + return { [weak self] views, aligned in + _ = self?.impl?.left(of: views, aligned: aligned) + return self + } } - public func right(ofViews views: [UIView], aligned: VerticalAlign) -> PinLayoutObjC { - impl?.right(of: views, aligned: aligned) - return self + var rightOf: POView { + return { [weak self] view in + _ = self?.impl?.right(of: view) + return self + } } - public func before(of view: UIView) -> PinLayoutObjC { - impl?.before(of: view) - return self + var rightOfViews: POViews { + return { [weak self] views in + _ = self?.impl?.right(of: views) + return self + } } - public func before(ofViews views: [UIView]) -> PinLayoutObjC { - impl?.before(of: views) - return self + var rightOfAligned: POViewVAligned { + return { [weak self] view, aligned in + _ = self?.impl?.right(of: view, aligned: aligned) + return self + } } - public func before(of view: UIView, aligned: VerticalAlign) -> PinLayoutObjC { - impl?.before(of: view, aligned: aligned) - return self + var rightOfViewsAligned: POViewsVAligned { + return { [weak self] views, aligned in + _ = self?.impl?.right(of: views, aligned: aligned) + return self + } } - public func before(ofViews views: [UIView], aligned: VerticalAlign) -> PinLayoutObjC { - impl?.before(of: views, aligned: aligned) - return self + var beforeOf: POView { + return { [weak self] view in + _ = self?.impl?.before(of: view) + return self + } } - public func after(of view: UIView) -> PinLayoutObjC { - impl?.after(of: view) - return self + var beforeOfViews: POViews { + return { [weak self] views in + _ = self?.impl?.before(of: views) + return self + } } - public func after(ofViews views: [UIView]) -> PinLayoutObjC { - impl?.after(of: views) - return self + var beforeOfAligned: POViewVAligned { + return { [weak self] view, aligned in + _ = self?.impl?.before(of: view, aligned: aligned) + return self + } } - public func after(of view: UIView, aligned: VerticalAlign) -> PinLayoutObjC { - impl?.after(of: view, aligned: aligned) - return self + var beforeOfViewsAligned: POViewsVAligned { + return { [weak self] views, aligned in + _ = self?.impl?.before(of: views, aligned: aligned) + return self + } } - public func after(ofViews views: [UIView], aligned: VerticalAlign) -> PinLayoutObjC { - impl?.after(of: views, aligned: aligned) - return self + var afterOf: POView { + return { [weak self] view in + _ = self?.impl?.after(of: view) + return self + } } - public func justify(_ align: HorizontalAlign) -> PinLayoutObjC { - impl?.justify(align) - return self + var afterOfViews: POViews { + return { [weak self] views in + _ = self?.impl?.after(of: views) + return self + } } - public func align(_ align: VerticalAlign) -> PinLayoutObjC { - impl?.align(align) - return self + var afterOfAligned: POViewVAligned { + return { [weak self] view, aligned in + _ = self?.impl?.after(of: view, aligned: aligned) + return self + } } - public func width(_ width: CGFloat) -> PinLayoutObjC { - impl?.width(width) - return self + var afterOfViewsAligned: POViewsVAligned { + return { [weak self] views, aligned in + _ = self?.impl?.after(of: views, aligned: aligned) + return self + } } - public func width(percent: CGFloat) -> PinLayoutObjC { - impl?.width(percent%) - return self + var justify: POHAlign { + return { [weak self] align in + _ = self?.impl?.justify(align) + return self + } } - public func width(of view: UIView) -> PinLayoutObjC { - impl?.width(of: view) - return self + var align: POVAlign { + return { [weak self] align in + _ = self?.impl?.align(align) + return self + } } - public func minWidth(_ width: CGFloat) -> PinLayoutObjC { - impl?.minWidth(width) - return self + var width: POValue { + return { [weak self] value in + _ = self?.impl?.width(value) + return self + } } - public func minWidth(percent: CGFloat) -> PinLayoutObjC { - impl?.minWidth(percent%) - return self + var widthPercent: POValue { + return { [weak self] percent in + _ = self?.impl?.width(percent%) + return self + } } - public func maxWidth(_ width: CGFloat) -> PinLayoutObjC { - impl?.maxWidth(width) - return self + var widthOf: POView { + return { [weak self] view in + _ = self?.impl?.width(of: view) + return self + } } - public func maxWidth(percent: CGFloat) -> PinLayoutObjC { - impl?.maxWidth(percent%) - return self + var minWidth: POValue { + return { [weak self] value in + _ = self?.impl?.minWidth(value) + return self + } } - public func height(_ height: CGFloat) -> PinLayoutObjC { - impl?.height(height) - return self + var minWidthPercent: POValue { + return { [weak self] percent in + _ = self?.impl?.minWidth(percent%) + return self + } } - public func height(percent: CGFloat) -> PinLayoutObjC { - impl?.height(percent%) - return self + var maxWidth: POValue { + return { [weak self] value in + _ = self?.impl?.maxWidth(value) + return self + } } - public func height(of view: UIView) -> PinLayoutObjC { - impl?.height(of: view) - return self + var maxWidthPercent: POValue { + return { [weak self] percent in + _ = self?.impl?.maxWidth(percent%) + return self + } } - public func minHeight(_ height: CGFloat) -> PinLayoutObjC { - impl?.minHeight(height) - return self + var height: POValue { + return { [weak self] value in + _ = self?.impl?.height(value) + return self + } } - public func minHeight(percent: CGFloat) -> PinLayoutObjC { - impl?.minHeight(percent%) - return self + var heightPercent: POValue { + return { [weak self] percent in + _ = self?.impl?.height(percent%) + return self + } } - public func maxHeight(_ height: CGFloat) -> PinLayoutObjC { - impl?.maxHeight(height) - return self + var heightOf: POView { + return { [weak self] view in + _ = self?.impl?.height(of: view) + return self + } } - public func maxHeight(percent: CGFloat) -> PinLayoutObjC { - impl?.maxHeight(percent%) - return self + var minHeight: POValue { + return { [weak self] value in + _ = self?.impl?.minHeight(value) + return self + } } - public func size(_ size: CGSize) -> PinLayoutObjC { - impl?.size(size) - return self + var minHeightPercent: POValue { + return { [weak self] percent in + _ = self?.impl?.minHeight(percent%) + return self + } } - public func size(length: CGFloat) -> PinLayoutObjC { - impl?.size(length) - return self + var maxHeight: POValue { + return { [weak self] value in + _ = self?.impl?.maxHeight(value) + return self + } } - public func size(percent: CGFloat) -> PinLayoutObjC { - impl?.size(percent%) - return self + var maxHeightPercent: POValue { + return { [weak self] percent in + _ = self?.impl?.maxHeight(percent%) + return self + } } - public func size(of view: UIView) -> PinLayoutObjC { - impl?.size(of: view) - return self + var size: POSize { + return { [weak self] size in + _ = self?.impl?.size(size) + return self + } } - public func aspectRatio(_ ratio: CGFloat) -> PinLayoutObjC { - impl?.aspectRatio(ratio) - return self + var sizeLength: POValue { + return { [weak self] value in + _ = self?.impl?.size(value) + return self + } } - public func aspectRatio(of view: UIView) -> PinLayoutObjC { - impl?.aspectRatio(of: view) - return self + var sizePercent: POValue { + return { [weak self] percent in + _ = self?.impl?.size(percent%) + return self + } } - public func aspectRatio() -> PinLayoutObjC { - impl?.aspectRatio() - return self + var sizeOf: POView { + return { [weak self] view in + _ = self?.impl?.size(of: view) + return self + } } - public func fitSize() -> PinLayoutObjC { - impl?.fitSize() - return self + var wrapContent: POVoid { + return { [weak self] in + _ = self?.impl?.wrapContent() + return self + } } - public func sizeToFit(_ fitType: Fit) -> PinLayoutObjC { - let type: FitType - switch fitType { - case .width: type = .width - case .height: type = .height - case .widthFlexible: type = .widthFlexible - case .heightFlexible: type = .heightFlexible + var wrapContentPadding: POValue { + return { [weak self] value in + _ = self?.impl?.wrapContent(padding: value) + return self } - impl?.sizeToFit(type) - return self } - public func marginTop(_ value: CGFloat) -> PinLayoutObjC { - impl?.marginTop(value) - return self + var wrapContentInsets: POEdgeInsets { + return { [weak self] insets in + _ = self?.impl?.wrapContent(padding: insets) + return self + } } - public func marginLeft(_ value: CGFloat) -> PinLayoutObjC { - impl?.marginLeft(value) - return self + var wrapContentType: POWrapType { + return { [weak self] type in + _ = self?.impl?.wrapContent(type) + return self + } } - public func marginBottom(_ value: CGFloat) -> PinLayoutObjC { - impl?.marginBottom(value) - return self + var wrapContentTypePadding: POWrapTypePadding { + return { [weak self] type, value in + _ = self?.impl?.wrapContent(type, padding: value) + return self + } } - public func marginRight(_ value: CGFloat) -> PinLayoutObjC { - impl?.marginRight(value) - return self + var wrapContentTypeInsets: POWrapTypeInsets { + return { [weak self] type, insets in + _ = self?.impl?.wrapContent(type, padding: insets) + return self + } } - public func marginStart(_ value: CGFloat) -> PinLayoutObjC { - impl?.marginStart(value) - return self + var aspectRatioValue: POValue { + return { [weak self] value in + _ = self?.impl?.aspectRatio(value) + return self + } } - public func marginEnd(_ value: CGFloat) -> PinLayoutObjC { - impl?.marginEnd(value) - return self + var aspectRatioOf: POView { + return { [weak self] view in + _ = self?.impl?.aspectRatio(of: view) + return self + } } - public func marginHorizontal(_ value: CGFloat) -> PinLayoutObjC { - impl?.marginHorizontal(value) - return self + #if os(iOS) || os(tvOS) + var aspectRatio: POVoid { + return { [weak self] in + _ = self?.impl?.aspectRatio() + return self + } } + #endif - public func marginVertical(_ value: CGFloat) -> PinLayoutObjC { - impl?.marginVertical(value) - return self + var sizeToFit: POVoid { + return { [weak self] in + _ = self?.impl?.sizeToFit() + return self + } } - public func margin(insets: UIEdgeInsets) -> PinLayoutObjC { - impl?.margin(insets) - return self + var sizeToFitType: POFitType { + return { [weak self] fitType in + let type: FitType + switch fitType { + case .width: type = .width + case .height: type = .height + case .widthFlexible: type = .widthFlexible + case .heightFlexible: type = .heightFlexible + case .content: type = .content + } + _ = self?.impl?.sizeToFit(type) + return self + } } - public func margin(_ value: CGFloat) -> PinLayoutObjC { - impl?.margin(value) - return self + var marginTop: POValue { + return { [weak self] value in + _ = self?.impl?.marginTop(value) + return self + } } - public func margin(vertical: CGFloat, horizontal: CGFloat) -> PinLayoutObjC { - impl?.margin(vertical, horizontal) - return self + var marginLeft: POValue { + return { [weak self] value in + _ = self?.impl?.marginLeft(value) + return self + } } - public func margin(top: CGFloat, horizontal: CGFloat, bottom: CGFloat) -> PinLayoutObjC { - impl?.margin(top, horizontal, bottom) - return self + var marginBottom: POValue { + return { [weak self] value in + _ = self?.impl?.marginBottom(value) + return self + } } - public func margin(top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat) -> PinLayoutObjC { - impl?.margin(top, left, bottom, right) - return self + var marginRight: POValue { + return { [weak self] value in + _ = self?.impl?.marginRight(value) + return self + } } - public func pinEdges() -> PinLayoutObjC { - impl?.pinEdges() - return self + var marginStart: POValue { + return { [weak self] value in + _ = self?.impl?.marginStart(value) + return self + } + } + + var marginEnd: POValue { + return { [weak self] value in + _ = self?.impl?.marginEnd(value) + return self + } + } + + var marginHorizontal: POValue { + return { [weak self] value in + _ = self?.impl?.marginHorizontal(value) + return self + } + } + + var marginVertical: POValue { + return { [weak self] value in + _ = self?.impl?.marginVertical(value) + return self + } + } + + var marginInsets: POEdgeInsets { + return { [weak self] insets in + _ = self?.impl?.margin(insets) + return self + } + } + + var margin: POValue { + return { [weak self] value in + _ = self?.impl?.margin(value) + return self + } + } + + var marginVH: (CGFloat, CGFloat) -> PinLayoutObjC? { + return { [weak self] vertical, horizontal in + _ = self?.impl?.margin(vertical, horizontal) + return self + } + } + + var marginTHB: (CGFloat, CGFloat, CGFloat) -> PinLayoutObjC? { + return { [weak self] top, horizontal, bottom in + _ = self?.impl?.margin(top, horizontal, bottom) + return self + } + } + + var marginTLBR: (CGFloat, CGFloat, CGFloat, CGFloat) -> PinLayoutObjC? { + return { [weak self] top, left, bottom, right in + _ = self?.impl?.margin(top, left, bottom, right) + return self + } + } + + var pinEdges: POVoid { + return { [weak self] in + _ = self?.impl?.pinEdges() + return self + } } } - -#endif diff --git a/Sources/Pin.swift b/Sources/Pin.swift index 30a33003..08842a02 100644 --- a/Sources/Pin.swift +++ b/Sources/Pin.swift @@ -19,24 +19,6 @@ import Foundation -@objc public enum LayoutDirection: Int { - case auto - case ltr - case rtl -} - -/// Control how PinLayout will calls `UIView.safeAreaInsetsDidChange` when the `UIView.pin.safeArea` change. -/// This support is usefull only on iOS 8/9/10. On iOS 11 `UIView.safeAreaInsetsDidChange` is supported -/// natively so this settings have no impact. -@objc public enum PinSafeAreaInsetsDidChangeMode: Int { - /// PinLayout won't call `UIView.safeAreaInsetsDidChange` on iOS 8/9/10. - case disable - /// PinLayout will call `UIView.safeAreaInsetsDidChange` only if the UIView implement the PinSafeAreaInsetsUpdate protocol. - case optIn - /// PinLayout will automatically calls `UIView.safeAreaInsetsDidChange` if the view has implemented this method. - case always -} - @objc public class Pin: NSObject { @objc public static var layoutDirection = LayoutDirection.ltr @@ -49,21 +31,7 @@ import Foundation } #endif - -#if DEBUG - @objc public static var logWarnings = true -#else - @objc public static var logWarnings = false -#endif - - /** - If your codes need to work in Xcode playgrounds, you may set to `true` the property - `Pin.logMissingLayoutCalls`, this way any missing call to `layout()` will generate - a warning in the Xcode console.. - */ - @objc public static var logMissingLayoutCalls = false - - static fileprivate var isInitialized = false + static private var isInitialized = false @objc public static func initPinLayout() { #if os(iOS) || os(tvOS) @@ -77,6 +45,42 @@ import Foundation self.layoutDirection = direction } + internal static var autoSizingInProgress: Bool = false + + // + // Warnings + // + #if DEBUG + @objc public static var logWarnings = true + #else + @objc public static var logWarnings = false + #endif + + @objc public static var activeWarnings = ActiveWarnings() + + /** + If your codes need to work in Xcode playgrounds, you may set to `true` the property + `Pin.logMissingLayoutCalls`, this way any missing call to `layout()` will generate + a warning in the Xcode console.. + */ + @objc public static var logMissingLayoutCalls = false + // Contains PinLayout last warning's text. Used by PinLayout's Unit Tests. @objc public static var lastWarningText: String? + + public static func resetWarnings() { + #if DEBUG + logWarnings = true + #endif + activeWarnings = ActiveWarnings() + } +} + +@objc public class ActiveWarnings: NSObject { + /// When set to true, a warning is displayed if there is no space available between views + /// specified in a call to `horizontallyBetween(...)` or `verticallyBetween(...)`. + public var noSpaceAvailableBetweenViews = true + + /// When set to true, a warning is displayed if 'aspectRatio()' is called on a UIImageView without a valid UIImage. + public var aspectRatioImageNotSet = true } diff --git a/Sources/PinLayout+Between.swift b/Sources/PinLayout+Between.swift new file mode 100644 index 00000000..c3031ed2 --- /dev/null +++ b/Sources/PinLayout+Between.swift @@ -0,0 +1,148 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(iOS) || os(tvOS) + import UIKit +#else + import AppKit +#endif + +extension PinLayout { + + // + // horizontallyBetween(...) + // + @discardableResult + public func horizontallyBetween(_ view1: PinView, and view2: PinView, aligned: VerticalAlign = .none) -> PinLayout { + func context() -> String { return betweenContext("horizontallyBetween", view1, view2, aligned) } + guard self.view != view1 && self.view != view2 else { + warnWontBeApplied("the view being layouted cannot be used as one of the references.", context) + return self + } + + guard view1 != view2 else { + warnWontBeApplied("reference views must be different.", context) + return self + } + + let anchors: [Anchor] + switch aligned { + case .top: anchors = [view1.anchor.topLeft, view1.anchor.topRight, view2.anchor.topLeft, view2.anchor.topRight] + case .center: anchors = [view1.anchor.centerLeft, view1.anchor.centerRight, view2.anchor.centerLeft, view2.anchor.centerRight] + case .bottom: anchors = [view1.anchor.bottomLeft, view1.anchor.bottomRight, view2.anchor.bottomLeft, view2.anchor.bottomRight] + case .none: anchors = [view1.anchor.topLeft, view1.anchor.topRight, view2.anchor.topLeft, view2.anchor.topRight] + } + + guard let coordinates = computeCoordinates(forAnchors: anchors, context), + coordinates.count == anchors.count else { return self } + + let view1MinX = coordinates[0].x + let view1MaxX = coordinates[1].x + let view2MinX = coordinates[2].x + let view2MaxX = coordinates[3].x + + if view1MaxX <= view2MinX { + setLeft(view1MaxX, context) + setRight(view2MinX, context) + applyVerticalAlignment(aligned, coordinates: [coordinates[0]], context: context) + } else if view2MaxX <= view1MinX { + setLeft(view2MaxX, context) + setRight(view1MinX, context) + applyVerticalAlignment(aligned, coordinates: [coordinates[1]], context: context) + } else { + guard Pin.logWarnings && Pin.activeWarnings.noSpaceAvailableBetweenViews else { return self } + warnWontBeApplied("there is no horizontal space between these views. (noSpaceAvailableBetweenViews)", context) + } + + return self + } + + // + // verticallyBetween(...) + // + @discardableResult + public func verticallyBetween(_ view1: PinView, and view2: PinView, aligned: HorizontalAlign = .none) -> PinLayout { + func context() -> String { return betweenContext("verticallyBetween", view1, view2, aligned) } + guard self.view != view1 && self.view != view2 else { + warnWontBeApplied("the view being layouted cannot be used as one of the references.", context) + return self + } + + guard view1 != view2 else { + warnWontBeApplied("reference views must be different.", context) + return self + } + + let anchors: [Anchor] + switch aligned { + case .left: anchors = [view1.anchor.topLeft, view1.anchor.bottomLeft, view2.anchor.topLeft, view2.anchor.bottomLeft] + case .center: anchors = [view1.anchor.topCenter, view1.anchor.bottomCenter, view2.anchor.topCenter, view2.anchor.bottomCenter] + case .right: anchors = [view1.anchor.topRight, view1.anchor.bottomRight, view2.anchor.topRight, view2.anchor.bottomRight] + case .start: anchors = isLTR() ? + [view1.anchor.topLeft, view1.anchor.bottomLeft, view2.anchor.topLeft, view2.anchor.bottomLeft] : + [view1.anchor.topRight, view1.anchor.bottomRight, view2.anchor.topRight, view2.anchor.bottomRight] + case .end: anchors = isLTR() ? + [view1.anchor.topRight, view1.anchor.bottomRight, view2.anchor.topRight, view2.anchor.bottomRight] : + [view1.anchor.topLeft, view1.anchor.bottomLeft, view2.anchor.topLeft, view2.anchor.bottomLeft] + case .none: anchors = [view1.anchor.topLeft, view1.anchor.bottomLeft, view2.anchor.topLeft, view2.anchor.bottomLeft] + } + + guard let coordinates = computeCoordinates(forAnchors: anchors, context), + coordinates.count == anchors.count else { return self } + + let view1MinY = coordinates[0].y + let view1MaxY = coordinates[1].y + let view2MinY = coordinates[2].y + let view2MaxY = coordinates[3].y + + if view1MaxY <= view2MinY { + setTop(view1MaxY, context) + setBottom(view2MinY, context) + applyHorizontalAlignment(aligned, coordinates: [coordinates[0]], context: context) + } else if view2MaxY <= view1MinY { + setTop(view2MaxY, context) + setBottom(view1MinY, context) + applyHorizontalAlignment(aligned, coordinates: [coordinates[1]], context: context) + } else { + guard Pin.logWarnings && Pin.activeWarnings.noSpaceAvailableBetweenViews else { return self } + warnWontBeApplied("there is no vertical space between these views. (noSpaceAvailableBetweenViews)", context) + } + + return self + } +} + +// MARK: private +extension PinLayout { + fileprivate func betweenContext(_ type: String, _ view1: PinView, _ view2: PinView, _ aligned: HorizontalAlign) -> String { + return betweenContext(type, view1, view2, aligned: aligned == HorizontalAlign.none ? nil : aligned.description) + } + + fileprivate func betweenContext(_ type: String, _ view1: PinView, _ view2: PinView, _ aligned: VerticalAlign) -> String { + return betweenContext(type, view1, view2, aligned: aligned == VerticalAlign.none ? nil : aligned.description) + } + + private func betweenContext(_ type: String, _ view1: PinView, _ view2: PinView, aligned: String?) -> String { + if let aligned = aligned { + return "\(type)(\(viewDescription(view1)), \(viewDescription(view1)), aligned: .\(aligned))" + } else { + return "\(type)(\(viewDescription(view1)), \(viewDescription(view1)))" + } + } +} diff --git a/Sources/PinLayout+Relative.swift b/Sources/PinLayout+Relative.swift new file mode 100644 index 00000000..c18df008 --- /dev/null +++ b/Sources/PinLayout+Relative.swift @@ -0,0 +1,319 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(iOS) || os(tvOS) + import UIKit +#else + import AppKit +#endif + +extension PinLayout { + + // + // above(of ...) + // + @discardableResult + public func above(of relativeView: PinView, aligned: HorizontalAlign = .none) -> PinLayout { + func context() -> String { return relativeContext("above", [relativeView], aligned) } + return above(of: [relativeView], aligned: aligned, context: context) + } + + @discardableResult + public func above(of relativeViews: [PinView], aligned: HorizontalAlign = .none) -> PinLayout { + func context() -> String { return relativeContext("above", relativeViews, aligned) } + return above(of: relativeViews, aligned: aligned, context: context) + } + + // + // below(of ...) + // + @discardableResult + public func below(of relativeView: PinView, aligned: HorizontalAlign = .none) -> PinLayout { + func context() -> String { return relativeContext("below", [relativeView], aligned) } + return below(of: [relativeView], aligned: aligned, context: context) + } + + @discardableResult + public func below(of relativeViews: [PinView], aligned: HorizontalAlign = .none) -> PinLayout { + func context() -> String { return relativeContext("below", relativeViews, aligned) } + return below(of: relativeViews, aligned: aligned, context: context) + } + + // + // left(of ...) + // + @discardableResult + public func left(of relativeView: PinView, aligned: VerticalAlign = .none) -> PinLayout { + func context() -> String { return relativeContext("left", [relativeView], aligned) } + return left(of: [relativeView], aligned: aligned, context: context) + } + + @discardableResult + public func left(of relativeViews: [PinView], aligned: VerticalAlign = .none) -> PinLayout { + func context() -> String { return relativeContext("left", relativeViews, aligned) } + return left(of: relativeViews, aligned: aligned, context: context) + } + + // + // right(of ...) + // + @discardableResult + public func right(of relativeView: PinView, aligned: VerticalAlign = .none) -> PinLayout { + func context() -> String { return relativeContext("right", [relativeView], aligned) } + return right(of: [relativeView], aligned: aligned, context: context) + } + + @discardableResult + public func right(of relativeViews: [PinView], aligned: VerticalAlign = .none) -> PinLayout { + func context() -> String { return relativeContext("right", relativeViews, aligned) } + return right(of: relativeViews, aligned: aligned, context: context) + } + + // + // before(of ...) + // + @discardableResult + public func before(of relativeView: PinView, aligned: VerticalAlign = .none) -> PinLayout { + func context() -> String { return relativeContext("before", [relativeView], aligned) } + if isLTR() { + return left(of: [relativeView], aligned: aligned, context: context) + } else { + return right(of: [relativeView], aligned: aligned, context: context) + } + } + + @discardableResult + public func before(of relativeViews: [PinView], aligned: VerticalAlign = .none) -> PinLayout { + func context() -> String { return relativeContext("before", relativeViews, aligned) } + if isLTR() { + return left(of: relativeViews, aligned: aligned, context: context) + } else { + return right(of: relativeViews, aligned: aligned, context: context) + } + } + + // + // after(of ...) + // + @discardableResult + public func after(of relativeView: PinView, aligned: VerticalAlign = .none) -> PinLayout { + func context() -> String { return relativeContext("after", [relativeView], aligned) } + if isLTR() { + return right(of: [relativeView], aligned: aligned, context: context) + } else { + return left(of: [relativeView], aligned: aligned, context: context) + } + } + + @discardableResult + public func after(of relativeViews: [PinView], aligned: VerticalAlign = .none) -> PinLayout { + func context() -> String { return relativeContext("after", relativeViews, aligned) } + if isLTR() { + return right(of: relativeViews, aligned: aligned, context: context) + } else { + return left(of: relativeViews, aligned: aligned, context: context) + } + } +} + +// MARK: private +extension PinLayout { + fileprivate func above(of relativeViews: [PinView], aligned: HorizontalAlign, context: Context) -> PinLayout { + guard let relativeViews = validateRelativeViews(relativeViews, context: context) else { return self } + + let anchors: [Anchor] + switch aligned { + case .left: anchors = relativeViews.map({ $0.anchor.topLeft }) + case .center: anchors = relativeViews.map({ $0.anchor.topCenter }) + case .right: anchors = relativeViews.map({ $0.anchor.topRight }) + case .start: anchors = isLTR() ? relativeViews.map({ $0.anchor.topLeft }) : relativeViews.map({ $0.anchor.topRight }) + case .end: anchors = isLTR() ? relativeViews.map({ $0.anchor.topRight }) : relativeViews.map({ $0.anchor.topLeft }) + case .none: anchors = relativeViews.map({ $0.anchor.topLeft }) + } + + if let coordinates = computeCoordinates(forAnchors: anchors, context) { + setBottom(getTopMostCoordinate(list: coordinates), context) + applyHorizontalAlignment(aligned, coordinates: coordinates, context: context) + } + return self + } + + fileprivate func below(of relativeViews: [PinView], aligned: HorizontalAlign, context: Context) -> PinLayout { + guard let relativeViews = validateRelativeViews(relativeViews, context: context) else { return self } + + let anchors: [Anchor] + switch aligned { + case .left: anchors = relativeViews.map({ $0.anchor.bottomLeft }) + case .center: anchors = relativeViews.map({ $0.anchor.bottomCenter }) + case .right: anchors = relativeViews.map({ $0.anchor.bottomRight }) + case .start: anchors = isLTR() ? relativeViews.map({ $0.anchor.bottomLeft }) : relativeViews.map({ $0.anchor.bottomRight }) + case .end: anchors = isLTR() ? relativeViews.map({ $0.anchor.bottomRight }) : relativeViews.map({ $0.anchor.bottomLeft }) + case .none: anchors = relativeViews.map({ $0.anchor.bottomLeft }) + } + + if let coordinates = computeCoordinates(forAnchors: anchors, context) { + setTop(getBottomMostCoordinate(list: coordinates), context) + applyHorizontalAlignment(aligned, coordinates: coordinates, context: context) + } + return self + } + + fileprivate func left(of relativeViews: [PinView], aligned: VerticalAlign, context: Context) -> PinLayout { + guard let relativeViews = validateRelativeViews(relativeViews, context: context) else { return self } + + let anchors: [Anchor] + switch aligned { + case .top: anchors = relativeViews.map({ $0.anchor.topLeft }) + case .center: anchors = relativeViews.map({ $0.anchor.centerLeft }) + case .bottom: anchors = relativeViews.map({ $0.anchor.bottomLeft }) + case .none: anchors = relativeViews.map({ $0.anchor.topLeft }) + } + + if let coordinates = computeCoordinates(forAnchors: anchors, context) { + setRight(getLeftMostCoordinate(list: coordinates), context) + applyVerticalAlignment(aligned, coordinates: coordinates, context: context) + } + return self + } + + fileprivate func right(of relativeViews: [PinView], aligned: VerticalAlign, context: Context) -> PinLayout { + guard let relativeViews = validateRelativeViews(relativeViews, context: context) else { return self } + + let anchors: [Anchor] + switch aligned { + case .top: anchors = relativeViews.map({ $0.anchor.topRight }) + case .center: anchors = relativeViews.map({ $0.anchor.centerRight }) + case .bottom: anchors = relativeViews.map({ $0.anchor.bottomRight }) + case .none: anchors = relativeViews.map({ $0.anchor.topRight }) + } + + if let coordinates = computeCoordinates(forAnchors: anchors, context) { + setLeft(getRightMostCoordinate(list: coordinates), context) + applyVerticalAlignment(aligned, coordinates: coordinates, context: context) + } + return self + } + + internal func applyHorizontalAlignment(_ aligned: HorizontalAlign?, coordinates: [CGPoint], context: Context) { + guard let aligned = aligned, aligned != .none else { return } + switch aligned { + case .left: setLeft(getLeftMostCoordinate(list: coordinates), context) + case .center: setHorizontalCenter(getAverageHCenterCoordinate(list: coordinates), context) + case .right: setRight(getRightMostCoordinate(list: coordinates), context) + case .start: + isLTR() ? setLeft(getLeftMostCoordinate(list: coordinates), context) : + setRight(getRightMostCoordinate(list: coordinates), context) + case .end: + isLTR() ? setRight(getRightMostCoordinate(list: coordinates), context) : + setLeft(getLeftMostCoordinate(list: coordinates), context) + case .none: break + } + } + + internal func applyVerticalAlignment(_ aligned: VerticalAlign?, coordinates: [CGPoint], context: Context) { + guard let aligned = aligned, aligned != .none else { return } + switch aligned { + case .top: setTop(getTopMostCoordinate(list: coordinates), context) + case .center: setVerticalCenter(getAverageVCenterCoordinate(list: coordinates), context) + case .bottom: setBottom(getBottomMostCoordinate(list: coordinates), context) + case .none: break + } + } + + private func getTopMostCoordinate(list: [CGPoint]) -> CGFloat { + assert(list.count > 0) + let firstCoordinate = list[0].y + return list.dropFirst().reduce(firstCoordinate, { (bestCoordinate, otherCoordinates) -> CGFloat in + return (otherCoordinates.y < bestCoordinate) ? otherCoordinates.y : bestCoordinate + }) + } + + private func getBottomMostCoordinate(list: [CGPoint]) -> CGFloat { + assert(list.count > 0) + let firstCoordinate = list[0].y + return list.dropFirst().reduce(firstCoordinate, { (bestCoordinate, otherCoordinates) -> CGFloat in + return (otherCoordinates.y > bestCoordinate) ? otherCoordinates.y : bestCoordinate + }) + } + + private func getLeftMostCoordinate(list: [CGPoint]) -> CGFloat { + assert(list.count > 0) + let firstCoordinate = list[0].x + return list.dropFirst().reduce(firstCoordinate, { (bestCoordinate, otherCoordinates) -> CGFloat in + return (otherCoordinates.x < bestCoordinate) ? otherCoordinates.x : bestCoordinate + }) + } + + private func getRightMostCoordinate(list: [CGPoint]) -> CGFloat { + assert(list.count > 0) + let firstCoordinate = list[0].x + return list.dropFirst().reduce(firstCoordinate, { (bestCoordinate, otherCoordinates) -> CGFloat in + return (otherCoordinates.x > bestCoordinate) ? otherCoordinates.x : bestCoordinate + }) + } + + private func getAverageHCenterCoordinate(list: [CGPoint]) -> CGFloat { + assert(list.count > 0) + let sum = list.reduce(0, { (result, point) -> CGFloat in + return result + point.x + }) + return sum / CGFloat(list.count) + } + + private func getAverageVCenterCoordinate(list: [CGPoint]) -> CGFloat { + assert(list.count > 0) + let sum = list.reduce(0, { (result, point) -> CGFloat in + return result + point.y + }) + return sum / CGFloat(list.count) + } + + private func validateRelativeViews(_ relativeViews: [PinView], context: Context) -> [PinView]? { + guard layoutSuperview(context) != nil else { return nil } + guard relativeViews.count > 0 else { + warnWontBeApplied("At least one view must be visible (i.e. UIView.isHidden != true) ", context) + return nil + } + + return relativeViews + } + + fileprivate func relativeContext(_ type: String, _ relativeViews: [PinView], _ aligned: HorizontalAlign) -> String { + return relativeContext(type, relativeViews, aligned: aligned == HorizontalAlign.none ? nil : aligned.description) + } + + fileprivate func relativeContext(_ type: String, _ relativeViews: [PinView], _ aligned: VerticalAlign) -> String { + return relativeContext(type, relativeViews, aligned: aligned == VerticalAlign.none ? nil : aligned.description) + } + + fileprivate func relativeContext(_ type: String, _ relativeViews: [PinView], aligned: String?) -> String { + let relativeViewsText: String + if relativeViews.count == 1 { + relativeViewsText = viewDescription(relativeViews[0]) + } else { + relativeViewsText = "[\(relativeViews.map({ viewDescription($0) }).joined(separator: ", "))]" + } + + if let aligned = aligned { + return "\(type)(of: \(relativeViewsText), aligned: .\(aligned))" + } else { + return "\(type)(of: \(relativeViewsText))" + } + } +} diff --git a/Sources/PinLayout+Size.swift b/Sources/PinLayout+Size.swift new file mode 100644 index 00000000..b308c37b --- /dev/null +++ b/Sources/PinLayout+Size.swift @@ -0,0 +1,266 @@ +// Copyright (c) 2018 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(iOS) || os(tvOS) +import UIKit +#else +import AppKit +#endif + +enum AdjustSizeType { + case fitTypeWidth + case fitTypeHeight + case fitTypeWidthFlexible + case fitTypeHeightFlexible + case fitTypeContent + + case aspectRatio(CGFloat) + + var isFlexible: Bool { + if case .fitTypeWidthFlexible = self { + return true + } else if case .fitTypeHeightFlexible = self { + return true + } else { + return false + } + } + + internal var requiresSizeCalculable: Bool { + switch self { + case .fitTypeWidth, .fitTypeHeight, + .fitTypeWidthFlexible, .fitTypeHeightFlexible, + .fitTypeContent: + return true + case .aspectRatio(_): + return false + } + } + + func toFitType() -> FitType? { + switch self { + case .fitTypeWidth: return .width + case .fitTypeHeight: return .height + case .fitTypeWidthFlexible: return .widthFlexible + case .fitTypeHeightFlexible: return .heightFlexible + default: return nil + } + } +} + +extension FitType { + func toAdjustSizeType() -> AdjustSizeType { + switch self { + case .width: return .fitTypeWidth + case .height: return .fitTypeHeight + case .widthFlexible: return .fitTypeWidthFlexible + case .heightFlexible: return .fitTypeHeightFlexible + case .content: return .fitTypeContent + } + } +} + +extension PinLayout { + @discardableResult + public func size(_ size: CGSize) -> PinLayout { + return setSize(size, { return "size(CGSize(width: \(size.width), height: \(size.height)))" }) + } + + @discardableResult + public func size(_ sideLength: CGFloat) -> PinLayout { + return setSize(CGSize(width: sideLength, height: sideLength), { return "size(sideLength: \(sideLength))" }) + } + + @discardableResult + public func size(_ percent: Percent) -> PinLayout { + func context() -> String { return "size(\(percent.description))" } + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + let size = CGSize(width: percent.of(layoutSuperviewRect.width), height: percent.of(layoutSuperviewRect.height)) + return setSize(size, context) + } + + @discardableResult + public func size(of view: PinView) -> PinLayout { + func context() -> String { return "size(of \(viewDescription(view)))" } + return setSize(view.getRect(keepTransform: keepTransform).size, context) + } + + /** + Set the view aspect ratio. + + AspectRatio is applied only if a single dimension (either width or height) can be determined, + in that case the aspect ratio will be used to compute the other dimension. + + * AspectRatio is defined as the ratio between the width and the height (width / height). + * An aspect ratio of 2 means the width is twice the size of the height. + * AspectRatio respects the min (minWidth/minHeight) and the max (maxWidth/maxHeight) + dimensions of an item. + */ + @discardableResult + public func aspectRatio(_ ratio: CGFloat) -> PinLayout { + return setAdjustSizeType(.aspectRatio(ratio), { "aspectRatio(\(ratio))" }) + } + + /** + Set the view aspect ratio using another UIView's aspect ratio. + + AspectRatio is applied only if a single dimension (either width or height) can be determined, + in that case the aspect ratio will be used to compute the other dimension. + + * AspectRatio is defined as the ratio between the width and the height (width / height). + * AspectRatio respects the min (minWidth/minHeight) and the max (maxWidth/maxHeight) + dimensions of an item. + */ + @discardableResult + public func aspectRatio(of view: PinView) -> PinLayout { + let rect = view.getRect(keepTransform: keepTransform) + return setAdjustSizeType(.aspectRatio(rect.width / rect.height), { "aspectRatio(of: \(viewDescription(view)))" }) + } + + /** + If the layouted view is an UIImageView, this method will set the aspectRatio using + the UIImageView's image dimension. + + For other types of views, this method as no impact. + */ + #if os(iOS) || os(tvOS) + @discardableResult + public func aspectRatio() -> PinLayout { + func context() -> String { return "aspectRatio()" } + guard let imageView = view as? UIImageView else { + warnWontBeApplied("the layouted view must be an UIImageView() to use the aspectRatio() method without parameter.", context) + return self + } + + guard let imageSize = imageView.image?.size else { + guard Pin.logWarnings && Pin.activeWarnings.aspectRatioImageNotSet else { return self } + warnWontBeApplied("the layouted UIImageView's image hasn't been set (aspectRatioImageNotSet)", context) + return self + } + + return setAdjustSizeType(.aspectRatio(imageSize.width / imageSize.height), context) + } + #endif + + /** + The method adjust the view's size based on the view's `sizeThatFits()` method result. + PinLayout will adjust either the view's width or height based on the `fitType` parameter value. + + Notes: + * If margin rules apply, margins will be applied when determining the reference dimension (width/height). + * The resulting size will always respect `minWidth` / `maxWidth` / `minHeight` / `maxHeight`. + + - Parameter fitType: Identify the reference dimension (width / height) that will be used + to adjust the view's size: + + .width: The method adjust the view's size based on the **reference width**. + * If properties related to the width have been pinned (e.g: width, left & right, margins, ...), + the **reference width will be determined by these properties**, if not the **current view's width** + will be used. + * The resulting width will always **match the reference width**. + + .height: The method adjust the view's size based on the **reference height**. + * If properties related to the height have been pinned (e.g: height, top & bottom, margins, ...), + the **reference height will be determined by these properties**, if not the **current view's height** + will be used. + * The resulting height will always **match the reference height**. + + .widthFlexible: Similar to `.width`, except that PinLayout won't constrain the resulting width to + match the reference width. The resulting width may be smaller or bigger depending on the view's + sizeThatFits(..) method result. For example a single line UILabel may returns a smaller width if its + string is smaller than the reference width. + + .heightFlexible: Similar to `.height`, except that PinLayout won't constrain the resulting height to + match the reference height. The resulting height may be smaller or bigger depending on the view's + sizeThatFits(..) method result. + + Examples: + + ``` + // Adjust the view's size based on a width of 100 pixels. + // The resulting width will always match the pinned property `width(100)`. + view.pin.width(100).sizeToFit(.width) + + // Adjust the view's size based on view's current width. + // The resulting width will always match the view's original width. + // The resulting height will never be bigger than the specified `maxHeight`. + view.pin.sizeToFit(.width).maxHeight(100) + + // Adjust the view's size based on 100% of the superview's height. + // The resulting height will always match the pinned property `height(100%)`. + view.pin.height(100%).sizeToFit(.height) + + // Adjust the view's size based on view's current height. + // The resulting width will always match the view's original height. + view.pin.sizeToFit(.height) + + // Since `.widthFlexible` has been specified, its possible that the resulting + // width will be smaller or bigger than 100 pixels, based of the label's sizeThatFits() + // method result. + label.pin.width(100).sizeToFit(.widthFlexible) + ``` + */ + @discardableResult + public func sizeToFit(_ fitType: FitType = .content) -> PinLayout { + return setAdjustSizeType(fitType.toAdjustSizeType(), { return "sizeToFit(\(fitType.description))" }) + }} + +// +// MARK: Private methods +extension PinLayout { + internal func setSize(_ size: CGSize, _ context: Context) -> PinLayout { + setWidth(size.width, { return "\(context())'s width" }) + setHeight(size.height, { return "\(context())'s height" }) + return self + } + + internal func setAdjustSizeType(_ type: AdjustSizeType?, _ context: Context) -> PinLayout { + guard adjustSizeType == nil else { + warnAdjustSizeConflict(context: context) + return self + } + + if let type = type { + if case let AdjustSizeType.aspectRatio(ratio) = type, ratio <= 0 { + warnWontBeApplied("the aspectRatio (\(ratio)) must be greater than zero.", context) + return self + } else if type.requiresSizeCalculable, !(view is SizeCalculable) { + warnWontBeApplied("PinLayout cannot comptute this view's size. This type of views doesn't conform to the protocol SizeCalculable.", context) + return self + } + } + + adjustSizeType = type + + return self + } + + private func warnAdjustSizeConflict(context: Context) { + guard let adjustSizeType = adjustSizeType else { return } + let conflict: String + switch adjustSizeType { + case .fitTypeWidth, .fitTypeHeight, .fitTypeWidthFlexible, .fitTypeHeightFlexible, .fitTypeContent: + conflict = "sizeToFit(\(adjustSizeType.toFitType()!.description))." + case .aspectRatio(let ratio): + conflict = "aspectRatio(\(ratio))" + } + + warn("PinLayout Conflict: \(context()) won't be applied since it conflicts with \(conflict).") + } +} diff --git a/Sources/PinLayout+WrapContent.swift b/Sources/PinLayout+WrapContent.swift new file mode 100644 index 00000000..e58731cd --- /dev/null +++ b/Sources/PinLayout+WrapContent.swift @@ -0,0 +1,143 @@ +// Copyright (c) 2018 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(iOS) || os(tvOS) +import UIKit +#else +import AppKit +#endif + +extension PinLayout { + /** + Adjust the view's width & height to wrap all its subviews. The method also adjust subviews position to create a tight wrap. + */ + @discardableResult + public func wrapContent() -> PinLayout { + return wrapContent(.all, padding: PEdgeInsets(top: 0, left: 0, bottom: 0, right: 0), { return "wrapContent()" }) + } + + /** + Adjust the view's width & height to wrap all its subviews. The method also adds a padding around all subviews. + + - Parameters: + - padding: Specify a padding value. + */ + @discardableResult + public func wrapContent(padding: CGFloat) -> PinLayout { + return wrapContent(.all, padding: PEdgeInsets(top: padding, left: padding, bottom: padding, right: padding), { return "wrapContent(padding: \(padding)" }) + } + + /** + The method... + - Adjust the view's width and height to wrap all its subviews. + - Adjust subviews's position to create a tight wrap. + - Apply padding around all subviews. + + - Parameters: + - padding: Specify a padding using an UIEdgeInsets. + */ + @discardableResult + public func wrapContent(padding: PEdgeInsets) -> PinLayout { + return wrapContent(.all, padding: padding, { return "wrapContent(padding: \(insetsDescription(padding))" }) + } + + /** + The method... + - Adjust the view's width and height to wrap all its subviews. + - Adjust subviews's position to create a tight wrap. + + - Parameters: + - type: Specify the wrap type (.all, .horizontally, .vertically) + */ + @discardableResult + public func wrapContent(_ type: WrapType) -> PinLayout { + return wrapContent(type, padding: PEdgeInsets(top: 0, left: 0, bottom: 0, right: 0), { return "wrapContent(\(type.description)" }) + } + + /** + The method... + - Adjust the view's width and height to wrap all its subviews. + - Adjust subviews's position to create a tight wrap. + - Apply padding around all subviews. + + - Parameters: + - type: Specify the wrap type (.all, .horizontally, .vertically) + - padding: Specify a padding value. + */ + @discardableResult + public func wrapContent(_ type: WrapType, padding: CGFloat) -> PinLayout { + return wrapContent(type, padding: PEdgeInsets(top: padding, left: padding, bottom: padding, right: padding), { return "wrapContent(\(type.description), padding: \(padding)" }) + } + + /** + The method... + - Adjust the view's width and height to wrap all its subviews. + - Adjust subviews's position to create a tight wrap. + - Apply padding around all subviews. + + - Parameters: + - type: Specify the wrap type (.all, .horizontally, .vertically) + - padding: Specify a padding using an UIEdgeInsets. + */ + @discardableResult + public func wrapContent(_ type: WrapType, padding: PEdgeInsets) -> PinLayout { + return wrapContent(type, padding: padding, { return "wrapContent(\(type.description), padding: \(insetsDescription(padding))" }) + } + + private func wrapContent(_ type: WrapType, padding: PEdgeInsets, _ context: Context) -> PinLayout { + let subviews = view.subviews + guard !subviews.isEmpty else { return self } + + let firstViewRect = subviews[0].getRect(keepTransform: keepTransform) + let boundingRect = subviews.reduce(firstViewRect, { (result, view) in + result.union(view.getRect(keepTransform: keepTransform)) + }) + + var offsetDx: CGFloat = 0 + var offsetDy: CGFloat = 0 + + if type == .all || type == .horizontally { + let contentWidth = boundingRect.width + padding.left + padding.right + if contentWidth >= 0 { + setWidth(contentWidth, context) + } + + offsetDx = -boundingRect.minX + padding.left + } + + if type == .all || type == .vertically { + let contentHeight = boundingRect.height + padding.top + padding.bottom + if contentHeight >= 0 { + setHeight(contentHeight, context) + } + + offsetDy = -boundingRect.minY + padding.top + } + + if offsetDx != 0 || offsetDy != 0 { + subviews.forEach { (view) in + let viewRect = view.getRect(keepTransform: keepTransform) + let newRect = viewRect.offsetBy(dx: offsetDx, dy: offsetDy) + view.setRect(newRect, keepTransform: keepTransform) + } + } + + return self + } +} diff --git a/Sources/PinLayout.swift b/Sources/PinLayout.swift index e456a11b..33d79e30 100644 --- a/Sources/PinLayout.swift +++ b/Sources/PinLayout.swift @@ -17,512 +17,962 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +import Foundation + #if os(iOS) || os(tvOS) -import UIKit + import UIKit + public typealias PEdgeInsets = UIEdgeInsets #else -import AppKit + import AppKit + public typealias PEdgeInsets = NSEdgeInsets #endif -/* - UIView's anchors point - ====================== - - topLeft topCenter topRight - o-------------o--------------o - | | - | | - | | - | | - | | - | center | - centerLeft o o o centerRight - | | - | | - | | - | | - | | - | | - o-------------o--------------o - bottomLeft bottomCenter bottomLeft - - */ - -/// UIViews's anchor definition -@objc public protocol Anchor { -} - -/// UIViews's list of anchors. -@objc public protocol AnchorList { - var topLeft: Anchor { get } - var topCenter: Anchor { get } - var topRight: Anchor { get } - var centerLeft: Anchor { get } - var center: Anchor { get } - var centerRight: Anchor { get } - var bottomLeft: Anchor { get } - var bottomCenter: Anchor { get } - var bottomRight: Anchor { get } +public class PinLayout { + internal let view: PinView + internal let keepTransform: Bool - // RTL support - var topStart: Anchor { get } - var topEnd: Anchor { get } - var centerStart: Anchor { get } - var centerEnd: Anchor { get } - var bottomStart: Anchor { get } - var bottomEnd: Anchor { get } -} - -/* - UIView's Edges - ====================== - top - +-----------------+ - | | - | | - | hCenter | - left | + | right - | vCenter | - | | - | | - +-----------------+ - bottom -*/ - -/// UIViews's list of edges -@objc public protocol EdgeList { - var top: VerticalEdge { get } - var vCenter: VerticalEdge { get } - var bottom: VerticalEdge { get } - var left: HorizontalEdge { get } - var hCenter: HorizontalEdge { get } - var right: HorizontalEdge { get } + internal var _top: CGFloat? // offset from superview's top edge + internal var _left: CGFloat? // offset from superview's left edge + internal var _bottom: CGFloat? // offset from superview's top edge + internal var _right: CGFloat? // offset from superview's left edge + + internal var _hCenter: CGFloat? + internal var _vCenter: CGFloat? + + internal var width: CGFloat? + internal var minWidth: CGFloat? + internal var maxWidth: CGFloat? + internal var height: CGFloat? + internal var minHeight: CGFloat? + internal var maxHeight: CGFloat? + + internal var adjustSizeType: AdjustSizeType? + + internal var shouldKeepViewDimension: Bool { + return adjustSizeType == nil + } + + internal var marginTop: CGFloat? + internal var marginLeft: CGFloat? + internal var marginBottom: CGFloat? + internal var marginRight: CGFloat? + internal var shouldPinEdges = false + + internal var justify: HorizontalAlign? + internal var align: VerticalAlign? + + internal var _marginTop: CGFloat { return marginTop ?? 0 } + internal var _marginLeft: CGFloat { return marginLeft ?? 0 } + internal var _marginBottom: CGFloat { return marginBottom ?? 0 } + internal var _marginRight: CGFloat { return marginRight ?? 0 } + + internal var isLayouted = false - // RTL support - var start: HorizontalEdge { get } - var end: HorizontalEdge { get } -} + init(view: PinView, keepTransform: Bool) { + self.view = view + self.keepTransform = keepTransform -/// PinLayout interface -public protocol PinLayout { + #if os(iOS) || os(tvOS) + Pin.initPinLayout() + #endif + } + + deinit { + if !isLayouted && Pin.logMissingLayoutCalls { + warn("PinLayout commands have been issued without calling the 'layout()' method to complete the layout. (These warnings can be disabled by setting Pin.logMissingLayoutCalls to false)") + } + apply() + } #if os(iOS) || os(tvOS) - /// Expose a compatible `safeAreaInsets` for all iOS releases (including iOS 7/8/9/10/...). Available only for >= iOS 9 - var safeArea: UIEdgeInsets { get } + public var safeArea: PEdgeInsets { + if let view = view as? UIView { + if #available(iOS 11.0, tvOS 11.0, *) { + return view.safeAreaInsets + } else { + return view.pinlayoutComputeSafeAreaInsets() + } + } else { + return .zero + } + } + + public var readableMargins: PEdgeInsets { + guard #available(iOS 9.0, *) else { return .zero } + guard let view = view as? UIView else { return .zero } + + let layoutFrame = view.readableContentGuide.layoutFrame + guard !layoutFrame.isEmpty else { return .zero } + + return UIEdgeInsets(top: layoutFrame.origin.y, left: layoutFrame.origin.x, + bottom: view.frame.height - layoutFrame.origin.y - layoutFrame.height, + right: view.frame.width - layoutFrame.origin.x - layoutFrame.width) + } + + public var layoutMargins: PEdgeInsets { + guard let view = view as? UIView else { return .zero } + return view.layoutMargins + } + #endif + + #if os(iOS) && compiler(>=5.5) // Xcode 13+ + public var keyboardArea: CGRect { + guard #available(iOS 15.0, *) else { return .zero } + guard let view = view as? UIView else { return .zero } + + return view.keyboardLayoutGuide.layoutFrame + } #endif // // MARK: Layout using distances from superview’s edges // - @discardableResult func top() -> PinLayout - @discardableResult func top(_ value: CGFloat) -> PinLayout - @discardableResult func top(_ percent: Percent) -> PinLayout - #if os(iOS) || os(tvOS) - @discardableResult func top(_ insets: UIEdgeInsets) -> PinLayout - #elseif os(macOS) - @discardableResult func top(_ insets: NSEdgeInsets) -> PinLayout - #endif + // top, left, bottom, right + // - @discardableResult func left() -> PinLayout - @discardableResult func left(_ value: CGFloat) -> PinLayout - @discardableResult func left(_ percent: Percent) -> PinLayout - #if os(iOS) || os(tvOS) - @discardableResult func left(_ insets: UIEdgeInsets) -> PinLayout - #elseif os(macOS) - @discardableResult func left(_ insets: NSEdgeInsets) -> PinLayout - #endif + /// Position the view's top edge. + /// + /// - Parameter offset: (Optional, default value is 0). Specifies a distance from the superview's + /// top edge in pixels. + @discardableResult + public func top(_ offset: CGFloat = 0) -> PinLayout { + return top(offset, { return "top(\(offset.optionnalDescription))" }) + } + + /// Position the top edge. + /// + /// - Parameter percent: Specifies the top edge distance from the superview's top edge in + /// percentage of its superview's height. + @discardableResult + public func top(_ percent: Percent) -> PinLayout { + func context() -> String { return "top(\(percent.description))" } + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + setTop(percent.of(layoutSuperviewRect.height), context) + return self + } + + /// Position the top edge. + /// The value + /// + /// - Parameter insets: specifies the top edge distance from the superview's top edge in pixels using + /// the `UIEdgeInsets.top` property. It is particularly useful with `UIView.pin.safeArea` or + /// `UIView.safeAreaInsets`. + @discardableResult + public func top(_ insets: PEdgeInsets) -> PinLayout { + return top(insets.top, { return "top(\(insetsDescription(insets))" }) + } + + /// Position the left edge. + /// + /// - Parameter offset: (Optional, default value is 0). Specifies a distance from the superview's + /// left edge in pixels + @discardableResult + public func left(_ offset: CGFloat = 0) -> PinLayout { + return left(offset, { return "left(\(offset.optionnalDescription))" }) + } + + /// Position the left edge. + /// + /// - Parameter offset: specifies the left edge distance from the superview's left edge in + /// percentage of its superview's width. + @discardableResult + public func left(_ offset: Percent) -> PinLayout { + return left(offset, { return "left(\(offset.description))" }) + } + + /// Position the left edge. + /// + /// - Parameter insets: specifies the left edge distance from the superview's left edge in pixels using + /// the `UIEdgeInsets.left` property. It is particularly useful with `UIView.pin.safeArea` or + /// `UIView.safeAreaInsets`. + @discardableResult + public func left(_ insets: PEdgeInsets) -> PinLayout { + return left(insets.left, { return "left(\(insetsDescription(insets))" }) + } + + /// Position the left edge In LTR direction. + /// Position the right edge In RTL direction. + /// + /// - Parameter offset: (Optional, default value is 0) In LTR direction the value specifies the left + /// edge distance from the superview's left edge in pixels. In RTL direction the + /// value specifies the right edge distance from the superview's right edge in pixels. + @discardableResult + public func start(_ offset: CGFloat = 0) -> PinLayout { + func context() -> String { return "start(\(offset.optionnalDescription))" } + return isLTR() ? left(offset, context) : right(offset, context) + } + + /// In LTR direction, position the left edge. + /// In RTL direction, position the right edge. + /// + /// - Parameter offset: (Optional, default value is 0) Specifies a distance from the superview's + /// corresponding edge in percentage of its superview's width. + @discardableResult + public func start(_ offset: Percent) -> PinLayout { + func context() -> String { return "start(\(offset.description))" } + return isLTR() ? left(offset, context) : right(offset, context) + } + + @discardableResult + public func start(_ insets: PEdgeInsets) -> PinLayout { + func context() -> String { return "start(\(insetsDescription(insets))" } + return isLTR() ? left(insets.left, context) : right(insets.right, context) + } + + @discardableResult + public func bottom(_ offset: CGFloat = 0) -> PinLayout { + return bottom(offset, { return "bottom(\(offset.optionnalDescription))" }) + } + + @discardableResult + public func bottom(_ offset: Percent) -> PinLayout { + return bottom(offset, { return "bottom(\(offset.description))" }) + } + + @discardableResult + public func bottom(_ insets: PEdgeInsets) -> PinLayout { + return bottom(insets.bottom, { return "bottom(\(insetsDescription(insets))" }) + } + + @discardableResult + public func right(_ offset: CGFloat = 0) -> PinLayout { + return right(offset, { return "right(\(offset.optionnalDescription))" }) + } + + @discardableResult + public func right(_ offset: Percent) -> PinLayout { + return right(offset, { return "right(\(offset.description))" }) + } + + @discardableResult + public func right(_ insets: PEdgeInsets) -> PinLayout { + return right(insets.right, { return "right(\(insetsDescription(insets))" }) + } + + @discardableResult + public func end(_ margin: CGFloat = 0) -> PinLayout { + func context() -> String { return "end(\(margin.optionnalDescription))" } + return isLTR() ? right(margin, context) : left(margin, context) + } + + @discardableResult + public func end(_ offset: Percent) -> PinLayout { + func context() -> String { return "end(\(offset.description))" } + return isLTR() ? right(offset, context) : left(offset, context) + } + + @discardableResult + public func end(_ insets: PEdgeInsets) -> PinLayout { + func context() -> String { return "end(\(insetsDescription(insets))" } + return isLTR() ? right(insets.right, context) : left(insets.left, context) + } + + @discardableResult + public func hCenter(_ margin: CGFloat = 0) -> PinLayout { + func context() -> String { return "hCenter(\(margin.optionnalDescription))" } + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + setHorizontalCenter((layoutSuperviewRect.width / 2) + margin, context) + return self + } + + @discardableResult + public func hCenter(_ offset: Percent) -> PinLayout { + func context() -> String { return "hCenter(\(offset.description))" } + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + setHorizontalCenter((layoutSuperviewRect.width / 2) + offset.of(layoutSuperviewRect.width), context) + return self + } + + @discardableResult + public func vCenter(_ margin: CGFloat = 0) -> PinLayout { + func context() -> String { return "vCenter(\(margin.optionnalDescription))" } + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + setVerticalCenter((layoutSuperviewRect.height / 2) + margin, context) + return self + } + + @discardableResult + public func vCenter(_ offset: Percent) -> PinLayout { + func context() -> String { return "vCenter(\(offset.description))" } + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + setVerticalCenter((layoutSuperviewRect.height / 2) + offset.of(layoutSuperviewRect.height), context) + return self + } - @discardableResult func bottom() -> PinLayout - @discardableResult func bottom(_ value: CGFloat) -> PinLayout - @discardableResult func bottom(_ percent: Percent) -> PinLayout - #if os(iOS) || os(tvOS) - @discardableResult func bottom(_ insets: UIEdgeInsets) -> PinLayout - #elseif os(macOS) - @discardableResult func bottom(_ insets: NSEdgeInsets) -> PinLayout - #endif + // Pin multiple edges at once. - @discardableResult func right() -> PinLayout - @discardableResult func right(_ value: CGFloat) -> PinLayout - @discardableResult func right(_ percent: Percent) -> PinLayout - #if os(iOS) || os(tvOS) - @discardableResult func right(_ insets: UIEdgeInsets) -> PinLayout - #elseif os(macOS) - @discardableResult func right(_ insets: NSEdgeInsets) -> PinLayout - #endif + /** + Pin all edges on its superview's corresponding edges (top, bottom, left, right). + The optionnal value specifies edges distance from the superview's corresponding edge in pixels. - @discardableResult func hCenter() -> PinLayout - @discardableResult func hCenter(_ value: CGFloat) -> PinLayout - @discardableResult func hCenter(_ percent: Percent) -> PinLayout + Similar to calling `view.top(margin).bottom(margin).left(margin).right(margin)` + */ + @discardableResult + public func all(_ margin: CGFloat = 0) -> PinLayout { + top(margin, { "all(\(margin.optionnalDescription)) top coordinate" }) + bottom(margin, { "all(\(margin.optionnalDescription)) bottom coordinate" }) + left(margin, { "all(\(margin.optionnalDescription)) left coordinate" }) + right(margin, { "all(\(margin.optionnalDescription)) right coordinate" }) + return self + } - @discardableResult func vCenter() -> PinLayout - @discardableResult func vCenter(_ value: CGFloat) -> PinLayout - @discardableResult func vCenter(_ percent: Percent) -> PinLayout + /** + Pin all edges on its superview's corresponding edges (top, bottom, left, right). - // RTL support - @discardableResult func start() -> PinLayout - @discardableResult func start(_ value: CGFloat) -> PinLayout - @discardableResult func start(_ percent: Percent) -> PinLayout - #if os(iOS) || os(tvOS) - @discardableResult func start(_ insets: UIEdgeInsets) -> PinLayout - #elseif os(macOS) - @discardableResult func start(_ insets: NSEdgeInsets) -> PinLayout - #endif + Similar to calling `view.top(insets).bottom(insets).left(insets).right(insets)` + */ + @discardableResult + public func all(_ insets: PEdgeInsets) -> PinLayout { + top(insets.top, { "all(\(insets)) top coordinate" }) + bottom(insets.bottom, { "all(\(insets)) bottom coordinate" }) + left(insets.left, { "all(\(insets)) left coordinate" }) + right(insets.right, { "all(\(insets)) right coordinate" }) + return self + } - @discardableResult func end() -> PinLayout - @discardableResult func end(_ value: CGFloat) -> PinLayout - @discardableResult func end(_ percent: Percent) -> PinLayout - #if os(iOS) || os(tvOS) - @discardableResult func end(_ insets: UIEdgeInsets) -> PinLayout - #elseif os(macOS) - @discardableResult func end(_ insets: NSEdgeInsets) -> PinLayout - #endif + /** + Pin the left and right edges on its superview's corresponding edges. + + Similar to calling `view.left().right()`. + */ + @discardableResult + public func horizontally(_ margin: CGFloat = 0) -> PinLayout { + left(margin, { return "horizontally(\(margin.optionnalDescription)) left coordinate" }) + right(margin, { return "horizontally(\(margin.optionnalDescription)) right coordinate" }) + return self + } - // Pin multiple edges at once. /** - Pin all edges on its superview's corresponding edges (top, bottom, left, right). + Pin the left and right edges on its superview's corresponding edges. - Similar to calling `view.top().bottom().left().right()` + Similar to calling `view.left().right()`. */ - @discardableResult func all() -> PinLayout - @discardableResult func all(_ value: CGFloat) -> PinLayout - #if os(iOS) || os(tvOS) - @discardableResult func all(_ insets: UIEdgeInsets) -> PinLayout - #elseif os(macOS) - @discardableResult func all(_ insets: NSEdgeInsets) -> PinLayout - #endif + @discardableResult + public func horizontally(_ percent: Percent) -> PinLayout { + left(percent, { return "horizontally(\(percent.description)) left coordinate" }) + right(percent, { return "horizontally(\(percent.description)) right coordinate" }) + return self + } /** Pin the left and right edges on its superview's corresponding edges. Similar to calling `view.left().right()`. */ - @discardableResult func horizontally() -> PinLayout - @discardableResult func horizontally(_ value: CGFloat) -> PinLayout - @discardableResult func horizontally(_ percent: Percent) -> PinLayout - #if os(iOS) || os(tvOS) - @discardableResult func horizontally(_ insets: UIEdgeInsets) -> PinLayout - #elseif os(macOS) - @discardableResult func horizontally(_ insets: NSEdgeInsets) -> PinLayout - #endif + @discardableResult + public func horizontally(_ insets: PEdgeInsets) -> PinLayout { + left(insets.left, { return "horizontally(\(insets)) left coordinate" }) + right(insets.right, { return "horizontally(\(insets)) right coordinate" }) + return self + } + + /** + Pin the **top and bottom edges** on its superview's corresponding edges. + + Similar to calling `view.top(margin).bottom(margin)`. + */ + @discardableResult + public func vertically(_ margin: CGFloat = 0) -> PinLayout { + top(margin, { return "vertically(\(margin.optionnalDescription)) top coordinate" }) + bottom(margin, { return "vertically(\(margin.optionnalDescription)) bottom coordinate" }) + return self + } /** Pin the **top and bottom edges** on its superview's corresponding edges. - Similar to calling `view.top().bottom()`. + Similar to calling `view.top(percent).bottom(percent)`. */ - @discardableResult func vertically() -> PinLayout - @discardableResult func vertically(_ value: CGFloat) -> PinLayout - @discardableResult func vertically(_ percent: Percent) -> PinLayout + @discardableResult + public func vertically(_ percent: Percent) -> PinLayout { + top(percent, { return "vertically(\(percent.description)) top coordinate" }) + bottom(percent, { return "vertically(\(percent.description)) bottom coordinate" }) + return self + } + /** Pin the **top and bottom edges** on its superview's corresponding edges. The UIEdgeInsets.top is used to pin the top edge and the UIEdgeInsets.bottom for the bottom edge. */ - #if os(iOS) || os(tvOS) - @discardableResult func vertically(_ insets: UIEdgeInsets) -> PinLayout - #elseif os(macOS) - @discardableResult func vertically(_ insets: NSEdgeInsets) -> PinLayout - #endif + @discardableResult + public func vertically(_ insets: PEdgeInsets) -> PinLayout { + top(insets.top, { return "vertically(\(insets)) top coordinate" }) + bottom(insets.bottom, { return "vertically(\(insets)) bottom coordinate" }) + return self + } // // MARK: Layout using edges // - @discardableResult func top(to edge: VerticalEdge) -> PinLayout - @discardableResult func vCenter(to edge: VerticalEdge) -> PinLayout - @discardableResult func bottom(to edge: VerticalEdge) -> PinLayout - @discardableResult func left(to edge: HorizontalEdge) -> PinLayout - @discardableResult func hCenter(to edge: HorizontalEdge) -> PinLayout - @discardableResult func right(to edge: HorizontalEdge) -> PinLayout - // RTL support - @discardableResult func start(to edge: HorizontalEdge) -> PinLayout - @discardableResult func end(to edge: HorizontalEdge) -> PinLayout + // top, left, bottom, right + // + + @discardableResult + public func top(to edge: VerticalEdge) -> PinLayout { + func context() -> String { return relativeEdgeContext(method: "top", edge: edge) } + if let coordinate = computeCoordinate(forEdge: edge, context) { + setTop(coordinate, context) + } + return self + } + + @discardableResult + public func vCenter(to edge: VerticalEdge) -> PinLayout { + func context() -> String { return relativeEdgeContext(method: "vCenter", edge: edge) } + if let coordinate = computeCoordinate(forEdge: edge, context) { + setVerticalCenter(coordinate, context) + } + return self + } + + @discardableResult + public func bottom(to edge: VerticalEdge) -> PinLayout { + func context() -> String { return relativeEdgeContext(method: "bottom", edge: edge) } + if let coordinate = computeCoordinate(forEdge: edge, context) { + setBottom(coordinate, context) + } + return self + } + + @discardableResult + public func left(to edge: HorizontalEdge) -> PinLayout { + func context() -> String { return relativeEdgeContext(method: "left", edge: edge) } + if let coordinate = computeCoordinate(forEdge: edge, context) { + setLeft(coordinate, context) + } + return self + } + + @discardableResult + public func hCenter(to edge: HorizontalEdge) -> PinLayout { + func context() -> String { return relativeEdgeContext(method: "hCenter", edge: edge) } + if let coordinate = computeCoordinate(forEdge: edge, context) { + setHorizontalCenter(coordinate, context) + } + return self + } + + @discardableResult + public func right(to edge: HorizontalEdge) -> PinLayout { + func context() -> String { return relativeEdgeContext(method: "right", edge: edge) } + if let coordinate = computeCoordinate(forEdge: edge, context) { + setRight(coordinate, context) + } + return self + } + + @discardableResult + public func start(to edge: HorizontalEdge) -> PinLayout { + func context() -> String { return relativeEdgeContext(method: "start", edge: edge) } + if let coordinate = computeCoordinate(forEdge: edge, context) { + setStart(coordinate, context) + } + return self + } + + @discardableResult + public func end(to edge: HorizontalEdge) -> PinLayout { + func context() -> String { return relativeEdgeContext(method: "end", edge: edge) } + if let coordinate = computeCoordinate(forEdge: edge, context) { + setEnd(coordinate, context) + } + return self + } // // MARK: Layout using anchors // - @discardableResult func topLeft(to anchor: Anchor) -> PinLayout - @discardableResult func topLeft() -> PinLayout - @discardableResult func topStart(to anchor: Anchor) -> PinLayout - @discardableResult func topStart() -> PinLayout - - @discardableResult func topCenter(to anchor: Anchor) -> PinLayout - @discardableResult func topCenter() -> PinLayout - - @discardableResult func topRight(to anchor: Anchor) -> PinLayout - @discardableResult func topRight() -> PinLayout - @discardableResult func topEnd(to anchor: Anchor) -> PinLayout - @discardableResult func topEnd() -> PinLayout - - @discardableResult func centerLeft(to anchor: Anchor) -> PinLayout - @discardableResult func centerLeft() -> PinLayout - @discardableResult func centerStart(to anchor: Anchor) -> PinLayout - @discardableResult func centerStart() -> PinLayout - - @discardableResult func center(to anchor: Anchor) -> PinLayout - @discardableResult func center() -> PinLayout - - @discardableResult func centerRight(to anchor: Anchor) -> PinLayout - @discardableResult func centerRight() -> PinLayout - @discardableResult func centerEnd(to anchor: Anchor) -> PinLayout - @discardableResult func centerEnd() -> PinLayout - - @discardableResult func bottomLeft(to anchor: Anchor) -> PinLayout - @discardableResult func bottomLeft() -> PinLayout - @discardableResult func bottomStart(to anchor: Anchor) -> PinLayout - @discardableResult func bottomStart() -> PinLayout + // topLeft, topCenter, topRight, + // centerLeft, center, centerRight, + // bottomLeft, bottomCenter, bottomRight, + // - @discardableResult func bottomCenter(to anchor: Anchor) -> PinLayout - @discardableResult func bottomCenter() -> PinLayout + @discardableResult + public func topLeft(to anchor: Anchor) -> PinLayout { + func context() -> String { return relativeAnchorContext(method: "topLeft", anchor: anchor) } + if let coordinates = computeCoordinates(forAnchors: [anchor], context) { + setTopLeft(coordinates[0], context) + } + return self + } - @discardableResult func bottomRight(to anchor: Anchor) -> PinLayout - @discardableResult func bottomRight() -> PinLayout - @discardableResult func bottomEnd(to anchor: Anchor) -> PinLayout - @discardableResult func bottomEnd() -> PinLayout + /// Position the top and left edges. + /// + /// - Parameter margin: (Optional, default value is 0). Specifies a distance from their superview's + // corresponding edges in pixels + @discardableResult + public func topLeft(_ margin: CGFloat = 0) -> PinLayout { + return topLeft(margin, context: { return "topLeft(\(margin.optionnalDescription)" }) + } + + @discardableResult + public func topStart(to anchor: Anchor) -> PinLayout { + func context() -> String { return relativeAnchorContext(method: "topStart", anchor: anchor) } + if let coordinates = computeCoordinates(forAnchors: [anchor], context) { + setTop(coordinates[0].y, context) + setStart(coordinates[0].x, context) + } + return self + } + + /// In LTR direction position the top and left edges. + /// In RTL direction position the top and right edges. + /// + /// - Parameter margin: specifies the distance from their superview's + /// corresponding edges in pixels. + @discardableResult + public func topStart(_ margin: CGFloat = 0) -> PinLayout { + func context() -> String { return "topStart(\(margin.optionnalDescription))" } + return isLTR() ? topLeft(margin, context: context) : topRight(margin, context: context) + } + + private func topLeft(_ margin: CGFloat, context: Context) -> PinLayout { + setTopLeft(CGPoint(x: margin, y: margin), context) + return self + } + + @discardableResult + public func topCenter(to anchor: Anchor) -> PinLayout { + func context() -> String { return relativeAnchorContext(method: "topCenter", anchor: anchor) } + if let coordinates = computeCoordinates(forAnchors: [anchor], context) { + setTopCenter(coordinates[0], context) + } + return self + } + + @discardableResult + public func topCenter(_ topMargin: CGFloat = 0) -> PinLayout { + func context() -> String { return "topCenter(\(topMargin.optionnalDescription))" } + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + setTopCenter(CGPoint(x: layoutSuperviewRect.width / 2, y: topMargin), context) + return self + } + + @discardableResult + public func topRight(to anchor: Anchor) -> PinLayout { + func context() -> String { return relativeAnchorContext(method: "topRight", anchor: anchor) } + if let coordinates = computeCoordinates(forAnchors: [anchor], context) { + setTopRight(coordinates[0], context) + } + return self + } + + @discardableResult + public func topRight(_ margin: CGFloat = 0) -> PinLayout { + return topRight(margin, context: { return "topRight(\(margin.optionnalDescription))" }) + } + + @discardableResult + public func topEnd(to anchor: Anchor) -> PinLayout { + func context() -> String { return relativeAnchorContext(method: "topEnd", anchor: anchor) } + if let coordinates = computeCoordinates(forAnchors: [anchor], context) { + setTop(coordinates[0].y, context) + setEnd(coordinates[0].x, context) + } + return self + } + + @discardableResult + public func topEnd(_ margin: CGFloat = 0) -> PinLayout { + func context() -> String { return "topEnd(\(margin.optionnalDescription))" } + return isLTR() ? topRight(margin, context: context) : topLeft(margin, context: context) + } + + private func topRight(_ margin: CGFloat, context: Context) -> PinLayout { + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + setTopRight(CGPoint(x: layoutSuperviewRect.width - margin, y: margin), context) + return self + } + + @discardableResult + public func centerLeft(to anchor: Anchor) -> PinLayout { + func context() -> String { return relativeAnchorContext(method: "centerLeft", anchor: anchor) } + if let coordinates = computeCoordinates(forAnchors: [anchor], context) { + setCenterLeft(coordinates[0], context) + } + return self + } + + @discardableResult + public func centerLeft(_ leftMargin: CGFloat = 0) -> PinLayout { + return centerLeft(leftMargin, context: { return "centerLeft(\(leftMargin.optionnalDescription))" }) + } + + @discardableResult + public func centerStart(to anchor: Anchor) -> PinLayout { + func context() -> String { return relativeAnchorContext(method: "centerStart", anchor: anchor) } + if let coordinates = computeCoordinates(forAnchors: [anchor], context) { + setVerticalCenter(coordinates[0].y, context) + setStart(coordinates[0].x, context) + } + return self + } + + @discardableResult + public func centerStart(_ startMargin: CGFloat = 0) -> PinLayout { + func context() -> String { return "centerStart(\(startMargin.optionnalDescription))" } + return isLTR() ? centerLeft(startMargin, context: context) : centerRight(startMargin, context: context) + } + + private func centerLeft(_ leftMargin: CGFloat, context: Context) -> PinLayout { + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + setCenterLeft(CGPoint(x: leftMargin, y: layoutSuperviewRect.height / 2), context) + return self + } + + @discardableResult + public func center(to anchor: Anchor) -> PinLayout { + func context() -> String { return relativeAnchorContext(method: "center", anchor: anchor) } + if let coordinates = computeCoordinates(forAnchors: [anchor], context) { + setCenter(coordinates[0], context) + } + return self + } + + @discardableResult + public func center(_ margin: CGFloat = 0) -> PinLayout { + func context() -> String { return "center(\(margin.optionnalDescription))" } + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + setCenter(CGPoint(x: (layoutSuperviewRect.width / 2) + margin, y: (layoutSuperviewRect.height / 2) + margin), context) + return self + } + + @discardableResult + public func centerRight(to anchor: Anchor) -> PinLayout { + func context() -> String { return relativeAnchorContext(method: "centerRight", anchor: anchor) } + if let coordinates = computeCoordinates(forAnchors: [anchor], context) { + setCenterRight(coordinates[0], context) + } + return self + } + + @discardableResult + public func centerRight(_ rightMargin: CGFloat = 0) -> PinLayout { + return centerRight(rightMargin, context: { return "centerRight(\(rightMargin.optionnalDescription))" }) + } + + @discardableResult + public func centerEnd(to anchor: Anchor) -> PinLayout { + func context() -> String { return relativeAnchorContext(method: "centerEnd", anchor: anchor) } + if let coordinates = computeCoordinates(forAnchors: [anchor], context) { + setVerticalCenter(coordinates[0].y, context) + setEnd(coordinates[0].x, context) + } + return self + } + + @discardableResult + public func centerEnd(_ endMargin: CGFloat = 0) -> PinLayout { + func context() -> String { return "centerEnd(\(endMargin.optionnalDescription))" } + return isLTR() ? centerRight(endMargin, context: context) : centerLeft(endMargin, context: context) + } + + private func centerRight(_ rightMargin: CGFloat, context: Context) -> PinLayout { + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + setCenterRight(CGPoint(x: layoutSuperviewRect.width - rightMargin, y: layoutSuperviewRect.height / 2), context) + return self + } + + @discardableResult + public func bottomLeft(to anchor: Anchor) -> PinLayout { + func context() -> String { return relativeAnchorContext(method: "bottomLeft", anchor: anchor) } + if let coordinates = computeCoordinates(forAnchors: [anchor], context) { + setBottomLeft(coordinates[0], context) + } + return self + } + + @discardableResult + public func bottomLeft(_ margin: CGFloat = 0) -> PinLayout { + return bottomLeft(margin, context: { return "bottomLeft(\(margin.optionnalDescription))" }) + } + + private func bottomLeft(_ margin: CGFloat, context: Context) -> PinLayout { + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + setBottomLeft(CGPoint(x: margin, y: layoutSuperviewRect.height - margin), context) + return self + } + + @discardableResult + public func bottomStart(to anchor: Anchor) -> PinLayout { + func context() -> String { return relativeAnchorContext(method: "bottomStart", anchor: anchor) } + if let coordinates = computeCoordinates(forAnchors: [anchor], context) { + setBottom(coordinates[0].y, context) + setStart(coordinates[0].x, context) + } + return self + } + + @discardableResult + public func bottomStart(_ margin: CGFloat = 0) -> PinLayout { + func context() -> String { return "bottomStart(\(margin.optionnalDescription)" } + return isLTR() ? bottomLeft(margin, context: context) : bottomRight(margin, context: context) + } + + @discardableResult + public func bottomCenter(to anchor: Anchor) -> PinLayout { + func context() -> String { return relativeAnchorContext(method: "bottomCenter", anchor: anchor) } + if let coordinates = computeCoordinates(forAnchors: [anchor], context) { + setBottomCenter(coordinates[0], context) + } + return self + } + + @discardableResult + public func bottomCenter(_ bottomMargin: CGFloat = 0) -> PinLayout { + func context() -> String { return "bottomCenter(\(bottomMargin.optionnalDescription))" } + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + setBottomCenter(CGPoint(x: layoutSuperviewRect.width / 2, y: layoutSuperviewRect.height - bottomMargin), context) + return self + } + + @discardableResult + public func bottomRight(to anchor: Anchor) -> PinLayout { + func context() -> String { return relativeAnchorContext(method: "bottomRight", anchor: anchor) } + if let coordinates = computeCoordinates(forAnchors: [anchor], context) { + setBottomRight(coordinates[0], context) + } + return self + } + + @discardableResult + public func bottomRight(_ margin: CGFloat = 0) -> PinLayout { + return bottomRight(margin, context: { return "bottomRight(\(margin.optionnalDescription))" }) + } + + private func bottomRight(_ margin: CGFloat, context: Context) -> PinLayout { + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + setBottomRight(CGPoint(x: layoutSuperviewRect.width - margin, y: layoutSuperviewRect.height - margin), context) + return self + } + + @discardableResult + public func bottomEnd(to anchor: Anchor) -> PinLayout { + func context() -> String { return relativeAnchorContext(method: "bottomEnd", anchor: anchor) } + if let coordinates = computeCoordinates(forAnchors: [anchor], context) { + setBottom(coordinates[0].y, context) + setEnd(coordinates[0].x, context) + } + return self + } + + @discardableResult + public func bottomEnd(_ margin: CGFloat = 0) -> PinLayout { + func context() -> String { return "bottomEnd(\(margin.optionnalDescription))" } + return isLTR() ? bottomRight(margin, context: context) : bottomLeft(margin, context: context) + } // - // MARK: Layout using relative positioning + // MARK: Width, height // - #if os(iOS) || os(tvOS) - @discardableResult func above(of: UIView) -> PinLayout - @discardableResult func above(of: [UIView]) -> PinLayout - @discardableResult func above(of: UIView, aligned: HorizontalAlign) -> PinLayout - @discardableResult func above(of: [UIView], aligned: HorizontalAlign) -> PinLayout - - @discardableResult func below(of: UIView) -> PinLayout - @discardableResult func below(of: [UIView]) -> PinLayout - @discardableResult func below(of: UIView, aligned: HorizontalAlign) -> PinLayout - @discardableResult func below(of: [UIView], aligned: HorizontalAlign) -> PinLayout - - @discardableResult func left(of: UIView) -> PinLayout - @discardableResult func left(of: [UIView]) -> PinLayout - @discardableResult func left(of: UIView, aligned: VerticalAlign) -> PinLayout - @discardableResult func left(of: [UIView], aligned: VerticalAlign) -> PinLayout - - @discardableResult func right(of: UIView) -> PinLayout - @discardableResult func right(of: [UIView]) -> PinLayout - @discardableResult func right(of: UIView, aligned: VerticalAlign) -> PinLayout - @discardableResult func right(of: [UIView], aligned: VerticalAlign) -> PinLayout - - // RTL support - @discardableResult func before(of: UIView) -> PinLayout - @discardableResult func before(of: [UIView]) -> PinLayout - @discardableResult func before(of: UIView, aligned: VerticalAlign) -> PinLayout - @discardableResult func before(of: [UIView], aligned: VerticalAlign) -> PinLayout - @discardableResult func after(of: UIView) -> PinLayout - @discardableResult func after(of: [UIView]) -> PinLayout - @discardableResult func after(of: UIView, aligned: VerticalAlign) -> PinLayout - @discardableResult func after(of: [UIView], aligned: VerticalAlign) -> PinLayout - #elseif os(macOS) - @discardableResult func above(of: NSView) -> PinLayout - @discardableResult func above(of: [NSView]) -> PinLayout - @discardableResult func above(of: NSView, aligned: HorizontalAlign) -> PinLayout - @discardableResult func above(of: [NSView], aligned: HorizontalAlign) -> PinLayout - - @discardableResult func below(of: NSView) -> PinLayout - @discardableResult func below(of: [NSView]) -> PinLayout - @discardableResult func below(of: NSView, aligned: HorizontalAlign) -> PinLayout - @discardableResult func below(of: [NSView], aligned: HorizontalAlign) -> PinLayout - - @discardableResult func left(of: NSView) -> PinLayout - @discardableResult func left(of: [NSView]) -> PinLayout - @discardableResult func left(of: NSView, aligned: VerticalAlign) -> PinLayout - @discardableResult func left(of: [NSView], aligned: VerticalAlign) -> PinLayout - - @discardableResult func right(of: NSView) -> PinLayout - @discardableResult func right(of: [NSView]) -> PinLayout - @discardableResult func right(of: NSView, aligned: VerticalAlign) -> PinLayout - @discardableResult func right(of: [NSView], aligned: VerticalAlign) -> PinLayout - - // RTL support - @discardableResult func before(of: NSView) -> PinLayout - @discardableResult func before(of: [NSView]) -> PinLayout - @discardableResult func before(of: NSView, aligned: VerticalAlign) -> PinLayout - @discardableResult func before(of: [NSView], aligned: VerticalAlign) -> PinLayout - @discardableResult func after(of: NSView) -> PinLayout - @discardableResult func after(of: [NSView]) -> PinLayout - @discardableResult func after(of: NSView, aligned: VerticalAlign) -> PinLayout - @discardableResult func after(of: [NSView], aligned: VerticalAlign) -> PinLayout - #endif + + @discardableResult + public func width(_ width: CGFloat) -> PinLayout { + return setWidth(width, { return "width(\(width))" }) + } + + @discardableResult + public func width(_ percent: Percent) -> PinLayout { + func context() -> String { return "width(\(percent.description))" } + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + return setWidth(percent.of(layoutSuperviewRect.width), context) + } + + @discardableResult + public func width(of view: PinView) -> PinLayout { + let rect = view.getRect(keepTransform: keepTransform) + return setWidth(rect.width, { return "width(of: \(viewDescription(view)))" }) + } + + @discardableResult + public func minWidth(_ width: CGFloat) -> PinLayout { + setMinWidth(width, { return "minWidth(\(width))" }) + return self + } + + @discardableResult + public func minWidth(_ percent: Percent) -> PinLayout { + func context() -> String { return "minWidth(\(percent.description))" } + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + return setMinWidth(percent.of(layoutSuperviewRect.width), context) + } + + @discardableResult + public func maxWidth(_ width: CGFloat) -> PinLayout { + setMaxWidth(width, { return "maxWidth(\(width))" }) + return self + } + + @discardableResult + public func maxWidth(_ percent: Percent) -> PinLayout { + func context() -> String { return "maxWidth(\(percent.description))" } + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + return setMaxWidth(percent.of(layoutSuperviewRect.width), context) + } + + @discardableResult + public func height(_ height: CGFloat) -> PinLayout { + return setHeight(height, { return "height(\(height))" }) + } + + @discardableResult + public func height(_ percent: Percent) -> PinLayout { + func context() -> String { return "height(\(percent.description))" } + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + return setHeight(percent.of(layoutSuperviewRect.height), context) + } + + @discardableResult + public func height(of view: PinView) -> PinLayout { + let rect = view.getRect(keepTransform: keepTransform) + return setHeight(rect.height, { return "height(of: \(viewDescription(view)))" }) + } + + @discardableResult + public func minHeight(_ height: CGFloat) -> PinLayout { + setMinHeight(height, { return "minHeight(\(height))" }) + return self + } + + @discardableResult + public func minHeight(_ percent: Percent) -> PinLayout { + func context() -> String { return "minHeight(\(percent.description))" } + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + return setMinHeight(percent.of(layoutSuperviewRect.height), context) + } + + @discardableResult + public func maxHeight(_ height: CGFloat) -> PinLayout { + setMaxHeight(height, { return "maxHeight(\(height))" }) + return self + } + + @discardableResult + public func maxHeight(_ percent: Percent) -> PinLayout { + func context() -> String { return "maxHeight(\(percent.description))" } + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + return setMaxHeight(percent.of(layoutSuperviewRect.height), context) + } // // MARK: justify / align // - @discardableResult func justify(_: HorizontalAlign) -> PinLayout - @discardableResult func align(_: VerticalAlign) -> PinLayout + @discardableResult + public func justify(_ value: HorizontalAlign) -> PinLayout { + justify = value + return self + } + + @discardableResult + public func align(_ value: VerticalAlign) -> PinLayout { + align = value + return self + } + // - // MARK: Width, height and size + // MARK: Margins // - @discardableResult func width(_ width: CGFloat) -> PinLayout - @discardableResult func width(_ percent: Percent) -> PinLayout - #if os(iOS) || os(tvOS) - @discardableResult func width(of view: UIView) -> PinLayout - #elseif os(macOS) - @discardableResult func width(of view: NSView) -> PinLayout - #endif - - @discardableResult func minWidth(_ width: CGFloat) -> PinLayout - @discardableResult func minWidth(_ percent: Percent) -> PinLayout - @discardableResult func maxWidth(_ width: CGFloat) -> PinLayout - @discardableResult func maxWidth(_ percent: Percent) -> PinLayout - - @discardableResult func height(_ height: CGFloat) -> PinLayout - @discardableResult func height(_ percent: Percent) -> PinLayout - #if os(iOS) || os(tvOS) - @discardableResult func height(of view: UIView) -> PinLayout - #elseif os(macOS) - @discardableResult func height(of view: NSView) -> PinLayout - #endif - - @discardableResult func minHeight(_ height: CGFloat) -> PinLayout - @discardableResult func minHeight(_ percent: Percent) -> PinLayout - @discardableResult func maxHeight(_ height: CGFloat) -> PinLayout - @discardableResult func maxHeight(_ percent: Percent) -> PinLayout - - @discardableResult func size(_ size: CGSize) -> PinLayout - @discardableResult func size(_ sideLength: CGFloat) -> PinLayout - @discardableResult func size(_ percent: Percent) -> PinLayout - #if os(iOS) || os(tvOS) - @discardableResult func size(of view: UIView) -> PinLayout - #elseif os(macOS) - @discardableResult func size(of view: NSView) -> PinLayout - #endif - - // @discardableResult func wrapSubViews() -> PinLayout - // @discardableResult func wrapSubViews(insets: UIEdgeInsets) -> PinLayout /** - Set the view aspect ratio. - - AspectRatio is applied only if a single dimension (either width or height) can be determined, - in that case the aspect ratio will be used to compute the other dimension. - - * AspectRatio is defined as the ratio between the width and the height (width / height). - * An aspect ratio of 2 means the width is twice the size of the height. - * AspectRatio respects the min (minWidth/minHeight) and the max (maxWidth/maxHeight) - dimensions of an item. + Set the top margin. */ - @discardableResult func aspectRatio(_ ratio: CGFloat) -> PinLayout - /** - Set the view aspect ratio using another UIView's aspect ratio. - - AspectRatio is applied only if a single dimension (either width or height) can be determined, - in that case the aspect ratio will be used to compute the other dimension. + @discardableResult + public func marginTop(_ margin: CGFloat) -> PinLayout { + marginTop = margin + return self + } - * AspectRatio is defined as the ratio between the width and the height (width / height). - * AspectRatio respects the min (minWidth/minHeight) and the max (maxWidth/maxHeight) - dimensions of an item. + /** + Set the top margin. */ - #if os(iOS) || os(tvOS) - @discardableResult func aspectRatio(of view: UIView) -> PinLayout - #elseif os(macOS) - @discardableResult func aspectRatio(of view: NSView) -> PinLayout - #endif + @discardableResult + public func marginTop(_ percent: Percent) -> PinLayout { + func context() -> String { return "marginTop(\(percent.description))" } + return marginTop(percent, context) + } + + private func marginTop(_ percent: Percent, _ context: Context) -> Self { + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + marginTop = percent.of(layoutSuperviewRect.height) + return self + } /** - If the layouted view is an UIImageView, this method will set the aspectRatio using - the UIImageView's image dimension. - - For other types of views, this method as no impact. + Set the left margin. */ - #if os(iOS) || os(tvOS) - @discardableResult func aspectRatio() -> PinLayout - #endif + @discardableResult + public func marginLeft(_ margin: CGFloat) -> PinLayout { + marginLeft = margin + return self + } /** - The method adjust the view's size based on the view's `sizeThatFits()` method result. - PinLayout will adjust either the view's width or height based on the `fitType` parameter value. - - Notes: - * If margin rules apply, margins will be applied when determining the reference dimension (width/height). - * The resulting size will always respect `minWidth` / `maxWidth` / `minHeight` / `maxHeight`. - - - Parameter fitType: Identify the reference dimension (width / height) that will be used - to adjust the view's size: - - .width: The method adjust the view's size based on the **reference width**. - * If properties related to the width have been pinned (e.g: width, left & right, margins, ...), - the **reference width will be determined by these properties**, if not the **current view's width** - will be used. - * The resulting width will always **match the reference width**. - - .height: The method adjust the view's size based on the **reference height**. - * If properties related to the height have been pinned (e.g: height, top & bottom, margins, ...), - the **reference height will be determined by these properties**, if not the **current view's height** - will be used. - * The resulting height will always **match the reference height**. - - .widthFlexible: Similar to `.width`, except that PinLayout won't constrain the resulting width to - match the reference width. The resulting width may be smaller or bigger depending on the view's - sizeThatFits(..) method result. For example a single line UILabel may returns a smaller width if its - string is smaller than the reference width. - - .heightFlexible: Similar to `.height`, except that PinLayout won't constrain the resulting height to - match the reference height. The resulting height may be smaller or bigger depending on the view's - sizeThatFits(..) method result. - - Examples: - - ``` - // Adjust the view's size based on a width of 100 pixels. - // The resulting width will always match the pinned property `width(100)`. - view.pin.width(100).sizeToFit(.width) - - // Adjust the view's size based on view's current width. - // The resulting width will always match the view's original width. - // The resulting height will never be bigger than the specified `maxHeight`. - view.pin.sizeToFit(.width).maxHeight(100) - - // Adjust the view's size based on 100% of the superview's height. - // The resulting height will always match the pinned property `height(100%)`. - view.pin.height(100%).sizeToFit(.height) - - // Adjust the view's size based on view's current height. - // The resulting width will always match the view's original height. - view.pin.sizeToFit(.height) - - // Since `.widthFlexible` has been specified, its possible that the resulting - // width will be smaller or bigger than 100 pixels, based of the label's sizeThatFits() - // method result. - label.pin.width(100).sizeToFit(.widthFlexible) - ``` + Set the left margin. */ - #if os(iOS) || os(tvOS) - @discardableResult func sizeToFit(_ fitType: FitType) -> PinLayout - #endif + @discardableResult + public func marginLeft(_ percent: Percent) -> PinLayout { + func context() -> String { return "marginLeft(\(percent.description))" } + return marginLeft(percent, context) + } + + private func marginLeft(_ percent: Percent, _ context: Context) -> Self { + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + marginLeft = percent.of(layoutSuperviewRect.width) + return self + } - #if os(iOS) || os(tvOS) - @available(*, deprecated, message: "fitSize() is deprecated, please use sizeToFit(fitType: FitType)") - @discardableResult func fitSize() -> PinLayout - #endif - - // - // MARK: Margins - // /** - Set the top margin. + Set the bottom margin. */ - @discardableResult func marginTop(_ value: CGFloat) -> PinLayout - @discardableResult func marginTop(_ percent: Percent) -> PinLayout + @discardableResult + public func marginBottom(_ margin: CGFloat) -> PinLayout { + marginBottom = margin + return self + } /** - Set the left margin. + Set the bottom margin. */ - @discardableResult func marginLeft(_ value: CGFloat) -> PinLayout - @discardableResult func marginLeft(_ percent: Percent) -> PinLayout + @discardableResult + public func marginBottom(_ percent: Percent) -> PinLayout { + func context() -> String { return "marginBottom(\(percent.description))" } + return marginBottom(percent, context) + } + + private func marginBottom(_ percent: Percent, _ context: Context) -> Self { + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + marginBottom = percent.of(layoutSuperviewRect.height) + return self + } /** - Set the bottom margin. + Set the right margin. */ - @discardableResult func marginBottom(_ value: CGFloat) -> PinLayout - @discardableResult func marginBottom(_ percent: Percent) -> PinLayout + @discardableResult + public func marginRight(_ margin: CGFloat) -> PinLayout { + marginRight = margin + return self + } /** Set the right margin. */ - @discardableResult func marginRight(_ value: CGFloat) -> PinLayout - @discardableResult func marginRight(_ percent: Percent) -> PinLayout + @discardableResult + public func marginRight(_ percent: Percent) -> PinLayout { + func context() -> String { return "marginRight(\(percent.description))" } + return marginRight(percent, context) + } + + private func marginRight(_ percent: Percent, _ context: Context) -> Self { + guard let layoutSuperviewRect = layoutSuperviewRect(context) else { return self } + marginRight = percent.of(layoutSuperviewRect.width) + return self + } // RTL support + /** Set the start margin. @@ -530,8 +980,35 @@ public protocol PinLayout { * In LTR direction, start margin specify the **left** margin. * In RTL direction, start margin specify the **right** margin. */ - @discardableResult func marginStart(_ value: CGFloat) -> PinLayout - @discardableResult func marginStart(_ percent: Percent) -> PinLayout + @discardableResult + public func marginStart(_ margin: CGFloat) -> PinLayout { + return isLTR() ? marginLeft(margin) : marginRight(margin) + } + + /** + Set the start margin. + + Depends on the value of `Pin.layoutDirection(...)`: + * In LTR direction, start margin specify the **left** margin. + * In RTL direction, start margin specify the **right** margin. + */ + @discardableResult + public func marginStart(_ percent: Percent) -> PinLayout { + func context() -> String { return "marginStart(\(percent.description))" } + return isLTR() ? marginLeft(percent, context) : marginRight(percent, context) + } + + /** + Set the end margin. + + Depends on the value of `Pin.layoutDirection(...)`: + * In LTR direction, end margin specify the **right** margin. + * In RTL direction, end margin specify the **left** margin. + */ + @discardableResult + public func marginEnd(_ margin: CGFloat) -> PinLayout { + return isLTR() ? marginRight(margin) : marginLeft(margin) + } /** Set the end margin. @@ -540,30 +1017,70 @@ public protocol PinLayout { * In LTR direction, end margin specify the **right** margin. * In RTL direction, end margin specify the **left** margin. */ - @discardableResult func marginEnd(_ value: CGFloat) -> PinLayout - @discardableResult func marginEnd(_ percent: Percent) -> PinLayout + @discardableResult + public func marginEnd(_ percent: Percent) -> PinLayout { + func context() -> String { return "marginEnd(\(percent.description))" } + return isLTR() ? marginRight(percent, context) : marginLeft(percent, context) + } /** Set the left, right, start and end margins to the specified value. */ - @discardableResult func marginHorizontal(_ value: CGFloat) -> PinLayout - @discardableResult func marginHorizontal(_ percent: Percent) -> PinLayout + @discardableResult + public func marginHorizontal(_ margin: CGFloat) -> PinLayout { + marginLeft = margin + marginRight = margin + return self + } + + /** + Set the left, right, start and end margins to the specified value. + */ + @discardableResult + public func marginHorizontal(_ percent: Percent) -> PinLayout { + func context() -> String { return "marginHorizontal(\(percent.description))" } + return marginHorizontal(percent, context) + } + + private func marginHorizontal(_ percent: Percent, _ context: Context) -> Self { + return marginLeft(percent, context).marginRight(percent, context) + } + + /** + Set the top and bottom margins to the specified value. + */ + @discardableResult + public func marginVertical(_ margin: CGFloat) -> PinLayout { + marginTop = margin + marginBottom = margin + return self + } /** Set the top and bottom margins to the specified value. */ - @discardableResult func marginVertical(_ value: CGFloat) -> PinLayout - @discardableResult func marginVertical(_ percent: Percent) -> PinLayout + @discardableResult + public func marginVertical(_ percent: Percent) -> PinLayout { + func context() -> String { return "marginVertical(\(percent.description))" } + return marginVertical(percent, context) + } + + private func marginVertical(_ percent: Percent, _ context: Context) -> Self { + return marginTop(percent, context).marginBottom(percent, context) + } /** Set all margins using UIEdgeInsets. This method is particularly useful to set all margins using iOS 11 `UIView.safeAreaInsets`. */ - #if os(iOS) || os(tvOS) - @discardableResult func margin(_ insets: UIEdgeInsets) -> PinLayout - #elseif os(macOS) - @discardableResult func margin(_ insets: NSEdgeInsets) -> PinLayout - #endif + @discardableResult + public func margin(_ insets: PEdgeInsets) -> PinLayout { + marginTop = insets.top + marginBottom = insets.bottom + marginLeft = insets.left + marginRight = insets.right + return self + } /** Set margins using NSDirectionalEdgeInsets. @@ -573,118 +1090,151 @@ public protocol PinLayout { */ #if os(iOS) || os(tvOS) @available(tvOS 11.0, iOS 11.0, *) - @discardableResult func margin(_ directionalInsets: NSDirectionalEdgeInsets) -> PinLayout + @discardableResult + public func margin(_ directionalInsets: NSDirectionalEdgeInsets) -> PinLayout { + marginTop = directionalInsets.top + marginBottom = directionalInsets.bottom + marginStart(directionalInsets.leading) + marginEnd(directionalInsets.trailing) + return self + } #endif /** Set all margins to the specified value. */ - @discardableResult func margin(_ value: CGFloat) -> PinLayout - @discardableResult func margin(_ percent: Percent) -> PinLayout + @discardableResult + public func margin(_ margin: CGFloat) -> PinLayout { + marginTop = margin + marginLeft = margin + marginBottom = margin + marginRight = margin + return self + } /** - Set individually vertical margins (top, bottom) and horizontal margins (left, right, start, end). + Set all margins to the specified value. */ - @discardableResult func margin(_ vertical: CGFloat, _ horizontal: CGFloat) -> PinLayout - @discardableResult func margin(_ vertical: Percent, _ horizontal: Percent) -> PinLayout + @discardableResult + public func margin(_ percent: Percent) -> PinLayout { + func context() -> String { return "margin(\(percent.description))" } + return marginTop(percent, context) + .marginLeft(percent, context) + .marginBottom(percent, context) + .marginRight(percent, context) + } /** Set individually top, horizontal margins and bottom margin. */ - @discardableResult func margin(_ top: CGFloat, _ horizontal: CGFloat, _ bottom: CGFloat) -> PinLayout - @discardableResult func margin(_ top: Percent, _ horizontal: Percent, _ bottom: Percent) -> PinLayout + @discardableResult + public func margin(_ top: CGFloat, _ left: CGFloat, _ bottom: CGFloat, _ right: CGFloat) -> PinLayout { + marginTop = top + marginLeft = left + marginBottom = bottom + marginRight = right + return self + } /** - Set individually top, left, bottom and right margins. + Set individually top, horizontal margins and bottom margin. */ - @discardableResult func margin(_ top: CGFloat, _ left: CGFloat, _ bottom: CGFloat, _ right: CGFloat) -> PinLayout - @discardableResult func margin(_ top: Percent, _ left: Percent, _ bottom: Percent, _ right: Percent) -> PinLayout - - /// Normally if only either left or right has been specified, PinLayout will MOVE the view to apply left or right margins. - /// This is also true even if the width has been set. - /// Calling pinEdges() will force PinLayout to pin the four edges and then apply left and/or right margins, and this without - /// moving the view. - /// - /// - Returns: PinLayout - @discardableResult func pinEdges() -> PinLayout + @discardableResult + public func margin(_ top: Percent, _ left: Percent, _ bottom: Percent, _ right: Percent) -> PinLayout { + func context() -> String { + return "margin(top: \(top.description), left: \(left.description), bottom: \(bottom.description), right: \(right.description)" + } + return marginTop(top, context) + .marginLeft(left, context) + .marginBottom(bottom, context) + .marginRight(right, context) + } /** - The method will execute PinLayout commands immediately. This method is **required only if your - source codes should also work in Xcode Playgrounds**. Outside of playgrounds, PinLayout executes - this method implicitly, it is not necessary to call it. - - Examples: - ```swift - view.pin.top(20).width(100).layout() - ``` + Set individually vertical margins (top, bottom) and horizontal margins (left, right, start, end). */ - func layout() -} - -/// Horizontal alignment used with relative positionning methods: above(of relativeView:, aligned:), below(of relativeView:, aligned:) -/// -/// - left: left aligned -/// - center: center aligned -/// - right: right aligned -@objc public enum HorizontalAlign: Int { - case left - case center - case right - - // RTL support - case start - case end -} - -/// Vertical alignment used with relative positionning methods: left(of relativeView:, aligned:), right(of relativeView:, aligned:) -/// -/// - top: top aligned -/// - center: center aligned -/// - bottom: bottom aligned -@objc public enum VerticalAlign: Int { - case top - case center - case bottom -} + @discardableResult + public func margin(_ vertical: CGFloat, _ horizontal: CGFloat) -> PinLayout { + marginTop = vertical + marginLeft = horizontal + marginBottom = vertical + marginRight = horizontal + return self + } -/// UIView's horizontal edges (left/right) definition -@objc public protocol HorizontalEdge { -} - -/// UIView's vertical edges (top/bottom) definition -@objc public protocol VerticalEdge { -} - -public enum FitType { - /** - **Adjust the view's height** based on the reference width. - * If properties related to the width have been pinned (e.g: width, left & right, margins), - the **reference width will be determined by these properties**, else the **current view's width** - will be used. - * The resulting width will always **match the reference width**. - */ - case width /** - **Adjust the view's width** based on the reference height. - * If properties related to the height have been pinned (e.g: height, top & bottom, margins), - the reference height will be determined by these properties, else the **current view's height** - will be used. - * The resulting height will always **match the reference height*. + Set individually vertical margins (top, bottom) and horizontal margins (left, right, start, end). */ - case height + @discardableResult + public func margin(_ vertical: Percent, _ horizontal: Percent) -> PinLayout { + func context() -> String { return "margin(vertical: \(vertical.description), horizontal: \(horizontal.description)" } + return marginVertical(vertical, context).marginHorizontal(horizontal, context) + } /** - Similar to `.width`, except that PinLayout won't constrain the resulting width to - match the reference width. The resulting width may be smaller of bigger depending on the view's - sizeThatFits(..) method result. For example a single line UILabel may returns a smaller width if its - string is smaller than the reference width. + Set individually top, horizontal margins and bottom margin. */ - case widthFlexible + @discardableResult + public func margin(_ top: CGFloat, _ horizontal: CGFloat, _ bottom: CGFloat) -> PinLayout { + marginTop = top + marginLeft = horizontal + marginBottom = bottom + marginRight = horizontal + return self + } + /** - Similar to `.height`, except that PinLayout won't constrain the resulting height to - match the reference height. The resulting height may be smaller of bigger depending on the view's - sizeThatFits(..) method result. + Set individually top, horizontal margins and bottom margin. */ - case heightFlexible + @discardableResult + public func margin(_ top: Percent, _ horizontal: Percent, _ bottom: Percent) -> PinLayout { + func context() -> String { return "margin(top: \(top.description), horizontal: \(horizontal.description), bottom: \(bottom.description)" } + return marginTop(top, context).marginHorizontal(horizontal, context).marginBottom(bottom, context) + } + + /// Normally if only either left or right has been specified, PinLayout will MOVE the view to apply left or right margins. + /// This is also true even if the width has been set. + /// Calling pinEdges() will force PinLayout to pin the four edges and then apply left and/or right margins, and this without + /// moving the view. + @discardableResult + public func pinEdges() -> PinLayout { + shouldPinEdges = true + return self + } } +// +// MARK: Private methods +// +extension PinLayout { + internal func layoutSuperviewRect(_ context: Context) -> CGRect? { + if let superview = view.superview { + return superview.getRect(keepTransform: keepTransform) + } else { + // Disable this warning: Using XIB, layoutSubview() is called even before views have been + // added, and there is no way to modify that strange behaviour of UIKit. + //warnWontBeApplied("the view must be added as a sub-view before being layouted using this method.", context) + return nil + } + } + + internal func layoutSuperview(_ context: Context) -> PinView? { + if let superview = view.superview { + return superview as? PinView + } else { + // Disable this warning: Using XIB, layoutSubview() is called even before views have been + // added, and there is no way to modify that strange behaviour of UIKit. + //warnWontBeApplied("the view must be added as a sub-view before being layouted using this method.", context) + return nil + } + } + + internal func referenceSuperview(_ referenceView: PinView, _ context: Context) -> PinView? { + if let superview = referenceView.superview { + return superview as? PinView + } else { + warnWontBeApplied("the reference view \(viewDescription(referenceView)) must be added as a sub-view before being used as a reference.", context) + return nil + } + } +} diff --git a/Sources/SizeCalculable.swift b/Sources/SizeCalculable.swift new file mode 100644 index 00000000..b9ac5e03 --- /dev/null +++ b/Sources/SizeCalculable.swift @@ -0,0 +1,28 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(iOS) || os(tvOS) +import UIKit +#else +import AppKit +#endif + +public protocol SizeCalculable { + func sizeThatFits(_ size: CGSize) -> CGSize +} diff --git a/Sources/SupportingFiles/PinLayoutMacOS.h b/Sources/SupportingFiles/PinLayoutMacOS.h index 9ac61a4b..bd5a5e76 100644 --- a/Sources/SupportingFiles/PinLayoutMacOS.h +++ b/Sources/SupportingFiles/PinLayoutMacOS.h @@ -1,13 +1,23 @@ +// Copyright (c) 2018 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: // -// PinLayoutMacOS.h -// PinLayoutMacOS -// -// Created by Luc Dion on 2018-04-16. -// Copyright © 2018 mcswiftlayyout.mirego.com. All rights reserved. +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. #import -//#import //! Project version number for PinLayoutMacOS. FOUNDATION_EXPORT double PinLayoutMacOSVersionNumber; diff --git a/Sources/Types+Description.swift b/Sources/Types+Description.swift new file mode 100644 index 00000000..a1da9c48 --- /dev/null +++ b/Sources/Types+Description.swift @@ -0,0 +1,84 @@ +// Copyright (c) 2018 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(iOS) || os(tvOS) +import UIKit +#else +import AppKit +#endif + +extension HorizontalAlign { + var description: String { + switch self { + case .left: return "left" + case .center: return "center" + case .right: return "right" + case .start: return "start" + case .end: return "end" + case .none: return "none" + } + } +} + +extension VerticalAlign { + var description: String { + switch self { + case .top: return "top" + case .center: return "center" + case .bottom: return "bottom" + case .none: return "none" + } + } +} + +extension CGFloat { + var description: String { + if self.truncatingRemainder(dividingBy: 1) == 0.0 { + return "\(Int(self))" + } else { + return "\(self)" + } + } + + var optionnalDescription: String { + return self == 0 ? "" : self.description + } +} + +extension FitType { + var description: String { + switch self { + case .width: return ".width" + case .height: return ".height" + case .widthFlexible: return ".widthFlexible" + case .heightFlexible: return ".heightFlexible" + case .content: return "" + } + } +} + +extension WrapType { + var description: String { + switch self { + case .all: return ".all" + case .horizontally: return ".horizontally" + case .vertically: return ".vertically" + } + } +} diff --git a/Sources/Types.swift b/Sources/Types.swift new file mode 100644 index 00000000..90ab6bb1 --- /dev/null +++ b/Sources/Types.swift @@ -0,0 +1,213 @@ +// Copyright (c) 2018 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +/* + UIView's anchors point + ====================== + + topLeft topCenter topRight + o-------------o--------------o + | | + | | + | | + | | + | | + | center | + centerLeft o o o centerRight + | | + | | + | | + | | + | | + | | + o-------------o--------------o + bottomLeft bottomCenter bottomLeft + + */ + +/// UIViews's anchor definition +@objc public protocol Anchor { +} + +/// UIViews's list of anchors. +@objc public protocol AnchorList { + var topLeft: Anchor { get } + var topCenter: Anchor { get } + var topRight: Anchor { get } + var centerLeft: Anchor { get } + var center: Anchor { get } + var centerRight: Anchor { get } + var bottomLeft: Anchor { get } + var bottomCenter: Anchor { get } + var bottomRight: Anchor { get } + + // RTL support + var topStart: Anchor { get } + var topEnd: Anchor { get } + var centerStart: Anchor { get } + var centerEnd: Anchor { get } + var bottomStart: Anchor { get } + var bottomEnd: Anchor { get } +} + +/* + UIView's Edges + ====================== + top + +-----------------+ + | | + | | + | hCenter | + left | + | right + | vCenter | + | | + | | + +-----------------+ + bottom + */ + +/// UIViews's list of edges +@objc public protocol EdgeList { + var top: VerticalEdge { get } + var vCenter: VerticalEdge { get } + var bottom: VerticalEdge { get } + var left: HorizontalEdge { get } + var hCenter: HorizontalEdge { get } + var right: HorizontalEdge { get } + + // RTL support + var start: HorizontalEdge { get } + var end: HorizontalEdge { get } +} + +/// Horizontal alignment used with relative positioning methods: above(of relativeView:, aligned:), below(of relativeView:, aligned:), ... +/// +/// - left: left aligned +/// - center: center aligned +/// - right: right aligned +@objc public enum HorizontalAlign: Int { + /// The view's left edge will be left-aligned with the relative view (or the left most view if a list of relative views is specified). + case left + /// The view's will be horizontally centered with the relative view (or the average hCenter if a list of relative views is used). + case center + /// The view's right edge will be right-aligned with the relative view (or the right most view if a list of relative views is specified). + case right + /// No alignment will be applied. + case none + // RTL support + /// In LTR direction, similar to using HorizontalAlignment.left. + /// In RTL direction, similar to using HorizontalAlignment.right. + case start + /// In LTR direction, similar to using HorizontalAlignment.right. + /// In RTL direction, similar to using HorizontalAlignment.left. + case end +} + +/// Vertical alignment used with relative positioning methods: after(of relativeView:, aligned:), before(of relativeView:, aligned:), ... +/// +/// - top: top aligned +/// - center: center aligned +/// - bottom: bottom aligned +@objc public enum VerticalAlign: Int { + /// The view's top edge will be top-aligned with the relative view (or the top most view if a list of relative views is specified). + case top + /// The view's will be vertically centered with the relative view (or the average vCenter if a list of relative views is used). + case center + /// The view's bottom edge will be bottom-aligned with the relative view (or the bottom most view if a list of relative views is specified). + case bottom + /// No alignment will be applied. + case none +} + +/// UIView's horizontal edges (left/right) definition +@objc public protocol HorizontalEdge { +} + +/// UIView's vertical edges (top/bottom) definition +@objc public protocol VerticalEdge { +} + +public enum FitType { + /** + **Adjust the view's height** based on the reference width. + * If properties related to the width have been pinned (e.g: width, left & right, margins), + the **reference width will be determined by these properties**, else the **current view's width** + will be used. + * The resulting width will always **match the reference width**. + */ + case width + /** + **Adjust the view's width** based on the reference height. + * If properties related to the height have been pinned (e.g: height, top & bottom, margins), + the reference height will be determined by these properties, else the **current view's height** + will be used. + * The resulting height will always **match the reference height*. + */ + case height + + /** + Similar to `.width`, except that PinLayout won't constrain the resulting width to + match the reference width. The resulting width may be smaller of bigger depending on the view's + sizeThatFits(..) method result. For example a single line UILabel may returns a smaller width if its + string is smaller than the reference width. + */ + case widthFlexible + /** + Similar to `.height`, except that PinLayout won't constrain the resulting height to + match the reference height. The resulting height may be smaller or bigger depending on the view's + sizeThatFits(..) method result. + */ + case heightFlexible + + /** + Adjust the view's size based on it's content size requirements so that it uses the + most appropriate amount of space. This fit type has the same effect as calling **sizeToFit()** on a view. + The resulting size come from sizeThatFits(..) being called with the current view bounds. + */ + case content +} + +@objc public enum WrapType: Int { + /// Adjust the view's width AND height to wrap all its subviews. + case all + /// Adjust only the view's width to wrap all its subviews. The view's height won't be modified. + case horizontally + /// Adjust only the view's height to wrap all its subviews. The view's width won't be modified. + case vertically +} + +@objc public enum LayoutDirection: Int { + case auto + case ltr + case rtl +} + +/// Control how PinLayout will calls `UIView.safeAreaInsetsDidChange` when the `UIView.pin.safeArea` change. +/// This support is usefull only on iOS 8/9/10. On iOS 11 `UIView.safeAreaInsetsDidChange` is supported +/// natively so this settings have no impact. +@objc public enum PinSafeAreaInsetsDidChangeMode: Int { + /// PinLayout won't call `UIView.safeAreaInsetsDidChange` on iOS 8/9/10. + case disable + /// PinLayout will call `UIView.safeAreaInsetsDidChange` only if the UIView implement the PinSafeAreaInsetsUpdate protocol. + case optIn + /// PinLayout will automatically calls `UIView.safeAreaInsetsDidChange` if the view has implemented this method. + case always +} diff --git a/Sources/UIView+PinLayout.swift b/Sources/UIView+PinLayout.swift deleted file mode 100644 index eca85485..00000000 --- a/Sources/UIView+PinLayout.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// UIView+PinLayout.swift -// PinLayout -// -// Created by Luc Dion on 2018-04-13. -// Copyright © 2018 mcswiftlayyout.mirego.com. All rights reserved. -// - -import Foundation - -#if os(iOS) || os(tvOS) -import UIKit - -public extension UIView { - public var pin: PinLayout { - return PinLayoutImpl(view: self, keepTransform: true) - } - - public var pinFrame: PinLayout { - return PinLayoutImpl(view: self, keepTransform: false) - } - - @objc public var anchor: AnchorList { - return AnchorListImpl(view: self) - } - - @objc public var edge: EdgeList { - return EdgeListImpl(view: self) - } -} -#endif diff --git a/TestProjects/carthage/ios/Cartfile b/TestProjects/carthage/ios/Cartfile index 0fde5dad..06800e2f 100644 --- a/TestProjects/carthage/ios/Cartfile +++ b/TestProjects/carthage/ios/Cartfile @@ -1 +1 @@ -github "mirego/PinLayout" +git "file:////Users/lucdion/dev_luc/PinLayout" diff --git a/TestProjects/carthage/ios/PinLayout-Carthage-iOS.xcodeproj/project.pbxproj b/TestProjects/carthage/ios/PinLayout-Carthage-iOS.xcodeproj/project.pbxproj index cde84b34..3ec5baf4 100644 --- a/TestProjects/carthage/ios/PinLayout-Carthage-iOS.xcodeproj/project.pbxproj +++ b/TestProjects/carthage/ios/PinLayout-Carthage-iOS.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ @@ -13,8 +13,8 @@ 24C873041EE2D113007BFE47 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 24C873021EE2D113007BFE47 /* Main.storyboard */; }; 24C873061EE2D113007BFE47 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 24C873051EE2D113007BFE47 /* Assets.xcassets */; }; 24C873091EE2D113007BFE47 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 24C873071EE2D113007BFE47 /* LaunchScreen.storyboard */; }; - DF98DA62208B566100C86E2D /* PinLayout.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF98DA60208B565400C86E2D /* PinLayout.framework */; }; - DF98DA63208B566100C86E2D /* PinLayout.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = DF98DA60208B565400C86E2D /* PinLayout.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + DFEB52C326514E1A00E952CE /* PinLayout.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFEB52C226514E1A00E952CE /* PinLayout.xcframework */; }; + DFEB52C426514E1A00E952CE /* PinLayout.xcframework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = DFEB52C226514E1A00E952CE /* PinLayout.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -24,7 +24,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - DF98DA63208B566100C86E2D /* PinLayout.framework in Copy Frameworks */, + DFEB52C426514E1A00E952CE /* PinLayout.xcframework in Copy Frameworks */, ); name = "Copy Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -42,6 +42,7 @@ 24C873081EE2D113007BFE47 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 24C8730A1EE2D113007BFE47 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DF98DA60208B565400C86E2D /* PinLayout.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PinLayout.framework; path = Carthage/Build/iOS/PinLayout.framework; sourceTree = SOURCE_ROOT; }; + DFEB52C226514E1A00E952CE /* PinLayout.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = PinLayout.xcframework; path = Carthage/Build/PinLayout.xcframework; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -49,7 +50,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - DF98DA62208B566100C86E2D /* PinLayout.framework in Frameworks */, + DFEB52C326514E1A00E952CE /* PinLayout.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -91,6 +92,7 @@ DF98DA59208B538700C86E2D /* Frameworks */ = { isa = PBXGroup; children = ( + DFEB52C226514E1A00E952CE /* PinLayout.xcframework */, DF98DA60208B565400C86E2D /* PinLayout.framework */, ); path = Frameworks; @@ -124,8 +126,8 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0830; - ORGANIZATIONNAME = Mirego; + LastUpgradeCheck = 1100; + ORGANIZATIONNAME = Layoutbox; TargetAttributes = { 24C872FA1EE2D113007BFE47 = { CreatedOnToolsVersion = 8.3.2; @@ -136,7 +138,7 @@ }; buildConfigurationList = 24C872F61EE2D113007BFE47 /* Build configuration list for PBXProject "PinLayout-Carthage-iOS" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -202,21 +204,30 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 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_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -240,12 +251,13 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -254,21 +266,30 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 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_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -286,10 +307,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -300,18 +323,21 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = X2BTQWMK8M; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", ); INFOPLIST_FILE = "PinLayout-Carthage-iOS/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.mirego.project.PinLayout-Carthage-iOS"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.layoutbox.project.PinLayout-Carthage-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "PinLayout-Carthage-iOS/PinLayout-Carthage-iOS-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -320,17 +346,20 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = X2BTQWMK8M; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", ); INFOPLIST_FILE = "PinLayout-Carthage-iOS/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.mirego.project.PinLayout-Carthage-iOS"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.layoutbox.project.PinLayout-Carthage-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "PinLayout-Carthage-iOS/PinLayout-Carthage-iOS-Bridging-Header.h"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/TestProjects/carthage/ios/PinLayout-Carthage-iOS/AppDelegate.swift b/TestProjects/carthage/ios/PinLayout-Carthage-iOS/AppDelegate.swift index a3b85c31..713f035f 100644 --- a/TestProjects/carthage/ios/PinLayout-Carthage-iOS/AppDelegate.swift +++ b/TestProjects/carthage/ios/PinLayout-Carthage-iOS/AppDelegate.swift @@ -1,10 +1,3 @@ -// -// AppDelegate.swift -// PinLayoutCarthageTest -// -// Created by DION, Luc (MTL) on 2017-06-03. -// Copyright © 2017 Mirego. All rights reserved. -// import UIKit @@ -14,7 +7,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } diff --git a/TestProjects/carthage/ios/PinLayout-Carthage-iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/TestProjects/carthage/ios/PinLayout-Carthage-iOS/Assets.xcassets/AppIcon.appiconset/Contents.json index 36d2c80d..9221b9bb 100644 --- a/TestProjects/carthage/ios/PinLayout-Carthage-iOS/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/TestProjects/carthage/ios/PinLayout-Carthage-iOS/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -2,67 +2,97 @@ "images" : [ { "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" + "scale" : "2x", + "size" : "20x20" }, { "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" + "scale" : "3x", + "size" : "20x20" }, { "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" + "scale" : "2x", + "size" : "29x29" }, { "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" + "scale" : "3x", + "size" : "29x29" }, { "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" + "scale" : "2x", + "size" : "40x40" }, { "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" }, { "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" + "scale" : "2x", + "size" : "20x20" }, { "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" + "scale" : "1x", + "size" : "29x29" }, { "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" + "scale" : "2x", + "size" : "29x29" }, { "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" + "scale" : "1x", + "size" : "40x40" }, { "idiom" : "ipad", - "size" : "76x76", - "scale" : "1x" + "scale" : "2x", + "size" : "40x40" }, { "idiom" : "ipad", - "size" : "76x76", - "scale" : "2x" + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/TestProjects/carthage/ios/PinLayout-Carthage-iOS/TestObjectiveC.m b/TestProjects/carthage/ios/PinLayout-Carthage-iOS/TestObjectiveC.m index 636fb5fd..6d612a7f 100644 --- a/TestProjects/carthage/ios/PinLayout-Carthage-iOS/TestObjectiveC.m +++ b/TestProjects/carthage/ios/PinLayout-Carthage-iOS/TestObjectiveC.m @@ -1,10 +1,3 @@ -// -// TestObjectiveC.m -// PinLayoutPodTester -// -// Created by DION, Luc (MTL) on 2017-10-12. -// Copyright © 2017 Mirego. All rights reserved. -// #import @@ -52,10 +45,10 @@ - (id)initWithFrame:(CGRect)frame { - (void) layoutSubviews { [super layoutSubviews]; - [[[[[[logo.pinObjc top] left] width:100] aspectRatio] marginWithTop:topLayoutGuide + 10 horizontal:10 bottom:10] layout]; - [[[[segmented.pinObjc rightOf:logo aligned:VerticalAlignTop] right] marginHorizontal:10] layout]; - [[[[[[textLabel.pinObjc belowOf:segmented aligned:HorizontalAlignLeft] widthOf:segmented] pinEdges] marginTop:10] fitSize] layout]; - [[[[[separatorView.pinObjc belowOfViews:@[logo, textLabel] aligned:HorizontalAlignLeft] rightTo:segmented.edge.right] height:1] marginTop:10] layout]; + logo.pinObjc.top().left().width(100).aspectRatio().marginTHB(topLayoutGuide + 10, 10, 10).layout(); + segmented.pinObjc.rightOfAligned(logo, VerticalAlignTop).right().marginHorizontal(10).layout(); + textLabel.pinObjc.belowOfAligned(segmented, HorizontalAlignLeft).widthOf(segmented).pinEdges().marginTop(10).sizeToFitType(FitWidth).layout(); + separatorView.pinObjc.belowOfViewsAligned(@[logo, textLabel], HorizontalAlignLeft).rightToEdge(segmented.edge.right).height(1).marginTop(10).layout(); } @end diff --git a/TestProjects/carthage/ios/PinLayout-Carthage-iOS/ViewController.swift b/TestProjects/carthage/ios/PinLayout-Carthage-iOS/ViewController.swift index e47536ee..f49e4e77 100644 --- a/TestProjects/carthage/ios/PinLayout-Carthage-iOS/ViewController.swift +++ b/TestProjects/carthage/ios/PinLayout-Carthage-iOS/ViewController.swift @@ -1,10 +1,3 @@ -// -// ViewController.swift -// PinLayoutCarthageTest -// -// Created by DION, Luc (MTL) on 2017-06-03. -// Copyright © 2017 Mirego. All rights reserved. -// import UIKit import PinLayout diff --git a/TestProjects/cocoapods/ios/PinLayout-iOS.xcodeproj/project.pbxproj b/TestProjects/cocoapods/ios/PinLayout-iOS.xcodeproj/project.pbxproj index fca52c90..b86faf23 100644 --- a/TestProjects/cocoapods/ios/PinLayout-iOS.xcodeproj/project.pbxproj +++ b/TestProjects/cocoapods/ios/PinLayout-iOS.xcodeproj/project.pbxproj @@ -122,19 +122,19 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0830; - ORGANIZATIONNAME = Mirego; + LastUpgradeCheck = 1420; + ORGANIZATIONNAME = LayoutBox; TargetAttributes = { 24C872DC1EE2CE0B007BFE47 = { CreatedOnToolsVersion = 8.3.2; - LastSwiftMigration = 0900; + LastSwiftMigration = 1100; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 24C872D81EE2CE0B007BFE47 /* Build configuration list for PBXProject "PinLayout-iOS" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -170,16 +170,18 @@ files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-PinLayout-iOS/Pods-PinLayout-iOS-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-PinLayout-iOS/Pods-PinLayout-iOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/PinLayout/PinLayout.framework", + "${BUILT_PRODUCTS_DIR}/SwifterSwift/SwifterSwift.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PinLayout.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwifterSwift.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PinLayout-iOS/Pods-PinLayout-iOS-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PinLayout-iOS/Pods-PinLayout-iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 5FB587B98FA7C73E8C04570F /* [CP] Check Pods Manifest.lock */ = { @@ -239,21 +241,31 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 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_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_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; @@ -276,7 +288,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -290,21 +302,31 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 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_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_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; @@ -321,7 +343,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -338,12 +360,13 @@ CLANG_ENABLE_MODULES = YES; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "PinLayout-iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.mirego.project.PinLayout-iOS"; + PRODUCT_BUNDLE_IDENTIFIER = "com.layoutbox.project.PinLayout-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "PinLayout-iOS/PinLayout-iOS-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -355,11 +378,12 @@ CLANG_ENABLE_MODULES = YES; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "PinLayout-iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.mirego.project.PinLayout-iOS"; + PRODUCT_BUNDLE_IDENTIFIER = "com.layoutbox.project.PinLayout-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "PinLayout-iOS/PinLayout-iOS-Bridging-Header.h"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/TestProjects/cocoapods/ios/PinLayout-iOS/AppDelegate.swift b/TestProjects/cocoapods/ios/PinLayout-iOS/AppDelegate.swift index 806d5e21..713f035f 100644 --- a/TestProjects/cocoapods/ios/PinLayout-iOS/AppDelegate.swift +++ b/TestProjects/cocoapods/ios/PinLayout-iOS/AppDelegate.swift @@ -1,10 +1,3 @@ -// -// AppDelegate.swift -// PinLayoutPodTester -// -// Created by DION, Luc (MTL) on 2017-06-03. -// Copyright © 2017 Mirego. All rights reserved. -// import UIKit @@ -14,7 +7,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } diff --git a/TestProjects/cocoapods/ios/PinLayout-iOS/TestObjectiveC.m b/TestProjects/cocoapods/ios/PinLayout-iOS/TestObjectiveC.m index 636fb5fd..6d612a7f 100644 --- a/TestProjects/cocoapods/ios/PinLayout-iOS/TestObjectiveC.m +++ b/TestProjects/cocoapods/ios/PinLayout-iOS/TestObjectiveC.m @@ -1,10 +1,3 @@ -// -// TestObjectiveC.m -// PinLayoutPodTester -// -// Created by DION, Luc (MTL) on 2017-10-12. -// Copyright © 2017 Mirego. All rights reserved. -// #import @@ -52,10 +45,10 @@ - (id)initWithFrame:(CGRect)frame { - (void) layoutSubviews { [super layoutSubviews]; - [[[[[[logo.pinObjc top] left] width:100] aspectRatio] marginWithTop:topLayoutGuide + 10 horizontal:10 bottom:10] layout]; - [[[[segmented.pinObjc rightOf:logo aligned:VerticalAlignTop] right] marginHorizontal:10] layout]; - [[[[[[textLabel.pinObjc belowOf:segmented aligned:HorizontalAlignLeft] widthOf:segmented] pinEdges] marginTop:10] fitSize] layout]; - [[[[[separatorView.pinObjc belowOfViews:@[logo, textLabel] aligned:HorizontalAlignLeft] rightTo:segmented.edge.right] height:1] marginTop:10] layout]; + logo.pinObjc.top().left().width(100).aspectRatio().marginTHB(topLayoutGuide + 10, 10, 10).layout(); + segmented.pinObjc.rightOfAligned(logo, VerticalAlignTop).right().marginHorizontal(10).layout(); + textLabel.pinObjc.belowOfAligned(segmented, HorizontalAlignLeft).widthOf(segmented).pinEdges().marginTop(10).sizeToFitType(FitWidth).layout(); + separatorView.pinObjc.belowOfViewsAligned(@[logo, textLabel], HorizontalAlignLeft).rightToEdge(segmented.edge.right).height(1).marginTop(10).layout(); } @end diff --git a/TestProjects/cocoapods/ios/PinLayout-iOS/ViewController.swift b/TestProjects/cocoapods/ios/PinLayout-iOS/ViewController.swift index e39e8ce0..5972f7d3 100644 --- a/TestProjects/cocoapods/ios/PinLayout-iOS/ViewController.swift +++ b/TestProjects/cocoapods/ios/PinLayout-iOS/ViewController.swift @@ -1,10 +1,3 @@ -// -// ViewController.swift -// PinLayoutPodTester -// -// Created by DION, Luc (MTL) on 2017-06-03. -// Copyright © 2017 Mirego. All rights reserved. -// import UIKit import PinLayout @@ -44,7 +37,7 @@ class ViewController: UIViewController { super.viewDidLayoutSubviews() // PinLayout - logo.pin.top().left().size(100).margin(topLayoutGuide.length + 10, 10, 10) + logo.pin.top().left().size(100).margin(view.safeAreaInsets.top + 10, 10, 10) label1.pin.after(of: logo, aligned: .top).right().marginHorizontal(10).sizeToFit(.width) label2.pin.after(of: logo, aligned: .center).right().marginHorizontal(10).sizeToFit(.width) label3.pin.after(of: logo, aligned: .bottom).right().marginHorizontal(10).sizeToFit(.width) diff --git a/TestProjects/cocoapods/ios/Podfile b/TestProjects/cocoapods/ios/Podfile index 85213eec..2836a0dd 100644 --- a/TestProjects/cocoapods/ios/Podfile +++ b/TestProjects/cocoapods/ios/Podfile @@ -1,11 +1,15 @@ -source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +source 'https://cdn.cocoapods.org/' +platform :ios, '12.0' use_frameworks! target 'PinLayout-iOS' do #pod 'PinLayout' - #pod 'PinLayout', :git => 'https://github.com/mirego/PinLayout.git', :commit => 'f29519b08890da144772fe9b082cd6499b5d82c1' + #pod 'PinLayout', :git => 'https://github.com/layoutBox/PinLayout.git', :commit => 'f29519b08890da144772fe9b082cd6499b5d82c1' pod 'PinLayout', :path => '../../..' + + + # Include popular libraries that include UIKit classes extensions to check conflicts with PinLayout. + pod 'SwifterSwift' end diff --git a/TestProjects/cocoapods/ios/Podfile.lock b/TestProjects/cocoapods/ios/Podfile.lock new file mode 100644 index 00000000..adee1935 --- /dev/null +++ b/TestProjects/cocoapods/ios/Podfile.lock @@ -0,0 +1,47 @@ +PODS: + - PinLayout (1.10.4) + - SwifterSwift (5.2.0): + - SwifterSwift/AppKit (= 5.2.0) + - SwifterSwift/CoreAnimation (= 5.2.0) + - SwifterSwift/CoreGraphics (= 5.2.0) + - SwifterSwift/CoreLocation (= 5.2.0) + - SwifterSwift/Dispatch (= 5.2.0) + - SwifterSwift/Foundation (= 5.2.0) + - SwifterSwift/MapKit (= 5.2.0) + - SwifterSwift/SceneKit (= 5.2.0) + - SwifterSwift/SpriteKit (= 5.2.0) + - SwifterSwift/StoreKit (= 5.2.0) + - SwifterSwift/SwiftStdlib (= 5.2.0) + - SwifterSwift/UIKit (= 5.2.0) + - SwifterSwift/AppKit (5.2.0) + - SwifterSwift/CoreAnimation (5.2.0) + - SwifterSwift/CoreGraphics (5.2.0) + - SwifterSwift/CoreLocation (5.2.0) + - SwifterSwift/Dispatch (5.2.0) + - SwifterSwift/Foundation (5.2.0) + - SwifterSwift/MapKit (5.2.0) + - SwifterSwift/SceneKit (5.2.0) + - SwifterSwift/SpriteKit (5.2.0) + - SwifterSwift/StoreKit (5.2.0) + - SwifterSwift/SwiftStdlib (5.2.0) + - SwifterSwift/UIKit (5.2.0) + +DEPENDENCIES: + - PinLayout (from `../../..`) + - SwifterSwift + +SPEC REPOS: + trunk: + - SwifterSwift + +EXTERNAL SOURCES: + PinLayout: + :path: "../../.." + +SPEC CHECKSUMS: + PinLayout: f8a677ce0cd1cfe96b58435d029b4ceb4ce9c04c + SwifterSwift: 334181863c416882d97b7a60c05054d9e4d799e2 + +PODFILE CHECKSUM: f40959ab258f12059918e48f6019cb401a81723f + +COCOAPODS: 1.12.0 diff --git a/TestProjects/cocoapods/macos/PinLayout-macOS.xcodeproj/project.pbxproj b/TestProjects/cocoapods/macos/PinLayout-macOS.xcodeproj/project.pbxproj index f45370fa..2be9d50f 100644 --- a/TestProjects/cocoapods/macos/PinLayout-macOS.xcodeproj/project.pbxproj +++ b/TestProjects/cocoapods/macos/PinLayout-macOS.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 447D4B571EA571EC002FDFF4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 447D4B561EA571EC002FDFF4 /* Assets.xcassets */; }; D481169C663D06B9487BD94A /* Pods_PinLayout_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD1FED5769AD72F9C3BA80B4 /* Pods_PinLayout_macOS.framework */; }; DF47E42B2088F8B2008599A2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DF47E42A2088F8B2008599A2 /* Main.storyboard */; }; + DFF222C520B9932600AC2A84 /* TestObjectiveC.m in Sources */ = {isa = PBXBuildFile; fileRef = DFF222C420B9932600AC2A84 /* TestObjectiveC.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -22,6 +23,8 @@ 5ECB97E9B35D047EBF30646A /* Pods-PinLayout-macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinLayout-macOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-PinLayout-macOS/Pods-PinLayout-macOS.release.xcconfig"; sourceTree = ""; }; AD1FED5769AD72F9C3BA80B4 /* Pods_PinLayout_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PinLayout_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DF47E42A2088F8B2008599A2 /* Main.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + DFF222BF20B9920E00AC2A84 /* PinLayout-macOS-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PinLayout-macOS-Bridging-Header.h"; sourceTree = ""; }; + DFF222C420B9932600AC2A84 /* TestObjectiveC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestObjectiveC.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -69,6 +72,8 @@ 447D4B561EA571EC002FDFF4 /* Assets.xcassets */, 447D4B5B1EA571EC002FDFF4 /* Info.plist */, DF47E42A2088F8B2008599A2 /* Main.storyboard */, + DFF222C420B9932600AC2A84 /* TestObjectiveC.m */, + DFF222BF20B9920E00AC2A84 /* PinLayout-macOS-Bridging-Header.h */, ); path = "PinLayout-macOS"; sourceTree = ""; @@ -111,18 +116,19 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0830; + LastUpgradeCheck = 1420; ORGANIZATIONNAME = Linkedin; TargetAttributes = { 447D4B4E1EA571EC002FDFF4 = { CreatedOnToolsVersion = 8.3.1; + LastSwiftMigration = 0930; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 447D4B4A1EA571EC002FDFF4 /* Build configuration list for PBXProject "PinLayout-macOS" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -175,7 +181,7 @@ files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-PinLayout-macOS/Pods-PinLayout-macOS-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-PinLayout-macOS/Pods-PinLayout-macOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/PinLayout/PinLayout.framework", ); name = "[CP] Embed Pods Frameworks"; @@ -184,7 +190,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PinLayout-macOS/Pods-PinLayout-macOS-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PinLayout-macOS/Pods-PinLayout-macOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -195,6 +201,7 @@ buildActionMask = 2147483647; files = ( 447D4B531EA571EC002FDFF4 /* AppDelegate.swift in Sources */, + DFF222C520B9932600AC2A84 /* TestObjectiveC.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -205,26 +212,37 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 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_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_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 = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -242,12 +260,13 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -255,26 +274,37 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 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_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_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 = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -286,10 +316,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -298,13 +329,18 @@ baseConfigurationReference = 039104F30404DFF00E9EE3EC /* Pods-PinLayout-macOS.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; INFOPLIST_FILE = "PinLayout-macOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.11; - PRODUCT_BUNDLE_IDENTIFIER = "com.mirego.project.PinLayout-macOS"; + MACOSX_DEPLOYMENT_TARGET = 11.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.layoutbox.project.PinLayout-macOS"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_OBJC_BRIDGING_HEADER = "PinLayout-macOS/PinLayout-macOS-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -313,13 +349,17 @@ baseConfigurationReference = 5ECB97E9B35D047EBF30646A /* Pods-PinLayout-macOS.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; INFOPLIST_FILE = "PinLayout-macOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.11; - PRODUCT_BUNDLE_IDENTIFIER = "com.mirego.project.PinLayout-macOS"; + MACOSX_DEPLOYMENT_TARGET = 11.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.layoutbox.project.PinLayout-macOS"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_OBJC_BRIDGING_HEADER = "PinLayout-macOS/PinLayout-macOS-Bridging-Header.h"; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/TestProjects/cocoapods/macos/PinLayout-macOS.xcodeproj/xcshareddata/xcschemes/PinLayout-macOS.xcscheme b/TestProjects/cocoapods/macos/PinLayout-macOS.xcodeproj/xcshareddata/xcschemes/PinLayout-macOS.xcscheme index 7cf4561a..1b95cac8 100644 --- a/TestProjects/cocoapods/macos/PinLayout-macOS.xcodeproj/xcshareddata/xcschemes/PinLayout-macOS.xcscheme +++ b/TestProjects/cocoapods/macos/PinLayout-macOS.xcodeproj/xcshareddata/xcschemes/PinLayout-macOS.xcscheme @@ -1,6 +1,6 @@ - - - - + + - - + +@import AppKit; +@import PinLayout; + +@interface IntroObjectiveCView: NSView { +} +@end + +@implementation IntroObjectiveCView { + CGFloat topLayoutGuide; + NSImageView* logo; + NSView* separatorView; +} + +- (id)initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame])) { + topLayoutGuide = 0; + + logo = [[NSImageView alloc] init]; + separatorView = [[NSView alloc] init]; + } + return self; +} + +- (void)layout { + [super layout]; + + logo.pinObjc.top().left().width(100).marginTHB(topLayoutGuide + 10, 10, 10).layout(); + separatorView.pinObjc.belowOfAligned(logo, HorizontalAlignLeft).height(1).marginTop(10).layout(); +} + +@end diff --git a/TestProjects/cocoapods/macos/Podfile b/TestProjects/cocoapods/macos/Podfile index 9b3c008e..95307e61 100755 --- a/TestProjects/cocoapods/macos/Podfile +++ b/TestProjects/cocoapods/macos/Podfile @@ -1,11 +1,11 @@ -source 'https://github.com/CocoaPods/Specs.git' -platform :osx, '10.11' +source 'https://cdn.cocoapods.org/' +platform :osx, '10.13' use_frameworks! target 'PinLayout-macOS' do #pod 'PinLayout' - #pod 'PinLayout', :git => 'https://github.com/mirego/PinLayout.git', :commit => 'f29519b08890da144772fe9b082cd6499b5d82c1' + #pod 'PinLayout', :git => 'https://github.com/layoutBox/PinLayout.git', :commit => 'f29519b08890da144772fe9b082cd6499b5d82c1' pod 'PinLayout', :path => '../../..' end diff --git a/TestProjects/cocoapods/macos/Podfile.lock b/TestProjects/cocoapods/macos/Podfile.lock new file mode 100644 index 00000000..d2828731 --- /dev/null +++ b/TestProjects/cocoapods/macos/Podfile.lock @@ -0,0 +1,16 @@ +PODS: + - PinLayout (1.10.4) + +DEPENDENCIES: + - PinLayout (from `../../..`) + +EXTERNAL SOURCES: + PinLayout: + :path: "../../.." + +SPEC CHECKSUMS: + PinLayout: 9b3aacc4a5a7c942a8ef6b42b254a448fbf422e5 + +PODFILE CHECKSUM: 6f367e85dc6a99ba93382237d1ecc5f5475c5e70 + +COCOAPODS: 1.12.0 diff --git a/TestProjects/cocoapods/tvos/PinLayout-tvOS.xcodeproj/project.pbxproj b/TestProjects/cocoapods/tvos/PinLayout-tvOS.xcodeproj/project.pbxproj index 6bef9549..d5fc9763 100644 --- a/TestProjects/cocoapods/tvos/PinLayout-tvOS.xcodeproj/project.pbxproj +++ b/TestProjects/cocoapods/tvos/PinLayout-tvOS.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 448160371EA5A76A00FBA809 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 448160361EA5A76A00FBA809 /* AppDelegate.swift */; }; DF4C0F5A2088FBAA00B4C060 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DF4C0F592088FBAA00B4C060 /* Main.storyboard */; }; DF4C0F5C2088FBC300B4C060 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF4C0F5B2088FBC300B4C060 /* ViewController.swift */; }; + DFF222C720B993A900AC2A84 /* TestObjectiveC.m in Sources */ = {isa = PBXBuildFile; fileRef = DFF222C620B993A900AC2A84 /* TestObjectiveC.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -22,6 +23,8 @@ 779A6A4C5B9EC1AC279D6A24 /* libPods-PinLayout-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-PinLayout-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; DF4C0F592088FBAA00B4C060 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; DF4C0F5B2088FBC300B4C060 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + DFF222BC20B991CF00AC2A84 /* PinLayout-tvOS-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PinLayout-tvOS-Bridging-Header.h"; sourceTree = ""; }; + DFF222C620B993A900AC2A84 /* TestObjectiveC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestObjectiveC.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -60,7 +63,9 @@ 448160361EA5A76A00FBA809 /* AppDelegate.swift */, DF4C0F592088FBAA00B4C060 /* Main.storyboard */, DF4C0F5B2088FBC300B4C060 /* ViewController.swift */, + DFF222C620B993A900AC2A84 /* TestObjectiveC.m */, 4481603F1EA5A76B00FBA809 /* Info.plist */, + DFF222BC20B991CF00AC2A84 /* PinLayout-tvOS-Bridging-Header.h */, ); path = "PinLayout-tvOS"; sourceTree = ""; @@ -110,18 +115,19 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0830; + LastUpgradeCheck = 1100; ORGANIZATIONNAME = Linkedin; TargetAttributes = { 448160321EA5A76A00FBA809 = { CreatedOnToolsVersion = 8.3.1; + LastSwiftMigration = 1100; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 4481602E1EA5A76A00FBA809 /* Build configuration list for PBXProject "PinLayout-tvOS" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -175,6 +181,7 @@ buildActionMask = 2147483647; files = ( DF4C0F5C2088FBC300B4C060 /* ViewController.swift in Sources */, + DFF222C720B993A900AC2A84 /* TestObjectiveC.m in Sources */, 448160371EA5A76A00FBA809 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -186,21 +193,30 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 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_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -236,21 +252,30 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 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_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -279,12 +304,15 @@ isa = XCBuildConfiguration; baseConfigurationReference = 3ACDA03AB2AD6F178CB4A934 /* Pods-PinLayout-tvOS.debug.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "PinLayout-tvOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.mirego.project.PinLayout-tvOS"; + PRODUCT_BUNDLE_IDENTIFIER = "com.layoutbox.project.PinLayout-tvOS"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_OBJC_BRIDGING_HEADER = "PinLayout-tvOS/PinLayout-tvOS-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -292,12 +320,14 @@ isa = XCBuildConfiguration; baseConfigurationReference = 6D86E0FD61D49B5CC904E75C /* Pods-PinLayout-tvOS.release.xcconfig */; buildSettings = { + CLANG_ENABLE_MODULES = YES; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "PinLayout-tvOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.mirego.project.PinLayout-tvOS"; + PRODUCT_BUNDLE_IDENTIFIER = "com.layoutbox.project.PinLayout-tvOS"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_OBJC_BRIDGING_HEADER = "PinLayout-tvOS/PinLayout-tvOS-Bridging-Header.h"; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/TestProjects/cocoapods/tvos/PinLayout-tvOS.xcodeproj/xcshareddata/xcschemes/PinLayout-tvOS.xcscheme b/TestProjects/cocoapods/tvos/PinLayout-tvOS.xcodeproj/xcshareddata/xcschemes/PinLayout-tvOS.xcscheme index 0f9035a9..b25f8ec0 100644 --- a/TestProjects/cocoapods/tvos/PinLayout-tvOS.xcodeproj/xcshareddata/xcschemes/PinLayout-tvOS.xcscheme +++ b/TestProjects/cocoapods/tvos/PinLayout-tvOS.xcodeproj/xcshareddata/xcschemes/PinLayout-tvOS.xcscheme @@ -1,6 +1,6 @@ - - - - + + - - Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } diff --git a/TestProjects/cocoapods/tvos/PinLayout-tvOS/PinLayout-tvOS-Bridging-Header.h b/TestProjects/cocoapods/tvos/PinLayout-tvOS/PinLayout-tvOS-Bridging-Header.h new file mode 100644 index 00000000..1b2cb5d6 --- /dev/null +++ b/TestProjects/cocoapods/tvos/PinLayout-tvOS/PinLayout-tvOS-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/TestProjects/cocoapods/tvos/PinLayout-tvOS/TestObjectiveC.m b/TestProjects/cocoapods/tvos/PinLayout-tvOS/TestObjectiveC.m new file mode 100644 index 00000000..6d612a7f --- /dev/null +++ b/TestProjects/cocoapods/tvos/PinLayout-tvOS/TestObjectiveC.m @@ -0,0 +1,54 @@ + +#import + +@import UIKit; +@import PinLayout; + +@interface IntroObjectiveCView: UIView { +} +@end + +@implementation IntroObjectiveCView { + CGFloat topLayoutGuide; + UIImageView* logo; + UISegmentedControl* segmented; + UILabel* textLabel; + UIView* separatorView; +} + +- (id)initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame])) { + topLayoutGuide = 0; + self.backgroundColor = UIColor.whiteColor; + + logo = [[UIImageView alloc] initWithImage: [UIImage imageNamed:@"PinLayout-logo" inBundle:nil compatibleWithTraitCollection:nil]]; + [self addSubview:logo]; + + segmented = [[UISegmentedControl alloc] initWithItems: @[@"Intro", @"1", @"2"]]; + [self addSubview:segmented]; + + textLabel = [[UILabel alloc] init]; + textLabel.text = @"Swift manual views layouting without auto layout, no magic, pure code, full control. Concise syntax, readable & chainable.\n\nSwift manual views layouting without auto layout, no magic, pure code, full control. Concise syntax, readable & chainable."; + textLabel.font = [UIFont systemFontOfSize:14]; + textLabel.numberOfLines = 0; + textLabel.lineBreakMode = NSLineBreakByWordWrapping; + [self addSubview:textLabel]; + + separatorView = [[UIView alloc] init]; + separatorView.backgroundColor = UIColor.grayColor; + + [self addSubview:separatorView]; + } + return self; +} + +- (void) layoutSubviews { + [super layoutSubviews]; + + logo.pinObjc.top().left().width(100).aspectRatio().marginTHB(topLayoutGuide + 10, 10, 10).layout(); + segmented.pinObjc.rightOfAligned(logo, VerticalAlignTop).right().marginHorizontal(10).layout(); + textLabel.pinObjc.belowOfAligned(segmented, HorizontalAlignLeft).widthOf(segmented).pinEdges().marginTop(10).sizeToFitType(FitWidth).layout(); + separatorView.pinObjc.belowOfViewsAligned(@[logo, textLabel], HorizontalAlignLeft).rightToEdge(segmented.edge.right).height(1).marginTop(10).layout(); +} + +@end diff --git a/TestProjects/cocoapods/tvos/PinLayout-tvOS/ViewController.swift b/TestProjects/cocoapods/tvos/PinLayout-tvOS/ViewController.swift index e6ce68b0..c6eeb0ca 100644 --- a/TestProjects/cocoapods/tvos/PinLayout-tvOS/ViewController.swift +++ b/TestProjects/cocoapods/tvos/PinLayout-tvOS/ViewController.swift @@ -1,10 +1,3 @@ -// -// ViewController.swift -// PinLayoutTvOsExample -// -// Created by DION, Luc (MTL) on 2017-06-16. -// Copyright © 2017 Mirego. All rights reserved. -// import UIKit import PinLayout diff --git a/TestProjects/cocoapods/tvos/Podfile b/TestProjects/cocoapods/tvos/Podfile index 26a9ba5e..226b3328 100755 --- a/TestProjects/cocoapods/tvos/Podfile +++ b/TestProjects/cocoapods/tvos/Podfile @@ -1,9 +1,9 @@ -source 'https://github.com/CocoaPods/Specs.git' -platform :tvos, '10.3' +source 'https://cdn.cocoapods.org/' +platform :tvos, '12.0' target 'PinLayout-tvOS' do #pod 'PinLayout' - #pod 'PinLayout', :git => 'https://github.com/mirego/PinLayout.git', :commit => 'f29519b08890da144772fe9b082cd6499b5d82c1' + #pod 'PinLayout', :git => 'https://github.com/layoutBox/PinLayout.git', :commit => 'f29519b08890da144772fe9b082cd6499b5d82c1' pod 'PinLayout', :path => '../../..' end diff --git a/TestProjects/cocoapods/tvos/Podfile.lock b/TestProjects/cocoapods/tvos/Podfile.lock new file mode 100644 index 00000000..477ceb25 --- /dev/null +++ b/TestProjects/cocoapods/tvos/Podfile.lock @@ -0,0 +1,16 @@ +PODS: + - PinLayout (1.10.4) + +DEPENDENCIES: + - PinLayout (from `../../..`) + +EXTERNAL SOURCES: + PinLayout: + :path: "../../.." + +SPEC CHECKSUMS: + PinLayout: 9b3aacc4a5a7c942a8ef6b42b254a448fbf422e5 + +PODFILE CHECKSUM: f6f33c54b299d48dce16c8db06bb1624e146e6de + +COCOAPODS: 1.12.0 diff --git a/TestProjects/swift-package-manager/ios/-destination/Logs/Package/LogStoreManifest.plist b/TestProjects/swift-package-manager/ios/-destination/Logs/Package/LogStoreManifest.plist new file mode 100644 index 00000000..c8c83d19 --- /dev/null +++ b/TestProjects/swift-package-manager/ios/-destination/Logs/Package/LogStoreManifest.plist @@ -0,0 +1,10 @@ + + + + + logFormatVersion + 10 + logs + + + diff --git a/TestProjects/swift-package-manager/ios/-destination/Logs/Test/LogStoreManifest.plist b/TestProjects/swift-package-manager/ios/-destination/Logs/Test/LogStoreManifest.plist new file mode 100644 index 00000000..c8c83d19 --- /dev/null +++ b/TestProjects/swift-package-manager/ios/-destination/Logs/Test/LogStoreManifest.plist @@ -0,0 +1,10 @@ + + + + + logFormatVersion + 10 + logs + + + diff --git a/TestProjects/swift-package-manager/ios/-destination/info.plist b/TestProjects/swift-package-manager/ios/-destination/info.plist new file mode 100644 index 00000000..03df6ccb --- /dev/null +++ b/TestProjects/swift-package-manager/ios/-destination/info.plist @@ -0,0 +1,10 @@ + + + + + LastAccessedDate + 2021-05-18T15:33:02Z + WorkspacePath + /Users/lucdion/dev_luc/PinLayout/TestProjects/swift-package-manager/ios/PinLayout-Carthage-iOS.xcodeproj + + diff --git a/TestProjects/swift-package-manager/ios/Package.resolved b/TestProjects/swift-package-manager/ios/Package.resolved new file mode 100644 index 00000000..459a7892 --- /dev/null +++ b/TestProjects/swift-package-manager/ios/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "PinLayout", + "repositoryURL": "https://github.com/layoutBox/PinLayout.git", + "state": { + "branch": null, + "revision": "eb3e2a3b14954c89f0a6c31f8f0a00dbe3208121", + "version": "1.9.4" + } + } + ] + }, + "version": 1 +} diff --git a/TestProjects/swift-package-manager/ios/Package.swift b/TestProjects/swift-package-manager/ios/Package.swift new file mode 100644 index 00000000..d2c7990c --- /dev/null +++ b/TestProjects/swift-package-manager/ios/Package.swift @@ -0,0 +1,13 @@ +// swift-tools-version:5.4 +import PackageDescription + +let package = Package( + name: "PinLayoutSwiftPackageManagerTest", + defaultLocalization: "en", + dependencies: [ + .package(url: "https://github.com/layoutBox/PinLayout.git", from: "1.0.0") + ], + targets: [ + .target(name: "PinLayout-SPM-iOS", dependencies: ["PinLayout"]), + ] +) \ No newline at end of file diff --git a/TestProjects/swift-package-manager/ios/PinLayout-Carthage-iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/TestProjects/swift-package-manager/ios/PinLayout-Carthage-iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..1a37b72d --- /dev/null +++ b/TestProjects/swift-package-manager/ios/PinLayout-Carthage-iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "PinLayout", + "repositoryURL": "https://github.com/layoutBox/PinLayout.git", + "state": { + "branch": "master", + "revision": "c33d7aba9e4501886d35a9399bccf109a93ebde8", + "version": null + } + } + ] + }, + "version": 1 +} diff --git a/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS.xcodeproj/project.pbxproj b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS.xcodeproj/project.pbxproj new file mode 100644 index 00000000..7dbc7ede --- /dev/null +++ b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS.xcodeproj/project.pbxproj @@ -0,0 +1,367 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 52; + objects = { + +/* Begin PBXBuildFile section */ + 24CCACAF1EE8268C005CC365 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24CCACAE1EE8268C005CC365 /* AppDelegate.swift */; }; + 24CCACB11EE8268C005CC365 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24CCACB01EE8268C005CC365 /* ViewController.swift */; }; + 24CCACB41EE8268C005CC365 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 24CCACB21EE8268C005CC365 /* Main.storyboard */; }; + 24CCACB61EE8268C005CC365 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 24CCACB51EE8268C005CC365 /* Assets.xcassets */; }; + 24CCACB91EE8268C005CC365 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 24CCACB71EE8268C005CC365 /* LaunchScreen.storyboard */; }; + DFF182372654172B007E6386 /* PinLayout in Frameworks */ = {isa = PBXBuildFile; productRef = DFF182362654172B007E6386 /* PinLayout */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 24CCACAB1EE8268C005CC365 /* PinLayout-SPM-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PinLayout-SPM-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 24CCACAE1EE8268C005CC365 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 24CCACB01EE8268C005CC365 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 24CCACB31EE8268C005CC365 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 24CCACB51EE8268C005CC365 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 24CCACB81EE8268C005CC365 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 24CCACBA1EE8268C005CC365 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 24CCACA81EE8268C005CC365 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DFF182372654172B007E6386 /* PinLayout in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 24CCACA21EE8268C005CC365 = { + isa = PBXGroup; + children = ( + 24CCACAD1EE8268C005CC365 /* PinLayout-SPM-iOS */, + 24CCACAC1EE8268C005CC365 /* Products */, + ); + sourceTree = ""; + }; + 24CCACAC1EE8268C005CC365 /* Products */ = { + isa = PBXGroup; + children = ( + 24CCACAB1EE8268C005CC365 /* PinLayout-SPM-iOS.app */, + ); + name = Products; + sourceTree = ""; + }; + 24CCACAD1EE8268C005CC365 /* PinLayout-SPM-iOS */ = { + isa = PBXGroup; + children = ( + 24CCACAE1EE8268C005CC365 /* AppDelegate.swift */, + 24CCACB01EE8268C005CC365 /* ViewController.swift */, + 24CCACB21EE8268C005CC365 /* Main.storyboard */, + 24CCACB51EE8268C005CC365 /* Assets.xcassets */, + 24CCACB71EE8268C005CC365 /* LaunchScreen.storyboard */, + 24CCACBA1EE8268C005CC365 /* Info.plist */, + ); + path = "PinLayout-SPM-iOS"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 24CCACAA1EE8268C005CC365 /* PinLayout-SPM-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 24CCACBD1EE8268C005CC365 /* Build configuration list for PBXNativeTarget "PinLayout-SPM-iOS" */; + buildPhases = ( + 24CCACA71EE8268C005CC365 /* Sources */, + 24CCACA81EE8268C005CC365 /* Frameworks */, + 24CCACA91EE8268C005CC365 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "PinLayout-SPM-iOS"; + packageProductDependencies = ( + DFF182362654172B007E6386 /* PinLayout */, + ); + productName = PinLayoutSwiftPackageManagerTest; + productReference = 24CCACAB1EE8268C005CC365 /* PinLayout-SPM-iOS.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 24CCACA31EE8268C005CC365 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0830; + LastUpgradeCheck = 1250; + ORGANIZATIONNAME = layoutbox; + TargetAttributes = { + 24CCACAA1EE8268C005CC365 = { + CreatedOnToolsVersion = 8.3.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 24CCACA61EE8268C005CC365 /* Build configuration list for PBXProject "PinLayout-SPM-iOS" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 24CCACA21EE8268C005CC365; + packageReferences = ( + DFF182352654172B007E6386 /* XCRemoteSwiftPackageReference "PinLayout" */, + ); + productRefGroup = 24CCACAC1EE8268C005CC365 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 24CCACAA1EE8268C005CC365 /* PinLayout-SPM-iOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 24CCACA91EE8268C005CC365 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 24CCACB91EE8268C005CC365 /* LaunchScreen.storyboard in Resources */, + 24CCACB61EE8268C005CC365 /* Assets.xcassets in Resources */, + 24CCACB41EE8268C005CC365 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 24CCACA71EE8268C005CC365 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 24CCACB11EE8268C005CC365 /* ViewController.swift in Sources */, + 24CCACAF1EE8268C005CC365 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 24CCACB21EE8268C005CC365 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 24CCACB31EE8268C005CC365 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 24CCACB71EE8268C005CC365 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 24CCACB81EE8268C005CC365 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 24CCACBB1EE8268C005CC365 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + 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_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_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; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 24CCACBC1EE8268C005CC365 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + 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_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_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; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 24CCACBE1EE8268C005CC365 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = X2BTQWMK8M; + INFOPLIST_FILE = "$(SRCROOT)/PinLayout-SPM-iOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.layoutbox.project.PinLayout-Carthage-iOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 24CCACBF1EE8268C005CC365 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = X2BTQWMK8M; + INFOPLIST_FILE = "$(SRCROOT)/PinLayout-SPM-iOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.layoutbox.project.PinLayout-Carthage-iOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 24CCACA61EE8268C005CC365 /* Build configuration list for PBXProject "PinLayout-SPM-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 24CCACBB1EE8268C005CC365 /* Debug */, + 24CCACBC1EE8268C005CC365 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 24CCACBD1EE8268C005CC365 /* Build configuration list for PBXNativeTarget "PinLayout-SPM-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 24CCACBE1EE8268C005CC365 /* Debug */, + 24CCACBF1EE8268C005CC365 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + DFF182352654172B007E6386 /* XCRemoteSwiftPackageReference "PinLayout" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/layoutBox/PinLayout.git"; + requirement = { + branch = master; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + DFF182362654172B007E6386 /* PinLayout */ = { + isa = XCSwiftPackageProductDependency; + package = DFF182352654172B007E6386 /* XCRemoteSwiftPackageReference "PinLayout" */; + productName = PinLayout; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 24CCACA31EE8268C005CC365 /* Project object */; +} diff --git a/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..1a37b72d --- /dev/null +++ b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "PinLayout", + "repositoryURL": "https://github.com/layoutBox/PinLayout.git", + "state": { + "branch": "master", + "revision": "c33d7aba9e4501886d35a9399bccf109a93ebde8", + "version": null + } + } + ] + }, + "version": 1 +} diff --git a/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/AppDelegate.swift b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/AppDelegate.swift new file mode 100644 index 00000000..713f035f --- /dev/null +++ b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/AppDelegate.swift @@ -0,0 +1,39 @@ + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..36d2c80d --- /dev/null +++ b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/Base.lproj/LaunchScreen.storyboard b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..fdf3f97d --- /dev/null +++ b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/Base.lproj/Main.storyboard b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/Base.lproj/Main.storyboard new file mode 100644 index 00000000..273375fc --- /dev/null +++ b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/Info.plist b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/Info.plist new file mode 100644 index 00000000..d0524738 --- /dev/null +++ b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/ViewController.swift b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/ViewController.swift new file mode 100644 index 00000000..be2c3bbd --- /dev/null +++ b/TestProjects/swift-package-manager/ios/PinLayout-SPM-iOS/ViewController.swift @@ -0,0 +1,18 @@ + +import UIKit + +class ViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view, typically from a nib. + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + +} + diff --git a/TestProjects/swift-package-manager/ios/update_packages.sh b/TestProjects/swift-package-manager/ios/update_packages.sh new file mode 100755 index 00000000..f6b28e19 --- /dev/null +++ b/TestProjects/swift-package-manager/ios/update_packages.sh @@ -0,0 +1 @@ +rm -rf .build/ && rm Package.pins; swift package show-dependencies --format json && swift package show-dependencies --format json diff --git a/Tests/BasicView.swift b/Tests/BasicView.swift index 261e7b92..f89ab13a 100644 --- a/Tests/BasicView.swift +++ b/Tests/BasicView.swift @@ -23,9 +23,11 @@ import UIKit import AppKit #endif +import PinLayout + class BasicView: PView { #if os(macOS) - fileprivate var _isFlipped: Bool = true + private var _isFlipped: Bool = true override var isFlipped: Bool { return _isFlipped } @@ -53,15 +55,14 @@ class BasicView: PView { override func sizeThatFits(_ size: CGSize) -> CGSize { return _sizeThatFits(size) } - #else - func sizeThatFits(_ size: CGSize) -> CGSize { - return _sizeThatFits(size) - } #endif - fileprivate func _sizeThatFits(_ size: CGSize) -> CGSize { + private func _sizeThatFits(_ size: CGSize) -> CGSize { var newSize = CGSize() - if size.width != CGFloat.greatestFiniteMagnitude { + if size.width != CGFloat.greatestFiniteMagnitude && size.height != CGFloat.greatestFiniteMagnitude { + newSize.width = 35 + newSize.height = 45 + } else if size.width != CGFloat.greatestFiniteMagnitude { newSize.width = size.width + sizeThatFitSizeOffset newSize.height = sizeThatFitsExpectedArea / newSize.width } else if size.height != CGFloat.greatestFiniteMagnitude { @@ -75,3 +76,11 @@ class BasicView: PView { return newSize } } + +#if os(macOS) +extension BasicView: SizeCalculable { + func sizeThatFits(_ size: CGSize) -> CGSize { + return _sizeThatFits(size) + } +} +#endif diff --git a/Tests/Common/AdjustSizeSpec.swift b/Tests/Common/AdjustSizeSpec.swift index 6fa8cd23..521a06b8 100644 --- a/Tests/Common/AdjustSizeSpec.swift +++ b/Tests/Common/AdjustSizeSpec.swift @@ -44,11 +44,8 @@ class AdjustSizeSpec: QuickSpec { - bViewChild */ - beforeSuite { - _pinlayoutSetUnitTest(displayScale: 2) - } - beforeEach { + _pinlayoutSetUnitTest(scale: 2) Pin.lastWarningText = nil viewController = PViewController() @@ -77,6 +74,10 @@ class AdjustSizeSpec: QuickSpec { bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) } + afterEach { + _pinlayoutSetUnitTest(scale: nil) + } + describe("the result of the width(...) methods") { it("should adjust the width of aView") { aView.pin.width(35) @@ -113,6 +114,42 @@ class AdjustSizeSpec: QuickSpec { aView.pin.width(of: aViewChild) expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 50.0, height: 60.0))) } + + it("should accept a width of 0") { + aView.pin.width(0) + expect(Pin.lastWarningText).to(beNil()) + expect(aView.frame).to(equal(CGRect(x: 140, y: 100.0, width: 0.0, height: 60.0))) + } + + it("should warn about negative width value") { + aView.pin.width(-2) + expect(Pin.lastWarningText).to(contain(["width", "won't be applied", "the width", "must be greater than or equal to zero"])) + expect(aView.frame).to(equal(CGRect(x: 140, y: 100.0, width: 100.0, height: 60.0))) + } + + it("should warn about NaN width value") { + aView.pin.width(CGFloat.nan) + expect(Pin.lastWarningText).to(contain(["width", "nan", "won't be applied", "the width", "must be greater than or equal to zero"])) + expect(aView.frame).to(equal(CGRect(x: 140, y: 100.0, width: 100.0, height: 60.0))) + } + + it("should warn about -NaN width value") { + aView.pin.width(-CGFloat.nan) + expect(Pin.lastWarningText).to(contain(["width", "nan", "won't be applied", "the width", "must be greater than or equal to zero"])) + expect(aView.frame).to(equal(CGRect(x: 140, y: 100.0, width: 100.0, height: 60.0))) + } + + it("should warn about infinity width value") { + aView.pin.width(CGFloat.infinity) + expect(Pin.lastWarningText).to(contain(["width", "inf", "won't be applied", "the width", "must be greater than or equal to zero"])) + expect(aView.frame).to(equal(CGRect(x: 140, y: 100.0, width: 100.0, height: 60.0))) + } + + it("should warn about -infinity width value") { + aView.pin.width(-CGFloat.infinity) + expect(Pin.lastWarningText).to(contain(["width", "inf", "won't be applied", "the width", "must be greater than or equal to zero"])) + expect(aView.frame).to(equal(CGRect(x: 140, y: 100.0, width: 100.0, height: 60.0))) + } } describe("the result of the height(...) methods") { @@ -146,6 +183,42 @@ class AdjustSizeSpec: QuickSpec { aView.pin.height(of: aViewChild) expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 100.0, height: 30.0))) } + + it("should accept a height of 0") { + aView.pin.height(0) + expect(Pin.lastWarningText).to(beNil()) + expect(aView.frame).to(equal(CGRect(x: 140, y: 100.0, width: 100.0, height: 0.0))) + } + + it("should warn about negative height value") { + aView.pin.height(-2) + expect(Pin.lastWarningText).to(contain(["height", "won't be applied", "the height", "must be greater than or equal to zero"])) + expect(aView.frame).to(equal(CGRect(x: 140, y: 100.0, width: 100.0, height: 60.0))) + } + + it("should warn about NaN height value") { + aView.pin.height(CGFloat.nan) + expect(Pin.lastWarningText).to(contain(["height", "nan", "won't be applied", "the height", "must be greater than or equal to zero"])) + expect(aView.frame).to(equal(CGRect(x: 140, y: 100.0, width: 100.0, height: 60.0))) + } + + it("should warn about -NaN height value") { + aView.pin.height(-CGFloat.nan) + expect(Pin.lastWarningText).to(contain(["height", "nan", "won't be applied", "the height", "must be greater than or equal to zero"])) + expect(aView.frame).to(equal(CGRect(x: 140, y: 100.0, width: 100.0, height: 60.0))) + } + + it("should warn about infinity height value") { + aView.pin.height(CGFloat.infinity) + expect(Pin.lastWarningText).to(contain(["height", "inf", "won't be applied", "the height", "must be greater than or equal to zero"])) + expect(aView.frame).to(equal(CGRect(x: 140, y: 100.0, width: 100.0, height: 60.0))) + } + + it("should warn about -infinity height value") { + aView.pin.height(-CGFloat.infinity) + expect(Pin.lastWarningText).to(contain(["height", "inf", "won't be applied", "the height", "must be greater than or equal to zero"])) + expect(aView.frame).to(equal(CGRect(x: 140, y: 100.0, width: 100.0, height: 60.0))) + } } describe("the result of the size(...) methods") { @@ -206,12 +279,6 @@ class AdjustSizeSpec: QuickSpec { // #if os(iOS) || os(tvOS) describe("the result of the fitSize() method") { - it("should not adjust the size of aView if width or height has not been specified") { - aView.pin.fitSize() - expect(Pin.lastWarningText).to(contain(["fitSize() won't be applied", "neither the width nor the height can be determined"])) - expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 100.0, height: 60.0))) - } - it("should adjust the size of aView by calling fitSize() method") { aView.pin.size(CGSize(width: 20, height: 100)).sizeToFit(.width) expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 20.0, height: 80.0))) @@ -346,7 +413,7 @@ class AdjustSizeSpec: QuickSpec { } it("should adjust the size with fitSize() and distribute extra width") { - aView.pin.bottom(100).height(200).pinEdges().marginTop(10).marginBottom(10).fitSize().justify(.left) + aView.pin.bottom(100).height(200).pinEdges().marginTop(10).marginBottom(10).sizeToFit(.height).justify(.left) expect(aView.frame).to(equal(CGRect(x: 140.0, y: 110.0, width: 9, height: 180))) } @@ -609,8 +676,8 @@ class AdjustSizeSpec: QuickSpec { } it("should adjust the size of aView by calling fitSize() method") { - aView.pin.height(100).minWidth(20).fitSize() - expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 20.0, height: 80.0))) + aView.pin.height(100).minWidth(20).sizeToFit(.height) + expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 20.0, height: 100.0))) } // luc pq ci bas != ci haut? @@ -626,20 +693,13 @@ class AdjustSizeSpec: QuickSpec { // describe("the result of the sizeToFit(.height) && sizeToFit(.width)") { it("should warn method") { - aView.pin.width(100).fitSize().sizeToFit(.width) - expect(Pin.lastWarningText).to(contain(["sizeToFit(.width)", "won't be applied", "conflicts with fitSize()"])) - expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 100.0, height: 16.0))) - } - - it("should warn method") { - aView.pin.width(100).fitSize().sizeToFit(.height) - expect(Pin.lastWarningText).to(contain(["sizeToFit(.height)", "won't be applied", "conflicts with fitSize()"])) - expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 100.0, height: 16.0))) + aView.pin.width(100).sizeToFit(.height).sizeToFit(.height) + expect(Pin.lastWarningText).to(contain(["sizeToFit(.height)", "won't be applied", "conflicts with sizeToFit(.height)"])) } it("should warn method") { aView.pin.width(100).aspectRatio(2).sizeToFit(.width) - expect(Pin.lastWarningText).to(contain(["sizeToFit(.width)", "won't be applied", "aspectRatio: 2"])) + expect(Pin.lastWarningText).to(contain(["sizeToFit(.width)", "won't be applied", "aspectRatio(2.0)"])) } it("should warn method") { @@ -648,8 +708,8 @@ class AdjustSizeSpec: QuickSpec { } it("should warn method") { - aView.pin.width(100).fitSize().aspectRatio(2) - expect(Pin.lastWarningText).to(contain([" aspectRatio(2.0)", "won't be applied", "conflicts with fitSize()"])) + aView.pin.width(100).sizeToFit(.width).aspectRatio(2) + expect(Pin.lastWarningText).to(contain([" aspectRatio(2.0)", "won't be applied", "conflicts with sizeToFit(.width)"])) } } @@ -794,8 +854,8 @@ class AdjustSizeSpec: QuickSpec { describe("the result of the fitSize()") { it("should adjust the aView") { aView.sizeThatFitSizeOffset = -10 - aView.pin.width(50).fitSize() - expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 40.0, height: 40.0))) + aView.pin.width(50).sizeToFit(.width) + expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 50.0, height: 40.0))) } it("should adjust the aView") { @@ -821,11 +881,10 @@ class AdjustSizeSpec: QuickSpec { expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 32.0, height: 50.0))) } - it("should adjust the aView") { aView.sizeThatFitSizeOffset = -10 - aView.pin.height(50).fitSize() - expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 40.0, height: 40.0))) + aView.pin.height(50).sizeToFit(.height) + expect(aView.frame).to(equal(CGRect(x: 140.0, y: 100.0, width: 40.0, height: 50.0))) } it("should adjust the aView") { @@ -862,6 +921,45 @@ class AdjustSizeSpec: QuickSpec { expect(aView.frame).to(beCloseTo(CGRect(x: 140.0, y: 100.0, width: 50.0, height: 32.0), within: 0.5)) } } + + // + // sizeToFit() + // + describe("the result of the sizeToFit()") { + it("should adjust the aView") { + _pinlayoutSetUnitTest(scale: 3) + + aView.pin.sizeToFit().center() + expect(aView.frame).to(beCloseTo(CGRect(x: 182.6667, y: 177.6667, width: 35.0, height: 45.0), within: 0.1)) + } + + it("should produce the same size as the built-in sizeToFit() method") { + _pinlayoutSetUnitTest(scale: nil) + + let label = PLabel(frame: CGRect.zero) + label.text = "Lorem ipsum dolor sit amet" + label.pin.sizeToFit() + let size = label.bounds.size + + label.bounds.size = CGSize.zero + label.sizeToFit() + + expect(size).to(equal(label.bounds.size)) + } + + it("should produce the same size as the built-in sizeToFit() method when there is a transform applied") { + _pinlayoutSetUnitTest(scale: nil) + + let label = PLabel(frame: CGRect.zero) + label.text = "Lorem ipsum dolor sit amet" + label.transform = CGAffineTransform(scaleX: 0.5, y: 0.5) + label.pin.sizeToFit() + let size = label.bounds.size + label.bounds.size = CGSize.zero + label.sizeToFit() + expect(size).to(equal(label.bounds.size)) + } + } #endif } } diff --git a/Tests/Common/AspectRatioTests.swift b/Tests/Common/AspectRatioTests.swift index 4a0a289b..38fa7dab 100644 --- a/Tests/Common/AspectRatioTests.swift +++ b/Tests/Common/AspectRatioTests.swift @@ -40,11 +40,8 @@ class AspectRatioTests: QuickSpec { - imageView */ - beforeSuite { - _pinlayoutSetUnitTest(displayScale: 2) - } - beforeEach { + _pinlayoutSetUnitTest(scale: 2) Pin.lastWarningText = nil viewController = PViewController() @@ -68,17 +65,17 @@ class AspectRatioTests: QuickSpec { rootView.addSubview(imageView) #endif } + + afterEach { + _pinlayoutSetUnitTest(scale: nil) + Pin.resetWarnings() + } // // aspectRatio(: CGFloat) // describe("the result of the aspectRatio(CGFloat)") { #if os(iOS) || os(tvOS) - it("should warn about fitSize()") { - aView.pin.left().width(100%).aspectRatio(2).fitSize() - expect(Pin.lastWarningText).to(contain(["fitSize() won't be applied", "conflicts", "aspectRatio"])) - } - it("should warn about fitSize()") { aView.pin.left().height(100).aspectRatio(2).sizeToFit(.width) expect(Pin.lastWarningText).to(contain(["sizeToFit(.width) won't be applied", "conflicts", "aspectRatio"])) @@ -174,9 +171,26 @@ class AspectRatioTests: QuickSpec { describe("the result of the aspectRatio(CGFloat)") { it("should warn about aspectRatio()") { aView.pin.left().width(100).aspectRatio() - expect(Pin.lastWarningText).to(contain(["aspectRatio() won't be applied", "the layouted must be an UIImageView()"])) + expect(Pin.lastWarningText).to(contain(["aspectRatio() won't be applied", "the layouted view must be an UIImageView()"])) expect(aView.frame).to(equal(CGRect(x: 0.0, y: 100.0, width: 100.0, height: 100.0))) } + + it("should warn about aspectRatio() if no image is set") { + imageView.image = nil + imageView.pin.left().width(100).aspectRatio() + expect(Pin.lastWarningText).to(contain(["aspectRatio() won't be applied", "the layouted UIImageView's image hasn't been set (aspectRatioImageNotSet)"])) + } + + it("should not warn about aspectRatio() if no image is set") { + Pin.activeWarnings.aspectRatioImageNotSet = false + imageView.image = nil + imageView.pin.left().width(100).aspectRatio() + expect(Pin.lastWarningText).to(beNil()) + expect(imageView.frame).to(equal(CGRect(x: 0.0, y: 10.0, width: 100.0, height: 60.0))) + } + +// guard Pin.logWarnings && else { return self } +// warnWontBeApplied(")", context) it("should warn about aspectRatio()") { imageView.pin.left().aspectRatio() diff --git a/Tests/Common/BetweenSpec.swift b/Tests/Common/BetweenSpec.swift new file mode 100644 index 00000000..874e94fd --- /dev/null +++ b/Tests/Common/BetweenSpec.swift @@ -0,0 +1,548 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Quick +import Nimble +import PinLayout + +class BetweenSpec: QuickSpec { + override func spec() { + var viewController: PViewController! + + var rootView: BasicView! + + var aView: BasicView! + var aViewChild: BasicView! + + var bView: BasicView! + var bViewChild: BasicView! + + var cView: BasicView! + + /* + root + | + - aView + | | + | - aViewChild + | + - bView + | + - bViewChild + */ + + beforeEach { + _pinlayoutSetUnitTest(scale: 2) + Pin.lastWarningText = nil + + viewController = PViewController() + viewController.view = BasicView() + + rootView = BasicView() + rootView.frame = CGRect(x: 0, y: 64, width: 400, height: 400) + viewController.view.addSubview(rootView) + + aView = BasicView() + rootView.addSubview(aView) + + aViewChild = BasicView() + aView.addSubview(aViewChild) + + bView = BasicView() + rootView.addSubview(bView) + + bViewChild = BasicView() + bView.addSubview(bViewChild) + + cView = BasicView() + rootView.addSubview(cView) + } + + afterEach { + _pinlayoutSetUnitTest(scale: nil) + Pin.resetWarnings() + } + + // + // horizontallyBetween(...) + // + describe("the result of horizontallyBetween(...)") { + it("should warns view being layouted cannot be used as one of the references") { + aView.frame = CGRect(x: 40, y: 100, width: 200, height: 100) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(aView, and: bView) + expect(Pin.lastWarningText).to(contain(["horizontallyBetween((BasicView, Frame: (40.0, 100.0, 200.0, 100.0))", ") won't be applied", + "the view being layouted cannot be used as one of the references"])) + expect(bView.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 0.0, height: 0.0))) + } + + it("should warns the view being layouted cannot be used as one of the references (2)") { + aView.frame = CGRect(x: 40, y: 100, width: 200, height: 100) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(bView, and: cView) + expect(Pin.lastWarningText).to(contain(["horizontallyBetween((BasicView, Frame: (0.0, 0.0, 0.0, 0.0))", "won't be applied", + "the view being layouted cannot be used as one of the references"])) + expect(bView.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 0.0, height: 0.0))) + } + + it("should warns that there is no space between views") { + aView.frame = CGRect(x: 40, y: 100, width: 200, height: 100) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(aView, and: cView) + expect(Pin.lastWarningText).to(contain(["horizontallyBetween((BasicView, Frame: (40.0, 100.0, 200.0, 100.0)), ", + " won't be applied", "there is no horizontal space between these views"])) + expect(bView.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 0.0, height: 0.0))) + } + + it("should not warns because Pin.activeLogWarnings.betweenNoSpaceAvailableBetweenViews is set to false") { + Pin.activeWarnings.noSpaceAvailableBetweenViews = false + aView.frame = CGRect(x: 40, y: 100, width: 200, height: 100) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(aView, and: cView) + expect(Pin.lastWarningText).to(beNil()) + expect(bView.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 0.0, height: 0.0))) + } + + it("should warns that there is no space between views. Use superview as reference") { + aView.frame = CGRect(x: 40, y: 100, width: 50, height: 100) + bView.frame = CGRect(x: 0, y: 0, width: 50, height: 50) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(rootView, and: cView, aligned: .bottom) + + expect(Pin.lastWarningText).to(contain(["horizontallyBetween((BasicView, Frame: (0.0, 64.0, 400.0, 400.0)), ", + " won't be applied", "there is no horizontal space between these views"])) + } + + it("should warns that there is no space between views. Use self as reference") { + aView.frame = CGRect(x: 40, y: 100, width: 50, height: 100) + bView.frame = CGRect(x: 0, y: 0, width: 50, height: 50) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(viewController.view, and: cView, aligned: .bottom) + expect(Pin.lastWarningText).to(contain(["horizontallyBetween((BasicView, Frame: (0.0, 0.0, 0.0, 0.0)), ", + " won't be applied", "must be added as a sub-view before being used as a reference"])) + } + + it("should horizontally layout between") { + aView.frame = CGRect(x: 40, y: 100, width: 50, height: 100) + bView.frame = CGRect(x: 0, y: 0, width: 50, height: 50) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(aView, and: cView) + expect(bView.frame).to(equal(CGRect(x: 90.0, y: 0.0, width: 70.0, height: 50.0))) + } + + it("should horizontally layout between (2)") { + aView.frame = CGRect(x: 40, y: 100, width: 50, height: 100) + bView.frame = CGRect(x: 0, y: 0, width: 50, height: 50) + cView.frame = CGRect(x: 160, y: 100, width: 110, height: 80) + bView.pin.horizontallyBetween(aView, and: cView, aligned: .top) + expect(bView.frame).to(equal(CGRect(x: 90.0, y: 100.0, width: 70.0, height: 50.0))) + } + + it("should horizontally layout between (3)") { + aView.frame = CGRect(x: 40, y: 100, width: 50, height: 100) + bView.frame = CGRect(x: 0, y: 0, width: 50, height: 50) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(cView, and: aView, aligned: .top) + expect(bView.frame).to(equal(CGRect(x: 90.0, y: 140.0, width: 70.0, height: 50.0))) + } + + it("should horizontally layout between (3)") { + aView.frame = CGRect(x: 40, y: 100, width: 50, height: 100) + bView.frame = CGRect(x: 0, y: 0, width: 50, height: 50) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(aView, and: cView, aligned: .center) + expect(bView.frame).to(equal(CGRect(x: 90.0, y: 125.0, width: 70.0, height: 50.0))) + } + + it("should horizontally layout between (4)") { + aView.frame = CGRect(x: 40, y: 100, width: 50, height: 100) + bView.frame = CGRect(x: 0, y: 0, width: 50, height: 50) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(cView, and: aView, aligned: .center) + expect(bView.frame).to(equal(CGRect(x: 90.0, y: 155.0, width: 70.0, height: 50.0))) + } + + it("should horizontally layout between (5)") { + aView.frame = CGRect(x: 40, y: 100, width: 50, height: 100) + bView.frame = CGRect(x: 0, y: 0, width: 50, height: 50) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(aView, and: cView, aligned: .bottom) + expect(bView.frame).to(equal(CGRect(x: 90.0, y: 150.0, width: 70.0, height: 50.0))) + } + + it("should horizontally layout between (6)") { + aView.frame = CGRect(x: 40, y: 100, width: 50, height: 100) + bView.frame = CGRect(x: 0, y: 0, width: 50, height: 50) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(cView, and: aView, aligned: .bottom) + expect(bView.frame).to(equal(CGRect(x: 90.0, y: 170.0, width: 70.0, height: 50.0))) + } + + it("should horizontally layout between align center") { + aView.frame = CGRect(x: 40, y: 100, width: 50, height: 100) + bView.frame = CGRect(x: 0, y: 0, width: 50, height: 50) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(aView, and: cView, aligned: .center) + expect(bView.frame).to(equal(CGRect(x: 90.0, y: 125.0, width: 70.0, height: 50.0))) + } + + it("should horizontally layout between align center (2)") { + aView.frame = CGRect(x: 40, y: 100, width: 50, height: 100) + bView.frame = CGRect(x: 0, y: 0, width: 50, height: 50) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(cView, and: aView, aligned: .center) + expect(bView.frame).to(equal(CGRect(x: 90.0, y: 155.0, width: 70.0, height: 50.0))) + } + + it("should horizontally layout between align bottom (1)") { + aView.frame = CGRect(x: 40, y: 100, width: 50, height: 100) + bView.frame = CGRect(x: 0, y: 0, width: 50, height: 50) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(aView, and: cView, aligned: .bottom) + expect(bView.frame).to(equal(CGRect(x: 90.0, y: 150.0, width: 70.0, height: 50.0))) + } + + it("should horizontally layout between align bottom (2)") { + aView.frame = CGRect(x: 40, y: 100, width: 50, height: 100) + bView.frame = CGRect(x: 0, y: 0, width: 50, height: 50) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.horizontallyBetween(cView, and: aView, aligned: .bottom) + expect(bView.frame).to(equal(CGRect(x: 90.0, y: 170.0, width: 70.0, height: 50.0))) + } + } + + // + // horizontallyBetween() when using child views from other container + // + describe("the result of horizontallyBetween(...) when using child views from other container") { + it("should layout between (1)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 180, y: 170, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 0, y: 100, width: 110, height: 80) + cView.pin.horizontallyBetween(aViewChild, and: bViewChild) + expect(cView.frame).to(equal(CGRect(x: 120.0, y: 100.0, width: 100.0, height: 80.0))) + } + + it("should layout between (2)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 180, y: 170, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 0, y: 100, width: 110, height: 80) + cView.pin.horizontallyBetween(aViewChild, and: bViewChild, aligned: .top) + expect(cView.frame).to(equal(CGRect(x: 120.0, y: 60.0, width: 100.0, height: 80.0))) + } + + it("should layout between (3)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 180, y: 170, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 0, y: 100, width: 110, height: 80) + cView.pin.horizontallyBetween(aViewChild, and: bViewChild, aligned: .center) + expect(cView.frame).to(equal(CGRect(x: 120.0, y: 40.0, width: 100.0, height: 80.0))) + } + + it("should layout between (4)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 180, y: 170, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 0, y: 100, width: 110, height: 80) + cView.pin.horizontallyBetween(aViewChild, and: bViewChild, aligned: .bottom) + expect(cView.frame).to(equal(CGRect(x: 120.0, y: 20.0, width: 100.0, height: 80.0))) + } + + it("should layout between (5)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 180, y: 170, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 0, y: 100, width: 110, height: 80) + cView.pin.horizontallyBetween(bViewChild, and: aViewChild, aligned: .center) + expect(cView.frame).to(equal(CGRect(x: 120.0, y: 150.0, width: 100.0, height: 80.0))) + } + + it("should layout between (6)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 180, y: 170, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 0, y: 100, width: 110, height: 80) + cView.pin.horizontallyBetween(aView, and: aViewChild, aligned: .top) + + expect(Pin.lastWarningText).to(contain(["horizontallyBetween((BasicView, Frame: (10.0, 10.0, 120.0, 150.0)), ", + ", aligned: .top)", " won't be applied", "there is no horizontal space between these views"])) + } + + it("should layout between (7)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 180, y: 170, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 0, y: 100, width: 110, height: 80) + cView.pin.horizontallyBetween(aView, and: bViewChild, aligned: .top) + expect(cView.frame).to(equal(CGRect(x: 130.0, y: 10.0, width: 90.0, height: 80.0))) + } + + it("should layout between (8)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 180, y: 170, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 0, y: 100, width: 110, height: 80) + cView.pin.horizontallyBetween(bViewChild, and: aView, aligned: .top) + expect(cView.frame).to(equal(CGRect(x: 130.0, y: 180.0, width: 90.0, height: 80.0))) + } + } + + // + // verticallyBetween(...) + // + describe("the result of verticallyBetween(...)") { + it("should warns view being layouted cannot be used as one of the references") { + aView.frame = CGRect(x: 40, y: 100, width: 200, height: 100) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.frame = CGRect(x: 10, y: 10, width: 11, height: 12) + bView.pin.verticallyBetween(aView, and: bView) + expect(Pin.lastWarningText).to(contain(["verticallyBetween((BasicView, Frame: (40.0, 100.0, 200.0, 100.0))", ") won't be applied", "the view being layouted cannot be used as one of the references"])) + expect(bView.frame).to(equal(CGRect(x: 10.0, y: 10.0, width: 11.0, height: 12.0))) + } + + it("should warns view being layouted cannot be used as one of the references") { + aView.frame = CGRect(x: 40, y: 100, width: 200, height: 100) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.frame = CGRect(x: 10, y: 10, width: 11, height: 12) + bView.pin.verticallyBetween(bView, and: cView) + expect(Pin.lastWarningText).to(contain(["verticallyBetween((BasicView, Frame: (10.0, 10.0, 11.0, 12.0))", ") won't be applied", "the view being layouted cannot be used as one of the references"])) + expect(bView.frame).to(equal(CGRect(x: 10.0, y: 10.0, width: 11.0, height: 12.0))) + } + + it("should warns that there is no space between views") { + aView.frame = CGRect(x: 40, y: 100, width: 200, height: 100) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.frame = CGRect(x: 10, y: 10, width: 11, height: 12) + bView.pin.verticallyBetween(aView, and: cView) + expect(Pin.lastWarningText).to(contain(["verticallyBetween((BasicView, Frame: (40.0, 100.0, 200.0, 100.0))", " won't be applied", "there is no vertical space between these views"])) + expect(bView.frame).to(equal(CGRect(x: 10.0, y: 10.0, width: 11.0, height: 12.0))) + } + + it("should not warns because Pin.activeLogWarnings.betweenNoSpaceAvailableBetweenViews is set to false") { + Pin.activeWarnings.noSpaceAvailableBetweenViews = false + aView.frame = CGRect(x: 40, y: 100, width: 200, height: 100) + cView.frame = CGRect(x: 160, y: 140, width: 110, height: 80) + bView.pin.verticallyBetween(aView, and: cView) + expect(Pin.lastWarningText).to(beNil()) + expect(bView.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 0.0, height: 0.0))) + } + + it("should vertically layout between") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + bView.frame = CGRect(x: 180, y: 240, width: 150, height: 50) + cView.frame = CGRect(x: 0, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(aView, and: bView) + expect(cView.frame).to(equal(CGRect(x: 0.0, y: 160.0, width: 110.0, height: 80.0))) + } + + it("should vertically layout between (2)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + bView.frame = CGRect(x: 180, y: 240, width: 150, height: 50) + cView.frame = CGRect(x: 0, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(bView, and: aView) + expect(cView.frame).to(equal(CGRect(x: 0.0, y: 160.0, width: 110.0, height: 80.0))) + } + + it("should vertically layout between (3)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + bView.frame = CGRect(x: 180, y: 240, width: 150, height: 50) + cView.frame = CGRect(x: 40, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(aView, and: bView, aligned: .left) + expect(cView.frame).to(equal(CGRect(x: 10.0, y: 160.0, width: 110.0, height: 80.0))) + } + + it("should vertically layout between (4)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + bView.frame = CGRect(x: 180, y: 240, width: 150, height: 50) + cView.frame = CGRect(x: 40, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(bView, and: aView, aligned: .left) + expect(cView.frame).to(equal(CGRect(x: 180.0, y: 160.0, width: 110.0, height: 80.0))) + } + + it("should vertically layout between (5)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + bView.frame = CGRect(x: 180, y: 240, width: 150, height: 50) + cView.frame = CGRect(x: 40, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(aView, and: bView, aligned: .center) + expect(cView.frame).to(equal(CGRect(x: 15.0, y: 160.0, width: 110.0, height: 80.0))) + } + + it("should vertically layout between (6)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + bView.frame = CGRect(x: 180, y: 240, width: 150, height: 50) + cView.frame = CGRect(x: 40, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(bView, and: aView, aligned: .center) + expect(cView.frame).to(equal(CGRect(x: 200.0, y: 160.0, width: 110.0, height: 80.0))) + } + + it("should vertically layout between (7)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + bView.frame = CGRect(x: 180, y: 240, width: 150, height: 50) + cView.frame = CGRect(x: 40, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(aView, and: bView, aligned: .right) + expect(cView.frame).to(equal(CGRect(x: 20.0, y: 160.0, width: 110.0, height: 80.0))) + } + + it("should vertically layout between (8)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + bView.frame = CGRect(x: 180, y: 240, width: 150, height: 50) + cView.frame = CGRect(x: 40, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(bView, and: aView, aligned: .right) + expect(cView.frame).to(equal(CGRect(x: 220.0, y: 160.0, width: 110.0, height: 80.0))) + } + + it("should vertically layout between (9)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + bView.frame = CGRect(x: 180, y: 240, width: 150, height: 50) + cView.frame = CGRect(x: 40, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(aView, and: bView, aligned: .start) + expect(cView.frame).to(equal(CGRect(x: 10.0, y: 160.0, width: 110.0, height: 80.0))) + } + + it("should vertically layout between (10)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + bView.frame = CGRect(x: 180, y: 240, width: 150, height: 50) + cView.frame = CGRect(x: 40, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(bView, and: aView, aligned: .start) + expect(cView.frame).to(equal(CGRect(x: 180.0, y: 160.0, width: 110.0, height: 80.0))) + } + + it("should vertically layout between (11)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + bView.frame = CGRect(x: 180, y: 240, width: 150, height: 50) + cView.frame = CGRect(x: 40, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(aView, and: bView, aligned: .end) + expect(cView.frame).to(equal(CGRect(x: 20.0, y: 160.0, width: 110.0, height: 80.0))) + } + + it("should vertically layout between (12)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + bView.frame = CGRect(x: 180, y: 240, width: 150, height: 50) + cView.frame = CGRect(x: 40, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(bView, and: aView, aligned: .end) + expect(cView.frame).to(equal(CGRect(x: 220.0, y: 160.0, width: 110.0, height: 80.0))) + } + } + + // + // verticallyBetween(...) when using child views from other container + // + describe("the result of verticallyBetween(...) when using child views from other container") { + it("should layout between (1)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 100, y: 220, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 30, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(aViewChild, and: bViewChild) + expect(cView.frame).to(equal(CGRect(x: 30.0, y: 100.0, width: 110.0, height: 130.0))) + } + + it("should layout between (2)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 100, y: 220, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 30, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(aViewChild, and: bViewChild, aligned: .left) + expect(cView.frame).to(equal(CGRect(x: 40.0, y: 100.0, width: 110.0, height: 130.0))) + } + + it("should layout between (3)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 100, y: 220, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 30, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(aViewChild, and: bViewChild, aligned: .center) + expect(cView.frame).to(equal(CGRect(x: 25.0, y: 100.0, width: 110.0, height: 130.0))) + } + + it("should layout between (4)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 100, y: 220, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 30, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(aViewChild, and: bViewChild, aligned: .right) + } + + it("should layout between (5)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 100, y: 220, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 30, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(bViewChild, and: aViewChild, aligned: .center) + expect(cView.frame).to(equal(CGRect(x: 115.0, y: 100.0, width: 110.0, height: 130.0))) + } + + it("should layout between (6)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 100, y: 220, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 30, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(aViewChild, and: bViewChild, aligned: .start) + expect(cView.frame).to(equal(CGRect(x: 40.0, y: 100.0, width: 110.0, height: 130.0))) + } + + it("should layout between (7)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 100, y: 220, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 30, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(aView, and: aViewChild, aligned: .left) + expect(Pin.lastWarningText).to(contain(["verticallyBetween((BasicView, Frame: (10.0, 10.0, 120.0, 150.0)),", " won't be applied", "there is no vertical space between these views"])) + } + + it("should layout between (8)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 100, y: 220, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 30, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(aView, and: bViewChild, aligned: .left) + expect(cView.frame).to(equal(CGRect(x: 10.0, y: 160.0, width: 110.0, height: 70.0))) + } + + it("should layout between (9)") { + aView.frame = CGRect(x: 10, y: 10, width: 120, height: 150) + aViewChild.frame = CGRect(x: 30, y: 50, width: 80, height: 40) + bView.frame = CGRect(x: 100, y: 220, width: 150, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + cView.frame = CGRect(x: 30, y: 100, width: 110, height: 80) + cView.pin.verticallyBetween(bViewChild, and: aView, aligned: .right) + expect(cView.frame).to(equal(CGRect(x: 90.0, y: 160.0, width: 110.0, height: 70.0))) + } + } + } +} diff --git a/Tests/Common/CALayerSpec.swift b/Tests/Common/CALayerSpec.swift new file mode 100644 index 00000000..9f070dd1 --- /dev/null +++ b/Tests/Common/CALayerSpec.swift @@ -0,0 +1,115 @@ +// Copyright (c) 2018 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Quick +import Nimble +import PinLayout + +class CALayerSpec: QuickSpec { + override func spec() { + var viewController: PViewController! + var rootView: BasicView! + var rootLayer: CALayer! + var aLayer: CALayer! + var bLayer: CALayer! + + /* + rootLayer + | + - aLayer + bLayer + */ + + beforeEach { + _pinlayoutSetUnitTest(scale: 2) + Pin.lastWarningText = nil + Pin.logMissingLayoutCalls = false + + viewController = PViewController() + viewController.view = BasicView() + + rootView = BasicView() + rootView.frame = CGRect(x: 0, y: 0, width: 400, height: 400) + + rootLayer = CALayer() + rootLayer.frame = CGRect(x: 0, y: 0, width: 400, height: 400) + + aLayer = CALayer() + aLayer.bounds.size = CGSize(width: 50, height: 50) + bLayer = CALayer() + bLayer.bounds.size = CGSize(width: 20, height: 20) + + #if os(macOS) + rootView.wantsLayer = true + rootView.layer?.addSublayer(rootLayer) + #else + rootView.layer.addSublayer(rootLayer) + #endif + + rootLayer.addSublayer(aLayer) + rootLayer.addSublayer(bLayer) + + viewController.view.addSubview(rootView) + } + + afterEach { + _pinlayoutSetUnitTest(scale: nil) + Pin.logMissingLayoutCalls = false + } + + // + // CALayer is already heavily tested since UIView delegate it's layout to it's layer. + // Validate only the direct usability. + // + describe("test CALayer interface") { + it("should work with basic pinlayout calls") { + aLayer.pin.top(10).left(10).width(20%).height(80%) + bLayer.pin.right(of: aLayer, aligned: .center) + expect(aLayer.frame).to(equal(CGRect(x: 10, y: 10, width: 80, height: 320))) + } + + it("should be able to be positioned relatively to edges") { + aLayer.pin.top().right(to: rootLayer.edge.right) + expect(aLayer.frame).to(equal(CGRect(x: 350, y: 0, width: 50, height: 50))) + } + + it("should be able to be positioned relatively to anchors") { + aLayer.pin.topLeft(to: rootLayer.anchor.center) + expect(aLayer.frame).to(equal(CGRect(x: 200, y: 200, width: 50, height: 50))) + } + + it("should support pinFrame properly when a transform is set") { + rootLayer.transform = CATransform3DIdentity + + bLayer.frame = aLayer.frame + + aLayer.transform = CATransform3DMakeScale(2, 2, 1) + bLayer.transform = CATransform3DMakeScale(2, 2, 1) + + aLayer.pin.top(100).left(100).width(100).height(50) + bLayer.pinFrame.top(100).left(100).width(100).height(50) + + expect(aLayer.frame).to(equal(CGRect(x: 50, y: 75, width: 200, height: 100))) + expect(aLayer.bounds).to(equal(CGRect(x: 0, y: 0, width: 100, height: 50))) + expect(bLayer.frame).to(equal(CGRect(x: 100, y: 100, width: 100, height: 50))) + expect(bLayer.bounds).to(equal(CGRect(x: 0, y: 0, width: 50, height: 25))) + } + } + } +} diff --git a/Tests/Common/JustifyAlignSpec.swift b/Tests/Common/JustifyAlignSpec.swift index 05b6ac53..97b21360 100644 --- a/Tests/Common/JustifyAlignSpec.swift +++ b/Tests/Common/JustifyAlignSpec.swift @@ -32,12 +32,9 @@ class JustifyAlignSpec: QuickSpec { | - aView */ - - beforeSuite { - _pinlayoutSetUnitTest(displayScale: 2) - } beforeEach { + _pinlayoutSetUnitTest(scale: 2) Pin.lastWarningText = nil viewController = PViewController() @@ -52,6 +49,10 @@ class JustifyAlignSpec: QuickSpec { aView.sizeThatFitsExpectedArea = 40 * 40 rootView.addSubview(aView) } + + afterEach { + _pinlayoutSetUnitTest(scale: nil) + } // // justify + warning diff --git a/Tests/Common/LayoutMethodSpec.swift b/Tests/Common/LayoutMethodSpec.swift index a38fe2eb..a9b9ad81 100644 --- a/Tests/Common/LayoutMethodSpec.swift +++ b/Tests/Common/LayoutMethodSpec.swift @@ -33,11 +33,8 @@ class LayoutMethodSpec: QuickSpec { - aView */ - beforeSuite { - _pinlayoutSetUnitTest(displayScale: 2) - } - beforeEach { + _pinlayoutSetUnitTest(scale: 2) Pin.lastWarningText = nil Pin.logMissingLayoutCalls = false @@ -52,8 +49,9 @@ class LayoutMethodSpec: QuickSpec { aView.frame = CGRect(x: 40, y: 100, width: 100, height: 60) rootView.addSubview(aView) } - + afterEach { + _pinlayoutSetUnitTest(scale: nil) Pin.logMissingLayoutCalls = false } diff --git a/Tests/Common/MinMaxWidthHeightSpec.swift b/Tests/Common/MinMaxWidthHeightSpec.swift index 8b6c86df..c3f20d98 100644 --- a/Tests/Common/MinMaxWidthHeightSpec.swift +++ b/Tests/Common/MinMaxWidthHeightSpec.swift @@ -33,11 +33,8 @@ class MinMaxWidthHeightSpec: QuickSpec { - aView */ - beforeSuite { - _pinlayoutSetUnitTest(displayScale: 2) - } - beforeEach { + _pinlayoutSetUnitTest(scale: 2) Pin.lastWarningText = nil viewController = PViewController() @@ -53,6 +50,10 @@ class MinMaxWidthHeightSpec: QuickSpec { rootView.addSubview(aView) } + afterEach { + _pinlayoutSetUnitTest(scale: nil) + } + // // minWidth // @@ -268,13 +269,13 @@ class MinMaxWidthHeightSpec: QuickSpec { describe("the result of the minWidth/maxWidth & fitSize()") { it("should adjust the width when using fitSize") { aView.sizeThatFitsExpectedArea = 40 * 40 - aView.pin.left().width(100%).maxWidth(250).maxHeight(20).fitSize() + aView.pin.left().width(100%).maxWidth(250).maxHeight(20).sizeToFit(.width) expect(aView.frame).to(equal(CGRect(x: 0.0, y: 100.0, width: 250.0, height: 6.5))) } it("should adjust the width when using fitSize") { aView.sizeThatFitsExpectedArea = 40 * 40 - aView.pin.left().width(100%).maxWidth(250).maxHeight(20).fitSize() + aView.pin.left().width(100%).maxWidth(250).maxHeight(20).sizeToFit(.width) expect(aView.frame).to(equal(CGRect(x: 0.0, y: 100.0, width: 250.0, height: 6.5))) } @@ -286,7 +287,7 @@ class MinMaxWidthHeightSpec: QuickSpec { it("should adjust the width when using fitSize") { aView.sizeThatFitsExpectedArea = 40 * 40 - aView.pin.left().width(100%).maxWidth(250).height(14).maxHeight(20).fitSize() + aView.pin.left().width(100%).maxWidth(250).height(14).maxHeight(20).sizeToFit(.width) expect(aView.frame).to(equal(CGRect(x: 0.0, y: 100.0, width: 250.0, height: 6.5))) } @@ -564,43 +565,43 @@ class MinMaxWidthHeightSpec: QuickSpec { describe("the result of the minHeight/maxWidth & fitSize()") { it("should adjust the height when using fitSize") { aView.sizeThatFitsExpectedArea = 40 * 40 - aView.pin.top().height(100%).maxHeight(200).fitSize() + aView.pin.top().height(100%).maxHeight(200).sizeToFit(.height) expect(aView.frame).to(equal(CGRect(x: 40.0, y: 0.0, width: 8.0, height: 200.0))) } it("should adjust the height when using fitSize") { aView.sizeThatFitsExpectedArea = 40 * 40 - aView.pin.top().height(100%).maxHeight(200).fitSize().align(.top) + aView.pin.top().height(100%).maxHeight(200).sizeToFit(.height).align(.top) expect(aView.frame).to(equal(CGRect(x: 40.0, y: 0.0, width: 8.0, height: 200.0))) } it("should adjust the height when using fitSize") { aView.sizeThatFitsExpectedArea = 40 * 40 - aView.pin.top().bottom().height(100%).maxHeight(200).fitSize().align(.bottom) + aView.pin.top().bottom().height(100%).maxHeight(200).sizeToFit(.height).align(.bottom) expect(aView.frame).to(equal(CGRect(x: 40.0, y: 200.0, width: 8.0, height: 200.0))) } it("should adjust the height when using fitSize") { aView.sizeThatFitsExpectedArea = 40 * 40 - aView.pin.top().bottom().maxHeight(200).fitSize().align(.center) + aView.pin.top().bottom().maxHeight(200).sizeToFit(.height).align(.center) expect(aView.frame).to(equal(CGRect(x: 40.0, y: 100.0, width: 8.0, height: 200.0))) } it("should adjust the height when using fitSize") { aView.sizeThatFitsExpectedArea = 40 * 40 - aView.pin.top().height(10).minHeight(200).fitSize() + aView.pin.top().height(10).minHeight(200).sizeToFit(.height) expect(aView.frame).to(equal(CGRect(x: 40.0, y: 0.0, width: 8.0, height: 200.0))) } it("should adjust the height when using fitSize") { aView.sizeThatFitsExpectedArea = 40 * 40 - aView.pin.top().bottom().height(10).minHeight(200).fitSize().align(.center) + aView.pin.top().bottom().height(10).minHeight(200).sizeToFit(.height).align(.center) expect(aView.frame).to(equal(CGRect(x: 40.0, y: 100.0, width: 8.0, height: 200.0))) } it("should adjust the height when using fitSize") { aView.sizeThatFitsExpectedArea = 40 * 40 - aView.pin.top().bottom().height(10).minHeight(200).marginTop(10).fitSize().align(.center) + aView.pin.top().bottom().height(10).minHeight(200).marginTop(10).sizeToFit(.height).align(.center) expect(aView.frame).to(equal(CGRect(x: 40.0, y: 105.0, width: 8.0, height: 200.0))) } diff --git a/Tests/iOS/ObjectiveCSpec.m b/Tests/Common/ObjectiveCSpec.m similarity index 51% rename from Tests/iOS/ObjectiveCSpec.m rename to Tests/Common/ObjectiveCSpec.m index 10dcf3d6..208b95a2 100644 --- a/Tests/iOS/ObjectiveCSpec.m +++ b/Tests/Common/ObjectiveCSpec.m @@ -1,10 +1,21 @@ +// Copyright (c) 2018 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: // -// ObjectiveCSpec.m -// PinLayout -// -// Created by DION, Luc (MTL) on 2017-11-21. -// Copyright © 2017 mcswiftlayyout.mirego.com. All rights reserved. +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. #import @@ -43,27 +54,34 @@ [Pin initPinLayout]; [Pin layoutDirection:LayoutDirectionLtr]; Pin.safeAreaInsetsDidChangeMode = PinSafeAreaInsetsDidChangeModeAlways; - Pin.layoutDirection = LayoutDirectionLtr; Pin.logWarnings = true; }); it(@"basic pinlayout calls", ^{ - [[[aView pinObjc] top:10] layout]; + aView.pinObjc.topValue(10).layout(); expect(@(aView.frame)).to(equal(@(CGRectMake(40, 10, 100, 60)))); }); it(@"using Pin.logMissingLayoutCalls", ^{ Pin.logMissingLayoutCalls = true; - [[aView pinObjc] top:10]; + aView.pinObjc.topValue(10); //expect(Pin.lastWarningText).to(contain(@"PinLayout commands have been issued without calling the 'layout()' method to complete the layout")); }); it(@"using Pin.logMissingLayoutCalls set to false", ^{ Pin.logMissingLayoutCalls = false; - [[[aView pinObjc] top:10] layout]; + aView.pinObjc.topValue(10).layout(); expect(@(aView.frame)).to(equal(@(CGRectMake(40, 10, 100, 60)))); expect(Pin.lastWarningText).to(beNil()); }); + + it(@"check the access to PinLayout methods from objective-c", ^{ + rootView.pinObjc.wrapContent().layout(); + rootView.pinObjc.wrapContentInsets(UIEdgeInsetsMake(0, 0, 0, 0)).layout(); + rootView.pinObjc.wrapContentTypeInsets(WrapTypeAll, UIEdgeInsetsMake(0, 0, 0, 0)).layout(); + rootView.pinObjc.wrapContentPadding(10).layout(); + rootView.pinObjc.wrapContentTypePadding(WrapTypeHorizontally, 10).layout(); + }); }); }); diff --git a/Tests/Common/PinEdgesSpec.swift b/Tests/Common/PinEdgesSpec.swift index 774d7fb6..65085978 100644 --- a/Tests/Common/PinEdgesSpec.swift +++ b/Tests/Common/PinEdgesSpec.swift @@ -592,11 +592,11 @@ class PinEdgesSpec: QuickSpec { } it("should warn") { aView.pin.left(10).horizontally(20) - expect(Pin.lastWarningText).to(contain(["horizontally(20.0) left coordinate", "won't be applied", "already been set to 10"])) + expect(Pin.lastWarningText).to(contain(["horizontally(20) left coordinate", "won't be applied", "already been set to 10"])) } it("should warn") { aView.pin.right(10).horizontally(20) - expect(Pin.lastWarningText).to(contain(["horizontally(20.0) right coordinate", "won't be applied", "already been set to 10"])) + expect(Pin.lastWarningText).to(contain(["horizontally(20) right coordinate", "won't be applied", "already been set to 10"])) } it("should warn") { aView.pin.left(10).horizontally(10%) @@ -658,11 +658,11 @@ class PinEdgesSpec: QuickSpec { } it("should warn") { aView.pin.top(10).vertically(20) - expect(Pin.lastWarningText).to(contain(["vertically(20.0) top coordinate", "won't be applied", "already been set to 10"])) + expect(Pin.lastWarningText).to(contain(["vertically(20) top coordinate", "won't be applied", "already been set to 10"])) } it("should warn") { aView.pin.bottom(10).vertically(20) - expect(Pin.lastWarningText).to(contain(["vertically(20.0) bottom coordinate", "won't be applied", "already been set to 10"])) + expect(Pin.lastWarningText).to(contain(["vertically(20) bottom coordinate", "won't be applied", "already been set to 10"])) } it("should warn") { aView.pin.top(10).vertically(10%) diff --git a/Tests/Common/PinPointCoordinatesSpec.swift b/Tests/Common/PinPointCoordinatesSpec.swift index 805b93c8..8cc3c6ac 100644 --- a/Tests/Common/PinPointCoordinatesSpec.swift +++ b/Tests/Common/PinPointCoordinatesSpec.swift @@ -123,6 +123,11 @@ class PinPointCoordinatesSpec: QuickSpec { aView.pin.topLeft() expect(aView.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 100.0, height: 60.0))) } + + it("should position the aView's topLeft corner on its parent's topLeft corner") { + aView.pin.topLeft(10) + expect(aView.frame).to(equal(CGRect(x: 10.0, y: 10.0, width: 100.0, height: 60.0))) + } it("should position the aViewChild's topLeft corner on the specified view's topLeft corner") { aViewChild.pin.topLeft(to: aView.anchor.topLeft) @@ -190,6 +195,11 @@ class PinPointCoordinatesSpec: QuickSpec { aView.pin.topCenter() expect(aView.frame).to(equal(CGRect(x: 150.0, y: 0.0, width: 100.0, height: 60.0))) } + + it("should position the aView's topCenter corner on its parent's topCenter corner") { + aView.pin.topCenter(10) + expect(aView.frame).to(equal(CGRect(x: 150.0, y: 10.0, width: 100.0, height: 60.0))) + } it("should position the aViewChild's topCenter corner on the specified view's topCenter corner") { aViewChild.pin.topCenter(to: aView.anchor.topLeft) @@ -257,6 +267,11 @@ class PinPointCoordinatesSpec: QuickSpec { aView.pin.topRight() expect(aView.frame).to(equal(CGRect(x: 300.0, y: 0.0, width: 100.0, height: 60.0))) } + + it("should position the aView's topRight corner at the specified position") { + aView.pin.topRight(10) + expect(aView.frame).to(equal(CGRect(x: 290.0, y: 10.0, width: 100.0, height: 60.0))) + } it("should position the aView's topRight corner at the specified position") { aViewChild.pin.topRight(to: aView.anchor.topLeft) @@ -319,6 +334,11 @@ class PinPointCoordinatesSpec: QuickSpec { aView.pin.centerLeft() expect(aView.frame).to(equal(CGRect(x: 0.0, y: 170.0, width: 100.0, height: 60.0))) } + + it("should position the aView's centerLeft corner at the specified position") { + aView.pin.centerLeft(10) + expect(aView.frame).to(equal(CGRect(x: 10.0, y: 170.0, width: 100.0, height: 60.0))) + } it("should position the aView's centerLeft corner at the specified position") { aViewChild.pin.centerLeft(to: aView.anchor.topLeft) @@ -386,6 +406,11 @@ class PinPointCoordinatesSpec: QuickSpec { aView.pin.center() expect(aView.frame).to(equal(CGRect(x: 150.0, y: 170.0, width: 100.0, height: 60.0))) } + + it("should position the aView's center corner at the specified position") { + aView.pin.center(10) + expect(aView.frame).to(equal(CGRect(x: 160.0, y: 180.0, width: 100.0, height: 60.0))) + } it("should position the aView's center corner at the specified position") { aViewChild.pin.center(to: aView.anchor.topLeft) @@ -444,6 +469,11 @@ class PinPointCoordinatesSpec: QuickSpec { expect(aView.frame).to(equal(CGRect(x: 300.0, y: 170.0, width: 100.0, height: 60.0))) } + it("should position the aView's centerRight corner at the specified position") { + aView.pin.centerRight(10) + expect(aView.frame).to(equal(CGRect(x: 290.0, y: 170.0, width: 100.0, height: 60.0))) + } + it("should position the aView's centerRight corner at the specified position") { aViewChild.pin.centerRight(to: aView.anchor.topLeft) expect(aViewChild.frame).to(equal(CGRect(x: -50.0, y: -15.0, width: 50.0, height: 30.0))) @@ -501,6 +531,11 @@ class PinPointCoordinatesSpec: QuickSpec { expect(aView.frame).to(equal(CGRect(x: 0.0, y: 340.0, width: 100.0, height: 60.0))) } + it("should position the aView's bottomLeft corner at the specified position") { + aView.pin.bottomLeft(10) + expect(aView.frame).to(equal(CGRect(x: 10.0, y: 330.0, width: 100.0, height: 60.0))) + } + it("should position the aView's bottomLeft corner at the specified position") { aViewChild.pin.bottomLeft(to: aView.anchor.topLeft) expect(aViewChild.frame).to(equal(CGRect(x: 0.0, y: -30.0, width: 50.0, height: 30.0))) @@ -558,6 +593,11 @@ class PinPointCoordinatesSpec: QuickSpec { expect(aView.frame).to(equal(CGRect(x: 150.0, y: 340.0, width: 100.0, height: 60.0))) } + it("should position the aView's bottomCenter corner at the specified position") { + aView.pin.bottomCenter(10) + expect(aView.frame).to(equal(CGRect(x: 150.0, y: 330.0, width: 100.0, height: 60.0))) + } + it("should position the aView's bottomCenter corner at the specified position") { aViewChild.pin.bottomCenter(to: aView.anchor.topLeft) expect(aViewChild.frame).to(equal(CGRect(x: -25.0, y: -30.0, width: 50.0, height: 30.0))) @@ -615,6 +655,11 @@ class PinPointCoordinatesSpec: QuickSpec { expect(aView.frame).to(equal(CGRect(x: 300.0, y: 340.0, width: 100.0, height: 60.0))) } + it("should position the aView's bottomRight corner at the specified position") { + aView.pin.bottomRight(10) + expect(aView.frame).to(equal(CGRect(x: 290.0, y: 330.0, width: 100.0, height: 60.0))) + } + it("should position the aView's bottomRight corner at the specified position") { aViewChild.pin.bottomRight(to: aView.anchor.topLeft) expect(aViewChild.frame).to(equal(CGRect(x: -50.0, y: -30.0, width: 50.0, height: 30.0))) diff --git a/Tests/Common/RTLSpec.swift b/Tests/Common/RTLSpec.swift index a2e1c8e2..8fd53122 100644 --- a/Tests/Common/RTLSpec.swift +++ b/Tests/Common/RTLSpec.swift @@ -626,5 +626,13 @@ class RTLSpec: QuickSpec { expect(aView.frame).to(equal(CGRect(x: 160.0, y: 200.0, width: 100.0, height: 60.0))) } } + + // TODO +// cView.pin.verticallyBetween(aView, and: bView, aligned: .start) +// cView.pin.verticallyBetween(bView, and: aView, aligned: .start) +// +// cView.pin.verticallyBetween(aView, and: bView, aligned: .end) +// cView.pin.verticallyBetween(bView, and: aView, aligned: .end) + } } diff --git a/Tests/Common/RelativePositionMultipleViewsSpec.swift b/Tests/Common/RelativePositionMultipleViewsSpec.swift index 67229d40..a5e67bc5 100644 --- a/Tests/Common/RelativePositionMultipleViewsSpec.swift +++ b/Tests/Common/RelativePositionMultipleViewsSpec.swift @@ -46,8 +46,9 @@ class RelativePositionMultipleViewsSpec: QuickSpec { */ beforeEach { + _pinlayoutSetUnitTest(scale: 2) Pin.lastWarningText = nil - + viewController = PViewController() viewController.view = BasicView() @@ -71,6 +72,10 @@ class RelativePositionMultipleViewsSpec: QuickSpec { bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) bView.addSubview(bViewChild) } + + afterEach { + _pinlayoutSetUnitTest(scale: nil) + } // // above(of: UIViews.....) warnings @@ -80,21 +85,39 @@ class RelativePositionMultipleViewsSpec: QuickSpec { let unatachedView = PView() bViewChild.pin.above(of: unatachedView) expect(bViewChild.frame).to(equal(CGRect(x: 40, y: 10, width: 60, height: 20))) - expect(Pin.lastWarningText).to(contain(["above", "won't be applied", "no valid references"])) + expect(Pin.lastWarningText).to(contain(["above(of: (", ")", "won't be applied", + "must be added as a sub-view before being used as a reference."])) + } + + it("should warns the view bottom edge") { + let unatachedView = PView() + bViewChild.pin.above(of: unatachedView, aligned: .left) + expect(bViewChild.frame).to(equal(CGRect(x: 40, y: 10, width: 60, height: 20))) + expect(Pin.lastWarningText).to(contain(["above(of: ", ", aligned: .left)", "won't be applied", + "must be added as a sub-view before being used as a reference."])) } it("should warns the view bottom edge") { let unatachedView = PView() bViewChild.pin.above(of: [aView, unatachedView]) expect(bViewChild.frame).to(equal(CGRect(x: 40.0, y: -40.0, width: 60.0, height: 20.0))) - expect(Pin.lastWarningText).to(contain(["above", "won't be applied", "the reference view", "must be added", "as a reference"])) + expect(Pin.lastWarningText).to(contain(["above", "won't be applied", "the reference view", + "must be added as a sub-view before being used as a reference."])) } it("Should warn, but the view should be anyway layout it above") { let unatachedView = PView() bViewChild.pin.above(of: [aView, unatachedView]) expect(bViewChild.frame).to(equal(CGRect(x: 40.0, y: -40.0, width: 60.0, height: 20.0))) - expect(Pin.lastWarningText).to(contain(["above", "won't be applied", "the reference view", "must be added", "as a reference"])) + expect(Pin.lastWarningText).to(contain(["above(of: [", "])", "won't be applied", + "the reference view", "must be added", "as a reference"])) + } + + it("Should warn, but the view should be anyway layout it above") { + let unatachedView = PView() + bViewChild.pin.above(of: [aView, unatachedView], aligned: .center) + expect(bViewChild.frame).to(equal(CGRect(x: -100.0, y: -40.0, width: 60.0, height: 20.0))) + expect(Pin.lastWarningText).to(contain(["above(of: [", "], aligned: .center)", "won't be applied", "the reference view", "must be added", "as a reference"])) } } diff --git a/Tests/Common/WarningSpec.swift b/Tests/Common/WarningSpec.swift index aafecc55..25fadfd3 100644 --- a/Tests/Common/WarningSpec.swift +++ b/Tests/Common/WarningSpec.swift @@ -33,11 +33,8 @@ class WarningSpec: QuickSpec { - aView */ - beforeSuite { - _pinlayoutSetUnitTest(displayScale: 2) - } - beforeEach { + _pinlayoutSetUnitTest(scale: 2) Pin.lastWarningText = nil viewController = PViewController() @@ -52,6 +49,10 @@ class WarningSpec: QuickSpec { aView.sizeThatFitsExpectedArea = 40 * 40 rootView.addSubview(aView) } + + afterEach { + _pinlayoutSetUnitTest(scale: nil) + } // // pinEdges warnings diff --git a/Tests/Common/WrapContentSpec.swift b/Tests/Common/WrapContentSpec.swift new file mode 100644 index 00000000..126c4b54 --- /dev/null +++ b/Tests/Common/WrapContentSpec.swift @@ -0,0 +1,377 @@ +// Copyright (c) 2018 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Quick +import Nimble +import PinLayout + +class WrapContentSpec: QuickSpec { + override func spec() { + var viewController: PViewController! + var rootView: BasicView! + + /* + root + | + - aView + | | + | |- aViewChild + |- aViewChild2 + |- aViewChild3 + */ + var aView: BasicView! + var aViewChild: BasicView! + var aViewChild2: BasicView! + var aViewChild3: BasicView! + + beforeEach { + _pinlayoutSetUnitTest(scale: 2) + Pin.lastWarningText = nil + + viewController = PViewController() + viewController.view = BasicView() + + rootView = BasicView() + viewController.view.addSubview(rootView) + + aView = BasicView() + aView.sizeThatFitsExpectedArea = 40 * 40 + rootView.addSubview(aView) + + aViewChild = BasicView() + aView.addSubview(aViewChild) + + aViewChild2 = BasicView() + aView.addSubview(aViewChild2) + + aViewChild3 = BasicView() + aView.addSubview(aViewChild3) + + rootView.frame = CGRect(x: 0, y: 100, width: 400, height: 400) + } + + afterEach { + _pinlayoutSetUnitTest(scale: nil) + } + + describe("wrapContent") { + it("wrap and update subviews position") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 120, width: 60, height: 60) + + aView.pin.wrapContent() + + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 260.0, height: 60.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 100.0, y: 0.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 200.0, y: 0.0, width: 60.0, height: 60.0))) + } + + it("wrapContent(.all) should have the same result") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 120, width: 60, height: 60) + + aView.pin.wrapContent(.all) + + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 260.0, height: 60.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 100.0, y: 0.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 200.0, y: 0.0, width: 60.0, height: 60.0))) + } + + it("wrapContent(.width) + wrapContent(.height) should have the same result") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 120, width: 60, height: 60) + + aView.pin.wrapContent(.horizontally) + aView.pin.wrapContent(.vertically) + + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 260.0, height: 60.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 100.0, y: 0.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 200.0, y: 0.0, width: 60.0, height: 60.0))) + } + + it("wrapContent(.all) and update subviews position") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 120, width: 60, height: 60) + + aView.pin.wrapContent(.all) + + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 260.0, height: 60.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 100.0, y: 0.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 200.0, y: 0.0, width: 60.0, height: 60.0))) + } + + it("wrapContent(.width) and update subviews position") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 120, width: 60, height: 60) + + aView.pin.wrapContent(.horizontally) + + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 260.0, height: 100.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 0.0, y: 120.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 100.0, y: 120.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 200.0, y: 120.0, width: 60.0, height: 60.0))) + } + it("wrapContent(.height) and update subviews position") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 120, width: 60, height: 60) + + aView.pin.wrapContent(.vertically) + + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 200.0, height: 60.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 160.0, y: 0.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 260.0, y: 0.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 360.0, y: 0.0, width: 60.0, height: 60.0))) + } + it("wrap and update subviews position") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 180, y: 140, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 220, y: 180, width: 60, height: 60) + + aView.pin.wrapContent() + + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 120.0, height: 120.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 20.0, y: 20.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 60.0, y: 60.0, width: 60.0, height: 60.0))) + } + + it("wrap when views are of size zero") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 0, height: 0) + aViewChild2.frame = CGRect(x: 180, y: 140, width: 0, height: 0) + aViewChild3.frame = CGRect(x: 220, y: 180, width: 0, height: 0) + + aView.pin.wrapContent() + + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 60.0, height: 60.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 0.0, height: 0.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 20.0, y: 20.0, width: 0.0, height: 0.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 60.0, y: 60.0, width: 0.0, height: 0.0))) + } + + it("wrap with subviews with negative position") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: -40, y: -40, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 350, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 350, y: -100, width: 60, height: 60) + + aView.pin.wrapContent() + + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 450.0, height: 280.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 0.0, y: 60.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 390.0, y: 220.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 390.0, y: 0.0, width: 60.0, height: 60.0))) + } + } + + describe("wrapContent with padding") { + it("wrap and update subviews position") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 120, width: 60, height: 60) + + aView.pin.wrapContent(padding: 10) + + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 280.0, height: 80.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 10.0, y: 10.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 110.0, y: 10.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 210.0, y: 10.0, width: 60.0, height: 60.0))) + + } + it("wrap and update subviews position + center") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 120, width: 60, height: 60) + + aView.pin.wrapContent(padding: 10).center() + + expect(aView.frame).to(equal(CGRect(x: 60.0, y: 160.0, width: 280.0, height: 80.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 10.0, y: 10.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 110.0, y: 10.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 210.0, y: 10.0, width: 60.0, height: 60.0))) + } + + it("wrap horizontally + padding") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 120, width: 60, height: 60) + + aView.pin.wrapContent(.horizontally, padding: 10) + + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 280.0, height: 100.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 10.0, y: 120.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 110.0, y: 120.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 210.0, y: 120.0, width: 60.0, height: 60.0))) + } + + it("wrap vertically + padding") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 140, width: 60, height: 60) + + aView.pin.wrapContent(.vertically, padding: 10) + + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 200.0, height: 100.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 160.0, y: 10.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 260.0, y: 10.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 360.0, y: 30.0, width: 60.0, height: 60.0))) + } + + it("wrap horizontally + negative padding") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 160, width: 60, height: 60) + aView.pin.wrapContent(.horizontally, padding: -10) + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 240.0, height: 100.0))) + expect(aViewChild.frame).to(equal(CGRect(x: -10.0, y: 120.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 90.0, y: 120.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 190.0, y: 160.0, width: 60.0, height: 60.0))) + } + + it("wrap vertically + negative padding") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 160, width: 60, height: 60) + + aView.pin.wrapContent(.vertically, padding: -10) + + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 200.0, height: 80.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 160.0, y: -10.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 260.0, y: -10.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 360.0, y: 30.0, width: 60.0, height: 60.0))) + } + + it("wrap all + padding UIEdgeInsets") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 140, width: 60, height: 60) + aView.pin.wrapContent(padding: PEdgeInsets(top: 10, left: 20, bottom: 30, right: 40)) + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 320.0, height: 120.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 120.0, y: 10.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 220.0, y: 30.0, width: 60.0, height: 60.0))) + } + + it("wrap all + padding UIEdgeInsets") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 140, width: 60, height: 60) + aView.pin.wrapContent(.all, padding: PEdgeInsets(top: 10, left: 20, bottom: 30, right: 40)) + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 320.0, height: 120.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 120.0, y: 10.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 220.0, y: 30.0, width: 60.0, height: 60.0))) + + } + + it("wrap horizontally + padding PEdgeInsets") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 140, width: 60, height: 60) + aView.pin.wrapContent(.horizontally, padding: PEdgeInsets(top: 10, left: 20, bottom: 30, right: 40)) + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 320.0, height: 100.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 20.0, y: 120.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 120.0, y: 120.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 220.0, y: 140.0, width: 60.0, height: 60.0))) + + } + + it("wrap vertically + padding PEdgeInsets") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 140, width: 60, height: 60) + aView.pin.wrapContent(.vertically, padding: PEdgeInsets(top: 10, left: 20, bottom: 30, right: 40)) + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 200.0, height: 120.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 160.0, y: 10.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 260.0, y: 10.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 360.0, y: 30.0, width: 60.0, height: 60.0))) + + } + + it("wrap all + negative padding PEdgeInsets") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 120, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 140, width: 60, height: 60) + aView.pin.wrapContent(.all, padding: PEdgeInsets(top: -10, left: -20, bottom: -30, right: -40)) + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 200.0, height: 40.0))) + expect(aViewChild.frame).to(equal(CGRect(x: -20.0, y: -10.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 80.0, y: -10.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 180.0, y: 10.0, width: 60.0, height: 60.0))) + } + } + + describe("wrapContent + min/max") { + it("wrap all + maxWidth + maxHeight") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 130, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 140, width: 60, height: 60) + + aView.pin.wrapContent().maxWidth(200).maxHeight(50) + + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 200.0, height: 50.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 100.0, y: 10.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 200.0, y: 20.0, width: 60.0, height: 60.0))) + } + + it("wrap all + minWidth + minHeight") { + aView.frame = CGRect(x: 20, y: 10, width: 200, height: 100) + aViewChild.frame = CGRect(x: 160, y: 120, width: 100, height: 40) + aViewChild2.frame = CGRect(x: 260, y: 130, width: 60, height: 60) + aViewChild3.frame = CGRect(x: 360, y: 140, width: 60, height: 60) + + aView.pin.wrapContent().minWidth(300).minHeight(100) + + expect(aView.frame).to(equal(CGRect(x: 20.0, y: 10.0, width: 300.0, height: 100.0))) + expect(aViewChild.frame).to(equal(CGRect(x: 0.0, y: 0.0, width: 100.0, height: 40.0))) + expect(aViewChild2.frame).to(equal(CGRect(x: 100.0, y: 10.0, width: 60.0, height: 60.0))) + expect(aViewChild3.frame).to(equal(CGRect(x: 200.0, y: 20.0, width: 60.0, height: 60.0))) + } + } + } +} diff --git a/Tests/RectNimbleMatcher.swift b/Tests/RectNimbleMatcher.swift index 10968f76..af174cf3 100644 --- a/Tests/RectNimbleMatcher.swift +++ b/Tests/RectNimbleMatcher.swift @@ -23,19 +23,19 @@ public func beCloseTo(_ expectedValue: CGRect, within delta: CGFloat = 0.00001) let errorMessage = "be close to <\(stringify(expectedValue))> (each within \(stringify(delta)))" return Predicate.simple(errorMessage) { actualExpression in if let actual = try actualExpression.evaluate() { - if fabs(actual.origin.x - expectedValue.origin.x) > delta { + if abs(actual.origin.x - expectedValue.origin.x) > delta { return .doesNotMatch } - if fabs(actual.origin.y - expectedValue.origin.y) > delta { + if abs(actual.origin.y - expectedValue.origin.y) > delta { return .doesNotMatch } - if fabs(actual.size.width - expectedValue.size.width) > delta { + if abs(actual.size.width - expectedValue.size.width) > delta { return .doesNotMatch } - if fabs(actual.size.height - expectedValue.size.height) > delta { + if abs(actual.size.height - expectedValue.size.height) > delta { return .doesNotMatch } diff --git a/Tests/UIImage+Color.swift b/Tests/UIImage+Color.swift index 14fb98b5..be71c723 100644 --- a/Tests/UIImage+Color.swift +++ b/Tests/UIImage+Color.swift @@ -17,11 +17,10 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - import UIKit public extension UIImage { - public convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) { + convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) { let rect = CGRect(origin: .zero, size: size) UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0) color.setFill() diff --git a/Tests/iOS/PinSafeAreaTests.swift b/Tests/iOS/PinSafeAreaTests.swift index 8858048e..74b11cae 100644 --- a/Tests/iOS/PinSafeAreaTests.swift +++ b/Tests/iOS/PinSafeAreaTests.swift @@ -45,6 +45,7 @@ class PinSafeAreaSpec: QuickSpec { window = UIWindow() window.rootViewController = viewController window.addSubview(viewController.view) + window.makeKeyAndVisible(); // Testing UIViewController's layout methods is kind of bad // but needed in our case so we need to wait some time @@ -54,29 +55,29 @@ class PinSafeAreaSpec: QuickSpec { describe("using opaque NavigationBar") { it("testOpaqueNavigationBar") { let mainView = viewController.mainView + mainView.layoutOffsetViewClosure = { _, _ in } + navigationController.navigationBar.isTranslucent = true + + setupWindow(with: navigationController) + let safeAreaInsetsWhenTranslucent = mainView.safeAreaInsets + let screenSizeWhenTranslucent = mainView.frame.size mainView.layoutOffsetViewClosure = { (_ offsetView: UIView, _ parent: UIView) in offsetView.pin.top(10).width(100).height(100) } - navigationController.navigationBar.barStyle = .blackOpaque navigationController.navigationBar.isTranslucent = false setupWindow(with: navigationController) - - let expectedSafeAreaInsets = UIEdgeInsets.zero + + let expectedSafeAreaInsets = mainView.safeAreaInsets let expectedOffsetViewSafeAreaInsets = UIEdgeInsets.zero - if #available(iOS 11.0, tvOS 11.0, *) { - expect(viewController.view.safeAreaInsets).to(equal(expectedSafeAreaInsets)) - expect(mainView.offsetView.safeAreaInsets).to(equal(expectedOffsetViewSafeAreaInsets)) - } - expect(mainView.pin.safeArea).to(equal(expectedSafeAreaInsets)) expect(mainView.offsetView.pin.safeArea).to(equal(expectedOffsetViewSafeAreaInsets)) - expect(mainView.safeAreaInsetsDidChangeCalledCount).to(equal(0)) + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 let screenSize = mainView.frame.size - expect(mainView.frame).to(equal(CGRect(x: 0, y: 44, width: screenSize.width, height: screenSize.height))) + expect(mainView.frame).to(equal(CGRect(x: 0, y: safeAreaInsetsWhenTranslucent.top, width: screenSize.width, height: screenSizeWhenTranslucent.height - safeAreaInsetsWhenTranslucent.top))) expect(mainView.offsetView.frame).to(equal(CGRect(x: 0, y: 10, width: 100, height: 100))) } } @@ -84,100 +85,78 @@ class PinSafeAreaSpec: QuickSpec { describe("using translucent NavigationBar") { it("default") { let mainView = viewController.mainView - var expectedSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 3 } - } mainView.layoutOffsetViewClosure = { (_ offsetView: UIView, _ parent: UIView) in offsetView.pin.all().margin(parent.pin.safeArea) } - let expectedSafeAreaInsets = UIEdgeInsets(top: 44, left: 0, bottom: 0, right: 0) - let expectedOffsetViewSafeAreaInsets = UIEdgeInsets.zero - - navigationController.navigationBar.barStyle = .blackTranslucent + navigationController.navigationBar.isTranslucent = true setupWindow(with: navigationController) - if #available(iOS 11.0, tvOS 11.0, *) { - XCTAssertEqual(viewController.view.safeAreaInsets, expectedSafeAreaInsets) - XCTAssertEqual(mainView.offsetView.safeAreaInsets, expectedOffsetViewSafeAreaInsets) - } + let expectedSafeAreaInsets = mainView.safeAreaInsets + let expectedOffsetViewSafeAreaInsets = UIEdgeInsets.zero + XCTAssertEqual(mainView.pin.safeArea, expectedSafeAreaInsets) XCTAssertEqual(mainView.offsetView.pin.safeArea, expectedOffsetViewSafeAreaInsets) - expect(mainView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSafeAreaInsetsDidChangeCalledCount)) - + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 + let screenSize = mainView.frame.size XCTAssertEqual(mainView.frame, CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height)) - XCTAssertEqual(mainView.offsetView.frame, CGRect(x: 0, y: 44, width: screenSize.width, height: screenSize.height - 44)) + XCTAssertEqual(mainView.offsetView.frame, CGRect(x: 0, y: expectedSafeAreaInsets.top, width: screenSize.width, height: screenSize.height - (expectedSafeAreaInsets.top + expectedSafeAreaInsets.bottom))) } it("with OffsetView") { + let offsetViewTop = CGFloat(10) let mainView = viewController.mainView - var expectedSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 3 } - } mainView.layoutOffsetViewClosure = { (_ offsetView: UIView, _ parent: UIView) in - offsetView.pin.top(10).width(100).height(100) + offsetView.pin.top(offsetViewTop).width(100).height(100) } - let expectedSafeAreaInsets = UIEdgeInsets(top: 44, left: 0, bottom: 0, right: 0) - let expectedOffsetViewSafeAreaInsets = UIEdgeInsets(top: 34, left: 0, bottom: 0, right: 0) - - navigationController.navigationBar.barStyle = .blackTranslucent + navigationController.navigationBar.isTranslucent = true setupWindow(with: navigationController) + + let expectedSafeAreaInsets = mainView.safeAreaInsets + let expectedOffsetViewSafeAreaInsets = UIEdgeInsets( + top: mainView.safeAreaInsets.top - offsetViewTop, + left: .zero, + bottom: .zero, + right: .zero + ) - if #available(iOS 11.0, tvOS 11.0, *) { - XCTAssertEqual(viewController.view.safeAreaInsets, expectedSafeAreaInsets) - XCTAssertEqual(mainView.offsetView.safeAreaInsets, expectedOffsetViewSafeAreaInsets) - } XCTAssertEqual(mainView.pin.safeArea, expectedSafeAreaInsets) XCTAssertEqual(mainView.offsetView.pin.safeArea, expectedOffsetViewSafeAreaInsets) let screenSize = mainView.frame.size XCTAssertEqual(mainView.frame, CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height)) - XCTAssertEqual(mainView.offsetView.frame, CGRect(x: 0, y: 10, width: 100, height: 100)) - expect(mainView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSafeAreaInsetsDidChangeCalledCount)) + XCTAssertEqual(mainView.offsetView.frame, CGRect(x: 0, y: offsetViewTop, width: 100, height: 100)) + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 } it("with OffsetView and AdditionalSafeAreaInsets") { let mainView = viewController.mainView - var expectedSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 2 } else { return 3 } - } mainView.layoutOffsetViewClosure = { (_ offsetView: UIView, _ parent: UIView) in offsetView.pin.all().margin(parent.pin.safeArea) } - navigationController.navigationBar.barStyle = .blackTranslucent + navigationController.navigationBar.isTranslucent = true + viewController.additionalSafeAreaInsets = UIEdgeInsets(top: 10, left: 10, bottom: 30, right: 0) setupWindow(with: navigationController) - let expectedSafeAreaInsets: UIEdgeInsets - let expectedOffsetViewSafeAreaInsets: UIEdgeInsets - let expectedOffsetViewFrame: CGRect let screenSize = mainView.frame.size + let expectedSafeAreaInsets = mainView.safeAreaInsets + let expectedOffsetViewSafeAreaInsets = UIEdgeInsets.zero + let expectedOffsetViewFrame = CGRect( + x: expectedSafeAreaInsets.left, + y: expectedSafeAreaInsets.top, + width: screenSize.width - (expectedSafeAreaInsets.left + expectedSafeAreaInsets.right), + height: screenSize.height - (expectedSafeAreaInsets.top + expectedSafeAreaInsets.bottom) + ) - if #available(iOS 11.0, tvOS 11.0, *) { - viewController.additionalSafeAreaInsets = UIEdgeInsets(top: 10, left: 10, bottom: 30, right: 0) - expectedSafeAreaInsets = UIEdgeInsets(top: 54, left: 10, bottom: 30, right: 0) - expectedOffsetViewSafeAreaInsets = UIEdgeInsets.zero - expectedOffsetViewFrame = CGRect(x: 0, y: 44, width: screenSize.width, - height: screenSize.height - 44) - } else { - expectedSafeAreaInsets = UIEdgeInsets(top: 44, left: 0, bottom: 0, right: 0) - expectedOffsetViewSafeAreaInsets = UIEdgeInsets.zero - expectedOffsetViewFrame = CGRect(x: 0, y: 44, width: screenSize.width, - height: screenSize.height - expectedSafeAreaInsets.top) - } - - if #available(iOS 11.0, tvOS 11.0, *) { - XCTAssertEqual(viewController.view.safeAreaInsets, expectedSafeAreaInsets) - XCTAssertEqual(mainView.offsetView.safeAreaInsets, expectedOffsetViewSafeAreaInsets) - } XCTAssertEqual(mainView.pin.safeArea, expectedSafeAreaInsets) XCTAssertEqual(mainView.offsetView.pin.safeArea, expectedOffsetViewSafeAreaInsets) - expect(mainView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSafeAreaInsetsDidChangeCalledCount)) + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 XCTAssertEqual(mainView.frame, CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height)) XCTAssertEqual(mainView.offsetView.frame, expectedOffsetViewFrame) @@ -185,34 +164,29 @@ class PinSafeAreaSpec: QuickSpec { it("with OffsetView and AdditionalSafeAreaInsets 2") { let mainView = viewController.mainView - var expectedSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 3 } - } + let offsetViewTop = CGFloat(10) mainView.layoutOffsetViewClosure = { (_ offsetView: UIView, _ parent: UIView) in - offsetView.pin.top(10).width(100).height(100) + offsetView.pin.top(offsetViewTop).width(100).height(100) } - let expectedSafeAreaInsets: UIEdgeInsets - let expectedOffsetViewSafeAreaInsets: UIEdgeInsets - if #available(iOS 11.0, tvOS 11.0, *) { - viewController.additionalSafeAreaInsets = UIEdgeInsets(top: 10, left: 10, bottom: 30, right: 0) - expectedSafeAreaInsets = UIEdgeInsets(top: 54, left: 10, bottom: 30, right: 0) - expectedOffsetViewSafeAreaInsets = UIEdgeInsets(top: 44, left: 10, bottom: 0, right: 0) - } else { - expectedSafeAreaInsets = UIEdgeInsets(top: 44, left: 0, bottom: 0, right: 0) - expectedOffsetViewSafeAreaInsets = UIEdgeInsets(top: 34, left: 0, bottom: 0, right: 0) - } - navigationController.navigationBar.barStyle = .blackTranslucent + navigationController.navigationBar.isTranslucent = true setupWindow(with: navigationController) - - if #available(iOS 11.0, tvOS 11.0, *) { - XCTAssertEqual(viewController.view.safeAreaInsets, expectedSafeAreaInsets) - XCTAssertEqual(mainView.offsetView.safeAreaInsets, expectedOffsetViewSafeAreaInsets) - } + + let expectedSafeAreaInsets = mainView.safeAreaInsets + let expectedOffsetViewSafeAreaInsets = UIEdgeInsets( + top: expectedSafeAreaInsets.top - offsetViewTop, + left: .zero, + bottom: .zero, + right: .zero + ) + + XCTAssertEqual(viewController.view.safeAreaInsets, expectedSafeAreaInsets) + XCTAssertEqual(mainView.offsetView.safeAreaInsets, expectedOffsetViewSafeAreaInsets) + XCTAssertEqual(mainView.pin.safeArea, expectedSafeAreaInsets) XCTAssertEqual(mainView.offsetView.pin.safeArea, expectedOffsetViewSafeAreaInsets) - expect(mainView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSafeAreaInsetsDidChangeCalledCount)) + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 } } } @@ -277,6 +251,7 @@ class PinSafeAreaMoreTestsSpec: QuickSpec { window = UIWindow() window.rootViewController = viewController window.addSubview(viewController.view) + window.makeKeyAndVisible(); // Testing UIViewController's layout methods is kind of bad // but needed in our case so we need to wait some time @@ -286,144 +261,163 @@ class PinSafeAreaMoreTestsSpec: QuickSpec { describe("navigationbar + subview") { it("transluscent navigationbar 1") { let mainView = viewController.mainView - var expectedSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 3 } - } - var expectedSubViewSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 2 } - } - + let subViewTop = CGFloat(10) + mainView.layoutOffsetViewClosure = { (_ subView: UIView, _ parent: UIView) in - subView.pin.top(10).left(10).size(100) + subView.pin.top(subViewTop).left(10).size(100) } - navigationController.navigationBar.barStyle = .blackTranslucent + navigationController.navigationBar.isTranslucent = true setupWindow(with: navigationController) + + let expectedSafeAreaInsets = mainView.safeAreaInsets + let expectedSubViewSafeAreaInsets = UIEdgeInsets( + top: expectedSafeAreaInsets.top - subViewTop, + left: .zero, + bottom: .zero, + right: .zero + ) // MATCH safeAreaInsets! - expect(mainView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSafeAreaInsetsDidChangeCalledCount)) - expect(mainView.subView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSubViewSafeAreaInsetsDidChangeCalledCount)) + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 + expect(mainView.subView.safeAreaInsetsDidChangeCalledCount) > 0 - expect(mainView.pin.safeArea).to(equal(UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0))) - expect(mainView.subView.pin.safeArea).to(equal(UIEdgeInsets(top: 34.0, left: 0.0, bottom: 0.0, right: 0.0))) + expect(mainView.pin.safeArea).to(equal(expectedSafeAreaInsets)) + expect(mainView.subView.pin.safeArea).to(equal(expectedSubViewSafeAreaInsets)) expect(mainView.subView.subViewB!.pin.safeArea).to(equal(UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0))) expect(mainView.subView.frame).to(equal(CGRect(x: 10, y: 10, width: 100, height: 100))) - expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: 34, width: 40, height: 40))) + expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: expectedSubViewSafeAreaInsets.top, width: 40, height: 40))) } it("transluscent navigationbar 2") { let mainView = viewController.mainView - var expectedSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 3 } - } - var expectedSubViewSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 2 } - } + let subViewTop = CGFloat(10) + let subViewLeft = CGFloat(-10) mainView.layoutOffsetViewClosure = { (_ subView: UIView, _ parent: UIView) in - subView.pin.top(10).left(-10).size(100) + subView.pin.top(subViewTop).left(subViewLeft).size(100) } - navigationController.navigationBar.barStyle = .blackTranslucent + navigationController.navigationBar.isTranslucent = true setupWindow(with: navigationController) + + let expectedSafeAreaInsets = mainView.safeAreaInsets + let expectedSubViewSafeAreaInsets = UIEdgeInsets( + top: expectedSafeAreaInsets.top - subViewTop, + left: .zero, + bottom: .zero, + right: .zero + ) // MATCH safeAreaInsets! - expect(mainView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSafeAreaInsetsDidChangeCalledCount)) - expect(mainView.subView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSubViewSafeAreaInsetsDidChangeCalledCount)) + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 + expect(mainView.subView.safeAreaInsetsDidChangeCalledCount) > 0 - expect(mainView.pin.safeArea).to(equal(UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0))) - expect(mainView.subView.pin.safeArea).to(equal(UIEdgeInsets(top: 34.0, left: 0.0, bottom: 0.0, right: 0.0))) + expect(mainView.pin.safeArea).to(equal(expectedSafeAreaInsets)) + expect(mainView.subView.pin.safeArea).to(equal(expectedSubViewSafeAreaInsets)) expect(mainView.subView.subViewB!.pin.safeArea).to(equal(UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0))) - expect(mainView.subView.frame).to(equal(CGRect(x: -10, y: 10, width: 100, height: 100))) - expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: 34, width: 40, height: 40))) + expect(mainView.subView.frame).to(equal(CGRect(x: subViewLeft, y: subViewTop, width: 100, height: 100))) + expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: expectedSubViewSafeAreaInsets.top, width: 40, height: 40))) } it("transluscent navigationbar 3") { let mainView = viewController.mainView - var expectedSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 3 } - } - var expectedSubViewSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 2 } - } + let subViewTop = CGFloat(10) + let subViewRight = CGFloat(10) mainView.layoutOffsetViewClosure = { (_ subView: UIView, _ parent: UIView) in - subView.pin.top(10).right(10).size(100) + subView.pin.top(subViewTop).right(subViewRight).size(100) } - navigationController.navigationBar.barStyle = .blackTranslucent + navigationController.navigationBar.isTranslucent = true setupWindow(with: navigationController) + + let expectedSafeAreaInsets = mainView.safeAreaInsets + let expectedSubViewSafeAreaInsets = UIEdgeInsets( + top: expectedSafeAreaInsets.top - subViewTop, + left: .zero, + bottom: .zero, + right: .zero + ) // MATCH safeAreaInsets! - expect(mainView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSafeAreaInsetsDidChangeCalledCount)) - expect(mainView.subView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSubViewSafeAreaInsetsDidChangeCalledCount)) + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 + expect(mainView.subView.safeAreaInsetsDidChangeCalledCount) > 0 - expect(mainView.pin.safeArea).to(equal(UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0))) - expect(mainView.subView.pin.safeArea).to(equal(UIEdgeInsets(top: 34.0, left: 0.0, bottom: 0.0, right: 0.0))) + expect(mainView.pin.safeArea).to(equal(expectedSafeAreaInsets)) + expect(mainView.subView.pin.safeArea).to(equal(expectedSubViewSafeAreaInsets)) expect(mainView.subView.subViewB!.pin.safeArea).to(equal(UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0))) let screenSize = mainView.frame.size - expect(mainView.subView.frame).to(equal(CGRect(x: screenSize.width - 100 - 10, y: 10, width: 100, height: 100))) - expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: 34, width: 40, height: 40))) + expect(mainView.subView.frame).to(equal(CGRect(x: screenSize.width - 100 - subViewRight, y: subViewTop, width: 100, height: 100))) + expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: expectedSubViewSafeAreaInsets.top, width: 40, height: 40))) } it("transluscent navigationbar 4") { let mainView = viewController.mainView - var expectedSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 3 } - } - var expectedSubViewSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 2 } - } - + let subViewTop = CGFloat(10) + let subViewRight = CGFloat(-10) + mainView.layoutOffsetViewClosure = { (_ subView: UIView, _ parent: UIView) in - subView.pin.top(10).right(-10).size(100) + subView.pin.top(subViewTop).right(subViewRight).size(100) } - navigationController.navigationBar.barStyle = .blackTranslucent + navigationController.navigationBar.isTranslucent = true setupWindow(with: navigationController) + + let expectedSafeAreaInsets = mainView.safeAreaInsets + let expectedSubViewSafeAreaInsets = UIEdgeInsets( + top: expectedSafeAreaInsets.top - subViewTop, + left: .zero, + bottom: .zero, + right: .zero + ) // MATCH safeAreaInsets! - expect(mainView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSafeAreaInsetsDidChangeCalledCount)) - expect(mainView.subView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSubViewSafeAreaInsetsDidChangeCalledCount)) + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 + expect(mainView.subView.safeAreaInsetsDidChangeCalledCount) > 0 - expect(mainView.pin.safeArea).to(equal(UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0))) - expect(mainView.subView.pin.safeArea).to(equal(UIEdgeInsets(top: 34.0, left: 0.0, bottom: 0.0, right: 0.0))) + expect(mainView.pin.safeArea).to(equal(expectedSafeAreaInsets)) + expect(mainView.subView.pin.safeArea).to(equal(expectedSubViewSafeAreaInsets)) expect(mainView.subView.subViewB!.pin.safeArea).to(equal(UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0))) let screenSize = mainView.frame.size - expect(mainView.subView.frame).to(equal(CGRect(x: screenSize.width - 100 + 10, y: 10, width: 100, height: 100))) - expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: 34, width: 40, height: 40))) + expect(mainView.subView.frame).to(equal(CGRect(x: screenSize.width - 100 - subViewRight, y: subViewTop, width: 100, height: 100))) + expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: expectedSubViewSafeAreaInsets.top, width: 40, height: 40))) } it("transluscent navigationbar 5") { let mainView = viewController.mainView - var expectedSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 3 } - } - var expectedSubViewSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 2 } else { return 1 } - } - + let subViewTop = CGFloat(-20) + let subViewLeft = CGFloat(-10) + mainView.layoutOffsetViewClosure = { (_ subView: UIView, _ parent: UIView) in - subView.pin.top(-20).left(-10).size(100) + subView.pin.top(subViewTop).left(subViewLeft).size(100) } - navigationController.navigationBar.barStyle = .blackTranslucent + navigationController.navigationBar.isTranslucent = true setupWindow(with: navigationController) + + let expectedSafeAreaInsets = mainView.safeAreaInsets + let expectedSubViewSafeAreaInsets = UIEdgeInsets( + top: expectedSafeAreaInsets.top, + left: .zero, + bottom: .zero, + right: .zero + ) // MATCH safeAreaInsets! - expect(mainView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSafeAreaInsetsDidChangeCalledCount)) - expect(mainView.subView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSubViewSafeAreaInsetsDidChangeCalledCount)) + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 + expect(mainView.subView.safeAreaInsetsDidChangeCalledCount) > 0 - expect(mainView.pin.safeArea).to(equal(UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0))) - expect(mainView.subView.pin.safeArea).to(equal(UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0))) + expect(mainView.pin.safeArea).to(equal(expectedSafeAreaInsets)) + expect(mainView.subView.pin.safeArea).to(equal(expectedSubViewSafeAreaInsets)) expect(mainView.subView.subViewB!.pin.safeArea).to(equal(UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0))) - expect(mainView.subView.frame).to(equal(CGRect(x: -10, y: -20, width: 100, height: 100))) - expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: 44, width: 40, height: 40))) + expect(mainView.subView.frame).to(equal(CGRect(x: subViewLeft, y: subViewTop, width: 100, height: 100))) + expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: expectedSubViewSafeAreaInsets.top, width: 40, height: 40))) } } } @@ -436,11 +430,13 @@ class PinSafeAreaWithOptInModeSpec: QuickSpec { var window: UIWindow! beforeEach { + Pin.safeAreaInsetsDidChangeMode = .optIn viewController = TestViewController2() navigationController = UINavigationController(rootViewController: viewController) } afterEach { + Pin.safeAreaInsetsDidChangeMode = .disable viewController = nil navigationController = nil window = nil @@ -450,6 +446,7 @@ class PinSafeAreaWithOptInModeSpec: QuickSpec { window = UIWindow() window.rootViewController = viewController window.addSubview(viewController.view) + window.makeKeyAndVisible(); // Testing UIViewController's layout methods is kind of bad // but needed in our case so we need to wait some time @@ -459,30 +456,45 @@ class PinSafeAreaWithOptInModeSpec: QuickSpec { describe("using Pin.safeAreaInsetsDidChangeMode = .optIn") { it("should not call safeAreaInsetsDidChange()") { let mainView = viewController.mainView - var expectedSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 0 } - } - var expectedSubViewSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 0 } - } + let subViewTop = CGFloat(10) + let subViewLeft = CGFloat(10) mainView.layoutOffsetViewClosure = { (_ subView: UIView, _ parent: UIView) in - subView.pin.top(10).left(10).size(100) + subView.pin.top(subViewTop).left(subViewLeft).size(100) } - navigationController.navigationBar.barStyle = .blackTranslucent + navigationController.navigationBar.isTranslucent = true setupWindow(with: navigationController) - + + let expectedSafeAreaInsets = mainView.safeAreaInsets + let expectedSubViewSafeAreaInsets = UIEdgeInsets( + top: expectedSafeAreaInsets.top - subViewTop, + left: .zero, + bottom: .zero, + right: .zero + ) + // MATCH safeAreaInsets! - expect(mainView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSafeAreaInsetsDidChangeCalledCount)) - expect(mainView.subView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSubViewSafeAreaInsetsDidChangeCalledCount)) + if #available(iOS 11.0, tvOS 11.0, *) { + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 + expect(mainView.subView.safeAreaInsetsDidChangeCalledCount) > 0 + } else { + // Should equal 0, because in optIn mode 'safeAreaInsetsDidChange' is called + // only if the UIView implement the PinSafeAreaInsetsUpdate protocol. Which is + // not the case with TestView2. + expect(mainView.safeAreaInsetsDidChangeCalledCount) == 0 + expect(mainView.subView.safeAreaInsetsDidChangeCalledCount) == 0 + } - expect(mainView.pin.safeArea).to(equal(UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0))) - expect(mainView.subView.pin.safeArea).to(equal(UIEdgeInsets(top: 34.0, left: 0.0, bottom: 0.0, right: 0.0))) + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 + expect(mainView.subView.safeAreaInsetsDidChangeCalledCount) > 0 + + expect(mainView.pin.safeArea).to(equal(expectedSafeAreaInsets)) + expect(mainView.subView.pin.safeArea).to(equal(expectedSubViewSafeAreaInsets)) expect(mainView.subView.subViewB!.pin.safeArea).to(equal(UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0))) - expect(mainView.subView.frame).to(equal(CGRect(x: 10, y: 10, width: 100, height: 100))) - expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: 34, width: 40, height: 40))) + expect(mainView.subView.frame).to(equal(CGRect(x: subViewLeft, y: subViewTop, width: 100, height: 100))) + expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: expectedSubViewSafeAreaInsets.top, width: 40, height: 40))) } } } @@ -509,6 +521,7 @@ class PinSafeAreaWithOptInInsetsUpdateModeSpec: QuickSpec { window = UIWindow() window.rootViewController = viewController window.addSubview(viewController.view) + window.makeKeyAndVisible(); // Testing UIViewController's layout methods is kind of bad // but needed in our case so we need to wait some time @@ -520,30 +533,34 @@ class PinSafeAreaWithOptInInsetsUpdateModeSpec: QuickSpec { Pin.safeAreaInsetsDidChangeMode = .optIn let mainView = viewController.mainView - var expectedSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 3 } - } - var expectedSubViewSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 0 } - } - + let subViewTop = CGFloat(10) + let subViewLeft = CGFloat(10) + mainView.layoutOffsetViewClosure = { (_ subView: UIView, _ parent: UIView) in - subView.pin.top(10).left(10).size(100) + subView.pin.top(subViewTop).left(subViewLeft).size(100) } - navigationController.navigationBar.barStyle = .blackTranslucent + navigationController.navigationBar.isTranslucent = true setupWindow(with: navigationController) + + let expectedSafeAreaInsets = mainView.safeAreaInsets + let expectedSubViewSafeAreaInsets = UIEdgeInsets( + top: expectedSafeAreaInsets.top - subViewTop, + left: .zero, + bottom: .zero, + right: .zero + ) // MATCH safeAreaInsets! - expect(mainView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSafeAreaInsetsDidChangeCalledCount)) - expect(mainView.subView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSubViewSafeAreaInsetsDidChangeCalledCount)) + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 + expect(mainView.subView.safeAreaInsetsDidChangeCalledCount) > 0 - expect(mainView.pin.safeArea).to(equal(UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0))) - expect(mainView.subView.pin.safeArea).to(equal(UIEdgeInsets(top: 34.0, left: 0.0, bottom: 0.0, right: 0.0))) + expect(mainView.pin.safeArea).to(equal(expectedSafeAreaInsets)) + expect(mainView.subView.pin.safeArea).to(equal(expectedSubViewSafeAreaInsets)) expect(mainView.subView.subViewB!.pin.safeArea).to(equal(UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0))) expect(mainView.subView.frame).to(equal(CGRect(x: 10, y: 10, width: 100, height: 100))) - expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: 34, width: 40, height: 40))) + expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: expectedSubViewSafeAreaInsets.top, width: 40, height: 40))) } } } @@ -577,6 +594,7 @@ class PinSafeAreaTabBarControllerSpec: QuickSpec { window = UIWindow() window.rootViewController = viewController window.addSubview(viewController.view) + window.makeKeyAndVisible(); // Testing UIViewController's layout methods is kind of bad // but needed in our case so we need to wait some time @@ -586,63 +604,64 @@ class PinSafeAreaTabBarControllerSpec: QuickSpec { describe("navigationbar + tabbar + subview") { it("translucent navigation bar") { let mainView = viewController.mainView - var expectedSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 2 } else { return 4 } - } - var expectedSubViewSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 2 } - } + let subViewTop = CGFloat(10) + let subViewLeft = CGFloat(10) mainView.layoutOffsetViewClosure = { (_ subView: UIView, _ parent: UIView) in - subView.pin.top(10).left(10).size(100) + subView.pin.top(subViewTop).left(subViewLeft).size(100) } - navigationController.navigationBar.barStyle = .blackTranslucent + navigationController.navigationBar.isTranslucent = true setupWindow(with: navigationController) + + let expectedSafeAreaInsets = mainView.safeAreaInsets + let expectedSubViewSafeAreaInsets = UIEdgeInsets( + top: expectedSafeAreaInsets.top - subViewTop, + left: .zero, + bottom: .zero, + right: .zero + ) // MATCH safeAreaInsets! - expect(mainView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSafeAreaInsetsDidChangeCalledCount)) - expect(mainView.subView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSubViewSafeAreaInsetsDidChangeCalledCount)) + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 + expect(mainView.subView.safeAreaInsetsDidChangeCalledCount) > 0 - expect(mainView.pin.safeArea).to(equal(UIEdgeInsets(top: 44.0, left: 0.0, bottom: 49.0, right: 0.0))) - expect(mainView.subView.pin.safeArea).to(equal(UIEdgeInsets(top: 34.0, left: 0.0, bottom: 0.0, right: 0.0))) + expect(mainView.pin.safeArea).to(equal(expectedSafeAreaInsets)) + expect(mainView.subView.pin.safeArea).to(equal(expectedSubViewSafeAreaInsets)) expect(mainView.subView.subViewB!.pin.safeArea).to(equal(UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0))) let screenSize = mainView.frame.size expect(mainView.frame).to(equal(CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height))) - expect(mainView.subView.frame).to(equal(CGRect(x: 10, y: 10, width: 100, height: 100))) - expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: 34, width: 40, height: 40))) + expect(mainView.subView.frame).to(equal(CGRect(x: subViewLeft, y: subViewTop, width: 100, height: 100))) + expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: expectedSubViewSafeAreaInsets.top, width: 40, height: 40))) } } describe("navigationbar + tabbar + subview") { it("transluscent navigationbar 2") { let mainView = viewController.mainView - var expectedSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 2 } else { return 4 } - } - var expectedSubViewSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 2 } - } mainView.layoutOffsetViewClosure = { (_ subView: UIView, _ parent: UIView) in subView.pin.all() } - navigationController.navigationBar.barStyle = .blackTranslucent + navigationController.navigationBar.isTranslucent = true setupWindow(with: navigationController) + + let expectedSafeAreaInsets = mainView.safeAreaInsets + let expectedSubViewSafeAreaInsets = expectedSafeAreaInsets // MATCH safeAreaInsets! - expect(mainView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSafeAreaInsetsDidChangeCalledCount)) - expect(mainView.subView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSubViewSafeAreaInsetsDidChangeCalledCount)) + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 + expect(mainView.subView.safeAreaInsetsDidChangeCalledCount) > 0 - expect(mainView.pin.safeArea).to(equal(UIEdgeInsets(top: 44.0, left: 0.0, bottom: 49.0, right: 0.0))) - expect(mainView.subView.pin.safeArea).to(equal(UIEdgeInsets(top: 44.0, left: 0.0, bottom: 49.0, right: 0.0))) + expect(mainView.pin.safeArea).to(equal(expectedSafeAreaInsets)) + expect(mainView.subView.pin.safeArea).to(equal(expectedSubViewSafeAreaInsets)) expect(mainView.subView.subViewB!.pin.safeArea).to(equal(UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0))) let screenSize = mainView.frame.size expect(mainView.subView.frame).to(equal(CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height))) - expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: 44, width: 40, height: 40))) + expect(mainView.subView.subViewB!.frame).to(equal(CGRect(x: 0, y: expectedSubViewSafeAreaInsets.top, width: 40, height: 40))) } } } @@ -671,6 +690,7 @@ class PinSafeAreaScrollViewControllerSpec: QuickSpec { window = UIWindow() window.rootViewController = viewController window.addSubview(viewController.view) + window.makeKeyAndVisible(); // Testing UIViewController's layout methods is kind of bad // but needed in our case so we need to wait some time @@ -680,18 +700,17 @@ class PinSafeAreaScrollViewControllerSpec: QuickSpec { describe("navigationbar + scrollview") { it("translucent navigation bar") { let mainView = viewController.mainView - var expectedSafeAreaInsetsDidChangeCalledCount: Int { - if #available(iOS 11.0, tvOS 11.0, *) { return 1 } else { return 3 } - } - navigationController.navigationBar.barStyle = .blackTranslucent + navigationController.navigationBar.isTranslucent = true setupWindow(with: navigationController) + + let expectedSafeAreaInsets = mainView.safeAreaInsets // MATCH safeAreaInsets! - expect(mainView.safeAreaInsetsDidChangeCalledCount).to(equal(expectedSafeAreaInsetsDidChangeCalledCount)) + expect(mainView.safeAreaInsetsDidChangeCalledCount) > 0 expect(mainView.subView.safeAreaInsetsDidChangeCalledCount).to(equal(0)) - expect(mainView.pin.safeArea).to(equal(UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0))) + expect(mainView.pin.safeArea).to(equal(expectedSafeAreaInsets)) // subView is inside a UIScrollView, its safeArea should be .zero expect(mainView.subView.pin.safeArea).to(equal(UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0))) @@ -705,7 +724,6 @@ class PinSafeAreaScrollViewControllerSpec: QuickSpec { } } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// fileprivate class TestViewController2: UIViewController { var mainView: TestView2 { return self.view as! TestView2 } @@ -739,7 +757,7 @@ class TestView2: UIView { layoutSubView() } - fileprivate func layoutSubView() { + private func layoutSubView() { assert(layoutOffsetViewClosure != nil) layoutOffsetViewClosure?(subView, self) @@ -787,7 +805,6 @@ fileprivate class TestScrollViewController: UIViewController { } } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// fileprivate class TestInsetsUpdateViewController: UIViewController { var mainView: TestInsetsUpdateView { return self.view as! TestInsetsUpdateView } @@ -821,7 +838,7 @@ class TestInsetsUpdateView: UIView, PinSafeAreaInsetsUpdate { layoutSubView() } - fileprivate func layoutSubView() { + private func layoutSubView() { assert(layoutOffsetViewClosure != nil) layoutOffsetViewClosure?(subView, self) @@ -830,7 +847,6 @@ class TestInsetsUpdateView: UIView, PinSafeAreaInsetsUpdate { } } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// class TestScrollView: UIView { let scrollView = UIScrollView() @@ -858,7 +874,7 @@ class TestScrollView: UIView { layoutSubView() } - fileprivate func layoutSubView() { + private func layoutSubView() { super.layoutSubviews() scrollView.pin.all() diff --git a/Tests/iOS/ReadableLayoutMarginsSpec.swift b/Tests/iOS/ReadableLayoutMarginsSpec.swift new file mode 100644 index 00000000..deaaac3e --- /dev/null +++ b/Tests/iOS/ReadableLayoutMarginsSpec.swift @@ -0,0 +1,137 @@ +// Copyright (c) 2017 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Quick +import Nimble +import PinLayout + +class ReadableLayoutMargins: QuickSpec { + override func spec() { + var viewController: PViewController! + var window: UIWindow! + + var rootView: BasicView! + var aView: BasicView! + + var bView: BasicView! + var bViewChild: BasicView! + + /* + root + | + |- aView + | | + | - aViewChild + | + - bView + | + - bViewChild + */ + + beforeEach { + viewController = PViewController() + viewController.view = BasicView() + + rootView = BasicView() + viewController.view.addSubview(rootView) + + aView = BasicView() + rootView.addSubview(aView) + + bView = BasicView() + rootView.addSubview(bView) + + bViewChild = BasicView() + bView.addSubview(bViewChild) + + rootView.frame = CGRect(x: 0, y: 0, width: 400, height: 400) + aView.frame = CGRect(x: 140, y: 100, width: 200, height: 120) + bView.frame = CGRect(x: 0, y: 0, width: 100, height: 50) + bViewChild.frame = CGRect(x: 40, y: 10, width: 60, height: 20) + } + + func setupWindow(with viewController: UIViewController) { + window = UIWindow() + window.rootViewController = viewController + window.addSubview(viewController.view) + window.makeKeyAndVisible(); + + // Testing UIViewController's layout methods is kind of bad + // but needed in our case so we need to wait some time + RunLoop.current.run(until: Date().addingTimeInterval(0.2)) + } + + describe("Using pin.readableMargins") { + it("test") { + setupWindow(with: viewController) + + aView.pin.all(rootView.pin.readableMargins) + + expect(aView.frame).to(equal(rootView.readableContentGuide.layoutFrame)) + } + } + + describe("Using pin.layoutMargins") { + it("test") { + setupWindow(with: viewController) + + aView.pin.all(rootView.pin.layoutMargins) + + let layoutMargins = rootView.layoutMargins + let aViewFrame = CGRect( + x: layoutMargins.left, + y: layoutMargins.top, + width: rootView.frame.width - (layoutMargins.left + layoutMargins.right), + height: rootView.frame.height - (layoutMargins.top + layoutMargins.bottom) + ) + expect(aView.frame).to(equal(aViewFrame)) + } + } + + #if os(iOS) + describe("Using pin.keyboardArea") { + it("test") { + setupWindow(with: viewController) + + rootView.pin.top(0).horizontally() + rootView.pin.bottom(rootView.pin.keyboardArea.height) + + let keyboardLayoutFrame: CGRect + + if #available(iOS 15, *) { + keyboardLayoutFrame = rootView.keyboardLayoutGuide.layoutFrame + } else { + keyboardLayoutFrame = .zero + } + + let screenSize = viewController.view.frame + let expectedRootViewFrame = CGRect( + x: .zero, + y: .zero, + width: screenSize.width, + height: screenSize.height - keyboardLayoutFrame.height + ) + + expect(rootView.frame).to(equal(expectedRootViewFrame)) + expect(rootView.pin.keyboardArea).to(equal(keyboardLayoutFrame)) + } + } + #endif + } +} diff --git a/Tests/iOS/Types+UIKit.swift b/Tests/iOS/Types+UIKit.swift index 534a2e89..c8e42d5c 100644 --- a/Tests/iOS/Types+UIKit.swift +++ b/Tests/iOS/Types+UIKit.swift @@ -1,10 +1,21 @@ +// Copyright (c) 2018 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: // -// Types+UIKit.swift -// PinLayoutTests-macOS -// -// Created by Luc Dion on 2018-04-18. -// Copyright © 2018 mcswiftlayyout.mirego.com. All rights reserved. +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. import UIKit @@ -13,3 +24,4 @@ typealias PScrollView = UIScrollView typealias PEdgeInsets = UIEdgeInsets typealias PViewController = UIViewController typealias PColor = UIColor +typealias PLabel = UILabel diff --git a/Tests/macOS/PinLayoutTestMacOS.swift b/Tests/macOS/PinLayoutTestMacOS.swift index 8fd080be..b011e88d 100644 --- a/Tests/macOS/PinLayoutTestMacOS.swift +++ b/Tests/macOS/PinLayoutTestMacOS.swift @@ -1,10 +1,21 @@ +// Copyright (c) 2018 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: // -// PinLayoutTestMacOS.swift -// PinLayoutTestMacOS -// -// Created by Luc Dion on 2018-04-16. -// Copyright © 2018 mcswiftlayyout.mirego.com. All rights reserved. +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. import XCTest diff --git a/Tests/macOS/Types+Appkit.swift b/Tests/macOS/Types+Appkit.swift index f30f15c9..22be7050 100644 --- a/Tests/macOS/Types+Appkit.swift +++ b/Tests/macOS/Types+Appkit.swift @@ -1,10 +1,21 @@ +// Copyright (c) 2018 Luc Dion +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: // -// Types+Appkit.swift -// PinLayoutTests-macOS -// -// Created by Luc Dion on 2018-04-18. -// Copyright © 2018 mcswiftlayyout.mirego.com. All rights reserved. +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. import AppKit diff --git a/build-ci.sh b/build-ci.sh deleted file mode 100755 index 429830f0..00000000 --- a/build-ci.sh +++ /dev/null @@ -1,100 +0,0 @@ - -DERIVED_DATA=${1:-/tmp/PinLayout} - -set -o pipefail && - -rm -rf $DERIVED_DATA && - -echo "===============================" && -echo "fastlane iOS travis" && -echo "===============================" && -time bundle exec fastlane ios travis && - - -# echo "===============================" && -# echo "fastlane macOS travis" && -# echo "===============================" && -# #time bundle exec fastlane mac travis && - - -# echo "===============================" && -# echo "iOS unit test" && -# echo "===============================" && -# time xcodebuild build test -workspace PinLayout.xcworkspace -scheme PinLayout-iOS -derivedDataPath $DERIVED_DATA -sdk iphonesimulator11.3 \ -# -destination 'platform=iOS Simulator,name=iPhone 7 Plus,OS=11.3' \ -# -destination 'platform=iOS Simulator,name=iPhone 7,OS=11.3' \ -# -destination 'platform=iOS Simulator,name=iPhone 6,OS=10.2'\ -# | xcpretty && - - -# echo "===============================" && -# echo "macOS unit test" && -# echo "===============================" && -# time xcodebuild clean test -workspace PinLayout.xcworkspace -scheme PinLayout-macOS -derivedDataPath $DERIVED_DATA -sdk macosx10.13 \ -# | xcpretty - - -# echo "===============================" && -# echo " Cocoapods: iOS Empty project" && -# echo "===============================" && -# cd TestProjects/cocoapods/ios && -# rm -rf $DERIVED_DATA && -# pod install && -# time xcodebuild clean build -workspace PinLayout-iOS.xcworkspace -scheme PinLayout-iOS -sdk iphonesimulator11.3 -derivedDataPath $DERIVED_DATA \ -# -destination 'platform=iOS Simulator,name=iPhone 7,OS=11.3' \ -# | xcpretty && -# cd ../../.. - - -# echo "===============================" && -# echo " Cocoapods: macOS Empty project" && -# echo "===============================" && -# cd TestProjects/cocoapods/macos && -# rm -rf $DERIVED_DATA && -# pod install && -# time xcodebuild clean build -workspace PinLayout-macOS.xcworkspace -scheme PinLayout-macOS -sdk macosx10.13 -derivedDataPath $DERIVED_DATA \ -# | xcpretty && -# cd ../../.. - - -# echo "===============================" && -# echo " Cocoapods: tvOS Empty project" && -# echo "===============================" && -# cd TestProjects/cocoapods/tvos && -# rm -rf $DERIVED_DATA && -# pod install && -# time xcodebuild clean build -workspace PinLayout-tvOS.xcworkspace -scheme PinLayout-tvOS -sdk appletvsimulator11.3 -derivedDataPath $DERIVED_DATA \ -# -destination 'platform=tvOS Simulator,name=Apple TV,OS=11.3' \ -# | xcpretty && -# cd ../../.. - - -echo "===============================" && -echo " Carthage: iOS Empty project" && -echo "===============================" && -cd TestProjects/carthage/ios && -rm -rf $DERIVED_DATA && -rm Cartfile && -echo "git \"$TRAVIS_BUILD_DIR\" \"$TRAVIS_BRANCH\"" > Cartfile && -carthage update --use-ssh --platform iOS && -time xcodebuild clean build -project PinLayout-Carthage-iOS.xcodeproj -scheme PinLayout-Carthage-iOS -sdk iphonesimulator11.3 -derivedDataPath $DERIVED_DATA \ - -destination 'platform=iOS Simulator,name=iPhone 7,OS=11.3' \ - | xcpretty && -cd ../../.. - - -# echo "==========================================" && -# echo " Swift Package Manager: iOS Empty project " && -# echo "==========================================" && -# cd TestProjects/swift-package-manager/ios && -# rm -rf $DERIVED_DATA && -# rm -rf .build && -# rm Package.pins -# swift package show-dependencies --format json && -# time xcodebuild clean build -project PinLayout-Carthage-iOS.xcodeproj -scheme PinLayout-Carthage-iOS -sdk iphonesimulator11.3 -derivedDataPath $DERIVED_DATA \ -# -destination 'platform=iOS Simulator,name=iPhone 7,OS=11.3' \ -# | xcpretty && -# cd ../../.. -# -# #OTHER_SWIFT_FLAGS='-Xfrontend -debug-time-function-bodies' -# xcodebuild clean test -workspace PinLayout.xcworkspace -scheme PinLayout-macOS -derivedDataPath $DERIVED_DATA -sdk macosx10.13 && diff --git a/docs/Benchmark.md b/docs/Benchmark.md index 9cef165e..c6b6e7e7 100644 --- a/docs/Benchmark.md +++ b/docs/Benchmark.md @@ -15,14 +15,15 @@ ##### LayoutKit Benchmark PinLayout and [FlexLayout](https://github.com/layoutBox/FlexLayout) performance has been benchmarked using [Layout Framework Benchmark](https://github.com/layoutBox/LayoutFrameworkBenchmark). -The benchmark includes the following layout frameworks: +The benchmark includes also many other layout frameworks, including: * Auto layout * [FlexLayout](https://github.com/layoutBox/FlexLayout) * [LayoutKit](https://github.com/linkedin/LayoutKit) * Manual layout (i.e. set UIView's frame directly) -* [PinLayout](https://github.com/mirego/PinLayout) +* [PinLayout](https://github.com/layoutBox/PinLayout) * UIStackViews +* ... ### Benchmark @@ -32,7 +33,7 @@ As you can see in the following chart, PinLayout are faster or equal to manual l The benchmark layout UICollectionView and UITableView cells in multiple pass, each pass contains more cells than the previous one.

- PinLayout Performance + PinLayout Performance See [Layout Framework Benchmark](https://github.com/layoutBox/LayoutFrameworkBenchmark) for complete details and benchmarks charts for iPhone X/8/7/6S/... @@ -55,30 +56,29 @@ override func layoutSubviews() { super.layoutSubviews() let hMargin: CGFloat = 8 - let vMargin: CGFloat = 2 + let vMargin: CGFloat = 4 optionsLabel.pin.topRight().margin(hMargin) actionLabel.pin.topLeft().margin(hMargin) posterImageView.pin.below(of: actionLabel, aligned: .left).marginTop(10) - posterNameLabel.pin.right(of: posterImageView, aligned: .top).margin(-6, 6).right(hMargin).sizeToFit() - posterHeadlineLabel.pin.below(of: posterNameLabel, aligned: .left).right(hMargin).marginTop(1).sizeToFit() - posterTimeLabel.pin.below(of: posterHeadlineLabel, aligned: .left).right(hMargin).marginTop(1).sizeToFit() - posterCommentLabel.pin.below(of: posterTimeLabel).left(hMargin).right().right(hMargin) - .marginTop(vMargin).sizeToFit() + posterHeadlineLabel.pin.after(of: posterImageView, aligned: .center).marginLeft(4) + posterNameLabel.pin.above(of: posterHeadlineLabel, aligned: .left).marginBottom(vMargin) + posterTimeLabel.pin.below(of: posterHeadlineLabel, aligned: .left).marginTop(vMargin) - contentImageView.pin.below(of: posterCommentLabel).hCenter().width(100%).sizeToFit() - contentTitleLabel.pin.below(of: contentImageView).left().right().marginHorizontal(hMargin).sizeToFit() - contentDomainLabel.pin.below(of: contentTitleLabel, aligned: .left).right().marginRight(hMargin) - .sizeToFit() + posterCommentLabel.pin.below(of: posterTimeLabel).left(hMargin).marginTop(vMargin) + + contentImageView.pin.below(of: posterCommentLabel, aligned: .left).right().marginTop(vMargin).marginRight(hMargin) + contentTitleLabel.pin.below(of: contentImageView).left().marginHorizontal(hMargin) + contentDomainLabel.pin.below(of: contentTitleLabel, aligned: .left) likeLabel.pin.below(of: contentDomainLabel, aligned: .left).marginTop(vMargin) - commentLabel.pin.top(to: likeLabel.edge.top).hCenter(50%) + commentLabel.pin.top(to: likeLabel.edge.top).hCenter() shareLabel.pin.top(to: likeLabel.edge.top).right().marginRight(hMargin) actorImageView.pin.below(of: likeLabel, aligned: .left).marginTop(vMargin) - actorCommentLabel.pin.right(of: actorImageView, aligned: .center).marginLeft(4) + actorCommentLabel.pin.after(of: actorImageView, aligned: .center).marginLeft(4) } ``` diff --git a/docs/Benchmark/benchmark_comparison_all.png b/docs/Benchmark/benchmark_comparison_all.png deleted file mode 100644 index 7b6068e1..00000000 Binary files a/docs/Benchmark/benchmark_comparison_all.png and /dev/null differ diff --git a/docs/Benchmark/benchmark_comparison_all_small.png b/docs/Benchmark/benchmark_comparison_all_small.png index fbd2baf4..0e93d47e 100644 Binary files a/docs/Benchmark/benchmark_comparison_all_small.png and b/docs/Benchmark/benchmark_comparison_all_small.png differ diff --git a/docs/Benchmark/benchmark_result_Autolayout.png b/docs/Benchmark/benchmark_result_Autolayout.png deleted file mode 100644 index 016b9ce1..00000000 Binary files a/docs/Benchmark/benchmark_result_Autolayout.png and /dev/null differ diff --git a/docs/Benchmark/benchmark_result_FlexLayout.png b/docs/Benchmark/benchmark_result_FlexLayout.png deleted file mode 100644 index b22f4233..00000000 Binary files a/docs/Benchmark/benchmark_result_FlexLayout.png and /dev/null differ diff --git a/docs/Benchmark/benchmark_result_LayoutKit.png b/docs/Benchmark/benchmark_result_LayoutKit.png deleted file mode 100644 index 1e0e3ca1..00000000 Binary files a/docs/Benchmark/benchmark_result_LayoutKit.png and /dev/null differ diff --git a/docs/Benchmark/benchmark_result_PinLayout.png b/docs/Benchmark/benchmark_result_PinLayout.png deleted file mode 100644 index 6fe1fd09..00000000 Binary files a/docs/Benchmark/benchmark_result_PinLayout.png and /dev/null differ diff --git a/docs/PinLayout_principles.md b/docs/PinLayout_principles.md index 34664e37..3ee6b36b 100644 --- a/docs/PinLayout_principles.md +++ b/docs/PinLayout_principles.md @@ -6,7 +6,7 @@ * Manual layouting (doesn't rely on auto layout). -* PinLayout exist to be simple and fast as possible! In fact, it is fast as manual layouting. See [performance results below.](#performance) +* PinLayout exist to be simple and fast as possible! In fact, it is fast as manual layouting. See [performance results here.](https://github.com/layoutBox/PinLayout#pinlayouts-performance) * Full control: You're in the middle of the layout process, no magic black box. * You can add conditions (if/switch/guard/...) related to the device orientation, device type, traitCollection, animations, ... @@ -31,4 +31,4 @@ Each view can use the layout system that better suit it (PinLayout, autolayout, * Not too intrusive. PinLayout only adds three properties to existing iOS classes: `UIView.pin`, `UIView.anchor` and `UIView.edge` * Minimize as much as possible calculations and constants when layouting views. But it is always possible to add advanced computation if required. -* Method's name match as much as possible other layout frameworks, including [FlexLayout](https://github.com/layoutBox/FlexLayout)/flexbox, CSS, React Native, … \ No newline at end of file +* Method's name match as much as possible other layout frameworks, including [FlexLayout](https://github.com/layoutBox/FlexLayout)/flexbox, CSS, React Native, … diff --git a/docs/animations.md b/docs/animations.md new file mode 100644 index 00000000..dbb738b2 --- /dev/null +++ b/docs/animations.md @@ -0,0 +1,190 @@ +

+ +

+ +

Animations using PinLayout

+ +## Basic Swift animations +Before explaining how to use PinLayout to animate views, you can check these two nice tutorials to learn the basic of animations: + +* [iOS Animation Tutorial: Getting Started](https://www.raywenderlich.com/363-ios-animation-tutorial-getting-started) +* [Basic UIView Animation Tutorial: Getting Started](https://www.raywenderlich.com/5255-basic-uiview-animation-tutorial-getting-started) + + +## PinLayout is stateless +It is important to remember that PinLayout is stateless, i.e. PinLayout always start from the view's current position and size (frame) when layouting a view. You don't need to reset anything to animate a view using PinLayout. + +This also means that you can modify only the property you want to animate, for example the following code will only animate the view's width: + +``` + UIView.animate(withDuration: 0.3) { + view.pin.width(30) + } +``` + +
+ +## Layout strategies +Multiple strategies can be used to animate layout using PinLayout. The choice is a question of preferences and the kind of animations you want to achieve. + +Some possible strategies will be shown below using a simple example. The example animates a view position from left to right when the user tap a button. + + + +Note that in the following source code the view's size was set to 150 px (`view.pin.size(150)`) in the initialization. + + +### Basic strategy: Using `UIView.setNeedsLayout` and `UIView.layoutIfNeeded` +In this strategy, to force a call to layoutSubviews(), we call `UIView.setNeedsLayout` and `UIView.layoutIfNeeded` from the animation block. + +```swift +var isViewLeftDocked = true + +override func layoutSubviews() { + super.layoutSubviews() + + if isViewLeftDocked { + view.pin.top().left() + } else { + view.pin.top().right() + } +} + +func didTapTogglePosition() { + isViewLeftDocked = !isViewLeftDocked + + UIView.animate(withDuration: 0.3) { + self.setNeedsLayout() + self.layoutIfNeeded() + } +} +``` + +### Using a layout method +This strategy use a private method to layout the animated view (`layoutAnimatedView()`). The advantage of this solution is that it is not required to call `UIView.setNeedsLayout` and `UIView.layoutIfNeeded` to relayout the view. + +```swift +var isViewLeftDocked = true + +override func layoutSubviews() { + super.layoutSubviews() + + layoutAnimatedView() +} + +private func layoutAnimatedView() { + if isViewLeftDocked { + view.pin.top().left() + } else { + view.pin.top().right() + } +} + +func didTapTogglePosition() { + isViewLeftDocked = !isViewLeftDocked + + UIView.animate(withDuration: 0.3) { + self.layoutAnimatedView() + } +} +``` + +### Using an animation state +This strategy is similar to the previous one, but use an enumeration to keep the animation state. + +```swift +enum AnimationState { + case leftDocked + case rightDocked +} + +var animationState = AnimationState.leftDocked + +override func layoutSubviews() { + super.layoutSubviews() + + layoutAnimatedView() +} + +private func layoutAnimatedView() { + switch animationState { + case .leftDocked: + view.pin.top().left() + case .rightDocked: + view.pin.top().right() + } +} + +func didTapTogglePosition() { + switch animationState { + case .leftDocked: animationState = .rightDocked + case .rightDocked: animationState = .leftDocked + } + + UIView.animate(withDuration: 0.3) { + self.layoutAnimatedView() + } +} +``` + +### Other strategies +It's really up to you to think of animation's strategies that match your situation. With PinLayout you are always in control of everything, including animations. + +
+ +## Collision between animations and `layoutSubViews()` +In some particular situation it is possible that `layoutSubViews()` may be called during the animation is in progress, this can occur particularly on long animation. To handle this kind of situation it is possible to use a boolean indicating if an animation is in progress, and to block temporarely the layout of animated views in `layoutSubViews()`. + +Here is an example: + +```swift +enum AnimationState { + case leftDocked + case rightDocked +} + +var animationState = AnimationState.leftDocked +var isAnimating = false + +override func layoutSubviews() { + super.layoutSubviews() + + // If an animation of the view is in progress, we don't update animated views position. + guard !isAnimating else { return } + layoutAnimatedView() +} + +private func layoutAnimatedView() { + switch animationState { + case .leftDocked: + view.pin.top().left() + case .rightDocked: + view.pin.top().right() + } +} + +func didTapTogglePosition() { + switch animationState { + case .leftDocked: animationState = .rightDocked + case .rightDocked: animationState = .leftDocked + } + + UIView.animate(withDuration: 0.3, animations: { + self.isAnimating = true + self.layoutAnimatedView() + }, completion: { (_) in + self.isAnimating = false + }) +} +``` + +
+ +## Animation Example +You can check the animation example available in the [PinLayout's Example App](https://github.com/layoutBox/PinLayout/blob/master/docs/examples.md): + +[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/Animations/AnimationsView.swift) + + + + diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 00000000..894bff63 --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,127 @@ +

+ +

+ +

Examples

+ +The PinLayout's Example exposes some usage example of PinLayout. + +The Example App is available in the [`Example`](https://github.com/layoutBox/PinLayout/tree/master/Example) folder. + +### Running the Example app +1. Do a `pod install` from the PinLayout root directory. +2. Open the newly generated `PinLayout.xcworkspace` Xcode workspace. +3. Select the `PinLayoutSample` target. +4. Run the app on your device or simulator. + +
+ +## Intro Example +PinLayout introduction example presented in the README. + +[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/Intro/IntroView.swift) + + + +## Relative Edges Layout Example +Example showing how to layout views relative to other views. + +[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/RelativeView/RelativeView.swift) + + + +## Between Example +Example showing how to use [`horizontallyBetween()`](https://github.com/layoutBox/PinLayout#layout-between-other-views) to position a view between two other views. +[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/BetweenView/BetweenView.swift) + + + +## UITableView Example +Example using a UITableView with variable height cells. + +[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/TableViewExample/TableViewExampleView.swift) + + + + +## UITableView Example with cells using `pin.readableMargins` +Similar to the UITableView Example, but in this one cells use `pin.readableMargins` to layout their content inside the zone defined by `UIView.readableContentGuide`. + +[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/TableViewExample/TableViewExampleView.swift) + + + + +## UICollectionView Example +Example using a UICollectionView with variable height cells. + +[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/CollectionViewExample/HouseCell.swift) + + + +## Animations Example +Example showing how to animate views with PinLayout. + +[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/Animations/AnimationsView.swift) + + + +## Right to left language support Example +This example show how PinLayout can support simultaneously Left to right and right to left languages. + +[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/IntroRTL/IntroRTLView.swift) + + + + +## pin.safeArea example +Example showing the usage of `UIView.pin.safeArea`] with UINavigationController and UITabViewController: + +[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaView.swift) + + + +Also display the usage of `pin.readableMargins` and `pin.layoutMargins`: + +[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/SafeArea/SafeAreaAndMarginsView.swift) + + + + +## Adjust To Container Example +Example showing how PinLayout can be used to adjust the layout depending of the space available. + +In this example the UISegmentedControl is shown below its label if the available width is smaller than 500 pixels, or on the same line as the label if the width is wider. + +[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/AdjustToContainer/Subviews/ChoiceSelectorView.swift) + + + + +## wrapContent Example +This example show how to use the `wrapContent()` method. This method is particularly useful to wrap a group of views and center them. + +[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/WrapContent/WrapContentView.swift) + + + +## Form Example +This example is a basic form containing 4 fields. + +[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/Form/FormView.swift) + + + +## Auto Adjusting Size Example +This example show how fixed size views and expandable views can be layouted using PinLayout to fill the available space. + +[Source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/AutoAdjustingSize/AutoAdjustingSizeView.swift) + + + + +## Automatic Sizing Example +This example show how to use Automatic Sizing (`autoSizeThatFits()`) to compute views size. +[Source code](https://github.com/layoutBox/PinLayout/tree/master/Example/PinLayoutSample/UI/Examples/AutoSizing) + + diff --git a/docs/images/example-animations-large.gif b/docs/images/example-animations-large.gif new file mode 100644 index 00000000..3451d365 Binary files /dev/null and b/docs/images/example-animations-large.gif differ diff --git a/docs/images/example-animations.gif b/docs/images/example-animations.gif new file mode 100644 index 00000000..beb2dbfb Binary files /dev/null and b/docs/images/example-animations.gif differ diff --git a/docs/images/pinlayout_animation_example1.gif b/docs/images/pinlayout_animation_example1.gif new file mode 100644 index 00000000..2e920652 Binary files /dev/null and b/docs/images/pinlayout_animation_example1.gif differ diff --git a/docs/images/pinlayout_example_anchor_center.png b/docs/images/pinlayout_example_anchor_center.png new file mode 100644 index 00000000..73d24fbc Binary files /dev/null and b/docs/images/pinlayout_example_anchor_center.png differ diff --git a/docs/images/pinlayout_example_layout_margins_all.png b/docs/images/pinlayout_example_layout_margins_all.png new file mode 100644 index 00000000..703eb4eb Binary files /dev/null and b/docs/images/pinlayout_example_layout_margins_all.png differ diff --git a/docs/images/pinlayout_example_layout_margins_landscape.png b/docs/images/pinlayout_example_layout_margins_landscape.png new file mode 100644 index 00000000..4d29810c Binary files /dev/null and b/docs/images/pinlayout_example_layout_margins_landscape.png differ diff --git a/docs/images/pinlayout_example_layout_margins_portrain.png b/docs/images/pinlayout_example_layout_margins_portrain.png new file mode 100644 index 00000000..11c533d3 Binary files /dev/null and b/docs/images/pinlayout_example_layout_margins_portrain.png differ diff --git a/docs/images/pinlayout_example_tableview_readable_content_all.png b/docs/images/pinlayout_example_tableview_readable_content_all.png new file mode 100644 index 00000000..805c74c2 Binary files /dev/null and b/docs/images/pinlayout_example_tableview_readable_content_all.png differ diff --git a/docs/images/pinlayout_example_tableview_readable_content_landscape.png b/docs/images/pinlayout_example_tableview_readable_content_landscape.png new file mode 100644 index 00000000..e4cc7be9 Binary files /dev/null and b/docs/images/pinlayout_example_tableview_readable_content_landscape.png differ diff --git a/docs/images/pinlayout_example_tableview_readable_content_portrait.png b/docs/images/pinlayout_example_tableview_readable_content_portrait.png new file mode 100644 index 00000000..71ff37aa Binary files /dev/null and b/docs/images/pinlayout_example_tableview_readable_content_portrait.png differ diff --git a/docs/images/pinlayout_example_topRight.png b/docs/images/pinlayout_example_topRight.png new file mode 100644 index 00000000..e635927d Binary files /dev/null and b/docs/images/pinlayout_example_topRight.png differ diff --git a/docs/images/pinlayout_horizontallyBetween.png b/docs/images/pinlayout_horizontallyBetween.png new file mode 100644 index 00000000..80eea2cd Binary files /dev/null and b/docs/images/pinlayout_horizontallyBetween.png differ diff --git a/docs/images/pinlayout_plus_layoutBox.png b/docs/images/pinlayout_plus_layoutBox.png new file mode 100644 index 00000000..02684ef2 Binary files /dev/null and b/docs/images/pinlayout_plus_layoutBox.png differ diff --git a/docs/images/pinlayout_right_to_left_example.png b/docs/images/pinlayout_right_to_left_example.png new file mode 100644 index 00000000..d398a916 Binary files /dev/null and b/docs/images/pinlayout_right_to_left_example.png differ diff --git a/docs/images/pinlayout_verticallyBetween.png b/docs/images/pinlayout_verticallyBetween.png new file mode 100644 index 00000000..871d46fb Binary files /dev/null and b/docs/images/pinlayout_verticallyBetween.png differ diff --git a/docs/images/wrapContent_all.png b/docs/images/wrapContent_all.png new file mode 100644 index 00000000..0f00ca0f Binary files /dev/null and b/docs/images/wrapContent_all.png differ diff --git a/docs/images/wrapContent_before.png b/docs/images/wrapContent_before.png new file mode 100644 index 00000000..ad081829 Binary files /dev/null and b/docs/images/wrapContent_before.png differ diff --git a/docs/images/wrapContent_example.png b/docs/images/wrapContent_example.png new file mode 100644 index 00000000..c55fde13 Binary files /dev/null and b/docs/images/wrapContent_example.png differ diff --git a/docs/images/wrapContent_horizontally.png b/docs/images/wrapContent_horizontally.png new file mode 100644 index 00000000..d8079987 Binary files /dev/null and b/docs/images/wrapContent_horizontally.png differ diff --git a/docs/images/wrapContent_padding.png b/docs/images/wrapContent_padding.png new file mode 100644 index 00000000..b71b2303 Binary files /dev/null and b/docs/images/wrapContent_padding.png differ diff --git a/docs/images/wrapContent_vertically.png b/docs/images/wrapContent_vertically.png new file mode 100644 index 00000000..32b5af9b Binary files /dev/null and b/docs/images/wrapContent_vertically.png differ diff --git a/docs/objective_c.md b/docs/objective_c.md index f1132fd0..6c17b258 100644 --- a/docs/objective_c.md +++ b/docs/objective_c.md @@ -9,21 +9,24 @@ PinLayout can also be used from Objective-C. The PinLayout interface is slightly ###### Example 1: This example implement the PinLayout's Intro example using objective-c - + ``` - (void) layoutSubviews { [super layoutSubviews]; - - [[[[[[logo.pinObjc top] left] width:100] aspectRatio] marginWithTop:topLayoutGuide + 10 horizontal:10 bottom:10] layout]; - [[[[segmented.pinObjc rightOf:logo aligned:VerticalAlignTop] right] marginHorizontal:10] layout]; - [[[[[[textLabel.pinObjc belowOf:segmented aligned:HorizontalAlignLeft] widthOf:segmented] pinEdges] marginTop:10] fitSize] layout]; - [[[[[separatorView.pinObjc belowOfViews:@[logo, textLabel] aligned:HorizontalAlignLeft] rightTo:segmented.edge.right] height:1] marginTop:10] layout]; + + CGFloat margin = 10; + UIEdgeInsets safeArea = self.pinObjc.safeArea; + + logo.pinObjc.topInsets(safeArea).leftInsets(safeArea).width(100).aspectRatio().margin(margin).layout(); + segmented.pinObjc.rightOfAligned(logo, VerticalAlignTop).rightInsets(safeArea).marginHorizontal(margin).layout(); + textLabel.pinObjc.belowOfAligned(segmented, HorizontalAlignLeft).widthOf(segmented).pinEdges().marginTop(margin).sizeToFitType(FitWidth).layout(); + separatorView.pinObjc.belowOfViewsAligned(@[logo, textLabel], HorizontalAlignLeft).rightToEdge(segmented.edge.right).height(1).marginTop(margin).layout(); } ``` -:pushpin: This example is available in the Examples App. See example complete [source code](https://github.com/mirego/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/IntroObjectiveC/IntroObjectiveCView.m) +:pushpin: This example is available in the Examples App. See example complete [source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/IntroObjectiveC/IntroObjectiveCView.m) ## Important notes about PinLayout's Objective-c interface @@ -31,7 +34,7 @@ This example implement the PinLayout's Intro example using objective-c The PinLayout's objective-c interface is available using the property `pinObjc` (instead of `pin` in Swift) ``` - [[view.pinObjc top] layout]; + view.pinObjc.top().layout(); ``` #### `layout()` @@ -47,6 +50,6 @@ When using the Objective-c interface, the `layout` method must be called explici view.pin.width(100) // Objective-c - [[view.pinObjc width:100] layout]; + view.pinObjc.width(100).layout(); ``` diff --git a/docs/pinlayout_example_adjust_to_container-landscape.png b/docs/pinlayout_example_adjust_to_container-landscape.png index fb45561d..9e0693f4 100644 Binary files a/docs/pinlayout_example_adjust_to_container-landscape.png and b/docs/pinlayout_example_adjust_to_container-landscape.png differ diff --git a/docs/pinlayout_example_wrapContent.png b/docs/pinlayout_example_wrapContent.png new file mode 100644 index 00000000..372a06cb Binary files /dev/null and b/docs/pinlayout_example_wrapContent.png differ diff --git a/docs/pinlayout_exampleapp_automatic_sizing.png b/docs/pinlayout_exampleapp_automatic_sizing.png new file mode 100644 index 00000000..77157fba Binary files /dev/null and b/docs/pinlayout_exampleapp_automatic_sizing.png differ diff --git a/docs/rtl_support.md b/docs/rtl_support.md index 0db33396..8f86f9b0 100644 --- a/docs/rtl_support.md +++ b/docs/rtl_support.md @@ -37,4 +37,4 @@ override func layoutSubviews() { } ``` -:pushpin: The complete RTL "Introduction example" [source code](https://github.com/mirego/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/IntroRTL/IntroRTLView.swift). This example is available in the [Examples App](#examples_app) \ No newline at end of file +:pushpin: The complete RTL "Introduction example" [source code](https://github.com/layoutBox/PinLayout/blob/master/Example/PinLayoutSample/UI/Examples/IntroRTL/IntroRTLView.swift). This example is available in the [Examples App](#examples_app) \ No newline at end of file diff --git a/docs/xcode_playground.md b/docs/xcode_playground.md index 329f9680..37a92f4e 100644 --- a/docs/xcode_playground.md +++ b/docs/xcode_playground.md @@ -16,7 +16,7 @@ The method will execute PinLayout commands immediately. This method is **require ```swift view.pin.top(20).bottom(20).width(100).layout() - view2.pin.below(of: view).left().right().layout() + view2.pin.below(of: view).horizontally().layout() ``` **TIP**: If your codes needs to work in Xcode playgrounds, you may set to `true` the property `Pin.logMissingLayoutCalls`, this way any missing call to `layout()` will generate a warning in the Xcode console. diff --git a/fastlane/Fastfile b/fastlane/Fastfile deleted file mode 100644 index 5622d104..00000000 --- a/fastlane/Fastfile +++ /dev/null @@ -1,126 +0,0 @@ -# Change the syntax highlighting to Ruby -# All lines starting with a # are ignored when running `fastlane` - -# If you want to automatically update fastlane if a new version is available: -# update_fastlane - -# This is the minimum version number required. -# Update this, if you use features of a newer version -fastlane_version "2.29.0" - -#default_platform :ios - -##### iOS platform -############################################## -platform :ios do - before_all do - end - - lane :install do - bundle_install - cocoapods(use_bundle_exec: true, try_repo_update_on_error: true) - end - - lane :build do - install - - swiftlint( - mode: :lint, - config_file: ".swiftlint.yml", - executable: "Pods/SwiftLint/swiftlint" - ) - - xcodebuild( - project: "PinLayout.xcodeproj", - scheme: "PinLayout-iOS", - destination: "name=iPhone 8 Plus,OS=11.2", - #configuration: "Debug", - build: true, - clean: true - ) - - xcodebuild( - project: "PinLayout.xcodeproj", - scheme: "PinLayout-tvOS", - destination: "name=Apple TV", - build: true, - clean: true - ) - - xcodebuild( - workspace: "PinLayout.xcworkspace", - scheme: "PinLayoutSample", - build: true, - clean: true, - #destination: "name=iPhone 8 Plus,OS=11.2", - destination: "generic/platform=iOS\" CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY=\"" - ) - end - - lane :tests do - scan( - scheme: "PinLayoutSample", - workspace: "PinLayout.xcworkspace", - devices: [ - "iPhone 7 (10.2)", - "iPhone 7 (11.2)", - "iPad Air (11.2)", - "iPhone X (11.2)" - ] - ) - end - - lane :doc do - jazzy - end - - lane :travis do - build - tests - pod_lib_lint(allow_warnings: true, verbose: false) - end -end - - -##### MAC platform -############################################## -platform :mac do - lane :install do - bundle_install - cocoapods(use_bundle_exec: true, try_repo_update_on_error: true) - end - - lane :build do - install - xcodebuild( - project: "PinLayout.xcodeproj", - scheme: "PinLayout-macOS", - #destination: "name=Apple TV", - build: true - #clean: true, - #destination: "generic/platform=iOS\" CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY=\"" - ) - - #xcodebuild( - # workspace: "PinLayout.xcworkspace", - # scheme: "PinLayoutSample", - # build: true, - # clean: true, - # #destination: "name=iPhone 8 Plus,OS=11.2", - # destination: "generic/platform=iOS\" CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY=\"" - #) - end - - #lane :tests do - # scan( - # scheme: "PinLayout-macOS", - # workspace: "PinLayout.xcworkspace", - # destination: "platform=macOS,arch=x86_64" - # ) - #end - - lane :travis do - build - # tests - end -end diff --git a/index.md b/index.md index 7095389e..eae3d345 100644 --- a/index.md +++ b/index.md @@ -1,17 +1,17 @@

- PinLayout + PinLayout

PinLayout

- - + + - + - +


@@ -22,4 +22,4 @@ Extremely Fast views layouting without auto layout. No magic, pure code, full co
-[See GitHub PinLayout' page for the complete documentation](https://github.com/mirego/PinLayout) \ No newline at end of file +[See GitHub PinLayout' page for the complete documentation](https://github.com/layoutBox/PinLayout) \ No newline at end of file