diff --git a/.travis.yml b/.travis.yml index 7a58334e..3b54ea89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode11.2 +osx_image: xcode11.5 cache: - bundler diff --git a/Example/PinLayoutSample.xcodeproj/project.pbxproj b/Example/PinLayoutSample.xcodeproj/project.pbxproj index b785bf1a..0c1e833b 100644 --- a/Example/PinLayoutSample.xcodeproj/project.pbxproj +++ b/Example/PinLayoutSample.xcodeproj/project.pbxproj @@ -41,7 +41,11 @@ 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 */; }; + 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 */; }; @@ -148,8 +152,12 @@ 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 = ""; }; 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 = ""; }; @@ -173,7 +181,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - DE6C3D736B571B80E207DF6A /* Pods_PinLayoutSample.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -183,7 +190,6 @@ 160FB83905049FCEDD18DC8A /* Frameworks */ = { isa = PBXGroup; children = ( - AAD69688AA2A3F0994F3074E /* Pods_PinLayoutSample.framework */, 246812FC1F8D013500462E53 /* NotificationCenter.framework */, ); name = Frameworks; @@ -240,6 +246,7 @@ DFD31B9D212EE4D600566CA4 /* TableViewReadableContent */, 24D18D181F3DECD6008129EF /* IntroRTL */, 2416376F1F8E4BC200EE703A /* IntroObjectiveC */, + C892FA1124A582050086A75E /* AutoSizing */, 24CD1E8D1F8E4B0A00C3A54D /* PinLayoutSample-Bridging-Header.h */, ); path = Examples; @@ -255,6 +262,8 @@ 24F246131FA8D57100B6332E /* UIImageView+Download.swift */, DFD27840211B1A700056BD93 /* UINavigationController+Orientation.swift */, DFD27847211B1D090056BD93 /* UITabBarController+Orientation.swift */, + C892FA1C24A584010086A75E /* ContentService.swift */, + C892FA2024A598170086A75E /* ProxyWrapper.swift */, ); path = Common; sourceTree = ""; @@ -399,6 +408,16 @@ 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 = ( @@ -661,10 +680,12 @@ 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 /* SafeAreaAndMarginsViewController.swift in Sources */, @@ -681,6 +702,7 @@ 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 */, @@ -693,9 +715,11 @@ 24F246111FA8D4D100B6332E /* CollectionViewExampleViewController.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 */, + C892FA1D24A584010086A75E /* ContentService.swift in Sources */, DFD31BA0212EE4F200566CA4 /* TableViewReadableContentView.swift in Sources */, DF39089A211900480049FD56 /* AnimationsViewController.swift in Sources */, 2439CC521E665C6B003326FB /* BetweenView.swift in Sources */, 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/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/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/Menu/MenuViewController.swift b/Example/PinLayoutSample/UI/Menu/MenuViewController.swift index ff8d2bbf..3ed89167 100644 --- a/Example/PinLayoutSample/UI/Menu/MenuViewController.swift +++ b/Example/PinLayoutSample/UI/Menu/MenuViewController.swift @@ -31,6 +31,7 @@ enum PageType: Int { case between case form case wrapContent + case autoSizing case tableViewWithReadable case introRTL case introObjC @@ -50,6 +51,7 @@ enum PageType: Int { case .between: return "Between Example" case .form: return "Form Example" 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" @@ -84,6 +86,8 @@ enum PageType: Int { return FormViewController(pageType: self) case .wrapContent: return WrapContentViewController(pageType: self) + case .autoSizing: + return AutoSizingViewController() case .tableViewWithReadable: return TableViewReadableContentViewController(pageType: self) case .introRTL: diff --git a/PinLayout.podspec b/PinLayout.podspec index 32d192dd..f37c9e4b 100644 --- a/PinLayout.podspec +++ b/PinLayout.podspec @@ -16,6 +16,7 @@ Pod::Spec.new do |spec| spec.author = { "Luc Dion" => "luc_dion@yahoo.com" } spec.source = { :git => "https://github.com/layoutBox/PinLayout.git", :tag => "#{spec.version}" } spec.source_files = "Sources/**/*.swift" + spec.swift_version = '5.0' spec.ios.deployment_target = '8.0' spec.ios.frameworks = 'Foundation', 'CoreGraphics', 'UIKit' diff --git a/PinLayout.xcodeproj/project.pbxproj b/PinLayout.xcodeproj/project.pbxproj index e5175b65..e39652e1 100644 --- a/PinLayout.xcodeproj/project.pbxproj +++ b/PinLayout.xcodeproj/project.pbxproj @@ -44,6 +44,9 @@ 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 */; }; @@ -230,9 +233,10 @@ 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 = 1; }; + 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 = ""; }; @@ -375,6 +379,7 @@ DF702D8C20D33BA90062045C /* PinLayout+Size.swift */, DF702D8B20D33BA90062045C /* PinLayout+WrapContent.swift */, C80435D220D0891C00EB1BD7 /* SizeCalculable.swift */, + C8291E40247A242600E95886 /* AutoSizeCalculable.swift */, DFF222CC20B999BD00AC2A84 /* Types.swift */, DF702DA720D33D2A0062045C /* Extensions */, DFA06B031E8B38B300B6D5E7 /* Impl */, @@ -418,7 +423,6 @@ DE878C8BA276883C90524382 /* Pods-PinLayoutTests-tvOS.debug.xcconfig */, 89672C5CCF1F123104855629 /* Pods-PinLayoutTests-tvOS.release.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -895,6 +899,7 @@ 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 */, ); @@ -927,6 +932,7 @@ DF46F684212442EE0055B081 /* PEdgeInsets+Operators.swift in Sources */, 24949A2E1EF69474003643D3 /* Filters.swift in Sources */, DFB3ECB12061602F005F226B /* PinSafeArea.swift in Sources */, + C8291E41247A242600E95886 /* AutoSizeCalculable.swift in Sources */, DF702DAB20D33D660062045C /* UIView+PinLayout.swift in Sources */, C80435D520D0898000EB1BD7 /* Layoutable+PinLayout.swift in Sources */, ); @@ -1018,6 +1024,7 @@ 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 */, ); @@ -1088,7 +1095,6 @@ SDKROOT = appletvos; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; - SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.0; }; @@ -1112,7 +1118,6 @@ SDKROOT = appletvos; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; - SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.0; }; @@ -1176,7 +1181,7 @@ 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; VERSIONING_SYSTEM = "apple-generic"; @@ -1234,7 +1239,7 @@ 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; VALIDATE_PRODUCT = YES; @@ -1260,7 +1265,6 @@ PRODUCT_NAME = PinLayout; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -1280,7 +1284,6 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.mirego.PinLayout-iOS"; PRODUCT_NAME = PinLayout; SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; }; name = Release; }; @@ -1295,7 +1298,6 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mirego.PinLayoutTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -1310,7 +1312,6 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mirego.PinLayoutTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; }; name = Release; }; @@ -1336,7 +1337,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SUPPORTED_PLATFORMS = macosx; - SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -1362,7 +1362,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SUPPORTED_PLATFORMS = macosx; - SWIFT_VERSION = 4.0; }; name = Release; }; @@ -1395,7 +1394,6 @@ SDKROOT = macosx; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = macosx; - SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -1428,7 +1426,6 @@ SDKROOT = macosx; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = macosx; - SWIFT_VERSION = 4.0; }; name = Release; }; @@ -1451,7 +1448,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; - SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 11.3; }; @@ -1476,7 +1472,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; - SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 11.3; }; diff --git a/Podfile.lock b/Podfile.lock index dcdba953..32a1f5c6 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,6 +1,6 @@ PODS: - Nimble (8.0.2) - - PinLayout (1.8.10) + - PinLayout (1.8.13) - Quick (2.1.0) - SwiftLint (0.35.0) @@ -22,7 +22,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Nimble: 622629381bda1dd5678162f21f1368cec7cbba60 - PinLayout: 641bc9679f73d3da35e04bb5180547cf55fd92d7 + PinLayout: e41a50260a76508b7a7fcee8955c76639adc69bc Quick: 4be43f6634acfa727dd106bdf3929ce125ffa79d SwiftLint: 5553187048b900c91aa03552807681bb6b027846 diff --git a/README.md b/README.md index e2728626..5116ecb6 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ Extremely Fast views layouting without auto layout. No magic, pure code, full co * [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) @@ -1477,6 +1478,58 @@ This example centered horizontally the view B in the space remaining at the righ
+ +## Automatic Sizing (UIView conly) +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. + ## UIView's transforms @@ -1749,6 +1802,7 @@ Included examples: * 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. @@ -1765,6 +1819,7 @@ Included examples: +


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/UIView+PinLayout.swift b/Sources/Extensions/UIView+PinLayout.swift index fb0a870d..2f551234 100644 --- a/Sources/Extensions/UIView+PinLayout.swift +++ b/Sources/Extensions/UIView+PinLayout.swift @@ -38,6 +38,8 @@ extension UIView: Layoutable, SizeCalculable { } 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 @@ -95,4 +97,55 @@ extension UIView: Layoutable, SizeCalculable { } } +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/Impl/PinLayout+Layouting.swift b/Sources/Impl/PinLayout+Layouting.swift index efc69e90..e9aacb61 100644 --- a/Sources/Impl/PinLayout+Layouting.swift +++ b/Sources/Impl/PinLayout+Layouting.swift @@ -133,14 +133,13 @@ extension PinLayout { if !validateComputedHeight(newRect.size.height) { newRect.size.height = view.getRect(keepTransform: keepTransform).height } - - /* - 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. - */ - view.setRect(newRect, keepTransform: keepTransform) + + 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) + } } private func handlePinEdges() { diff --git a/Sources/Pin.swift b/Sources/Pin.swift index 9c023b58..08842a02 100644 --- a/Sources/Pin.swift +++ b/Sources/Pin.swift @@ -45,6 +45,8 @@ import Foundation self.layoutDirection = direction } + internal static var autoSizingInProgress: Bool = false + // // Warnings // diff --git a/build-ci.sh b/build-ci.sh index 4c395e57..8756e671 100755 --- a/build-ci.sh +++ b/build-ci.sh @@ -8,16 +8,16 @@ echo "===============================" && echo "PinLayout-iOS" && echo "===============================" && time xcodebuild build -project PinLayout.xcodeproj -scheme PinLayout-iOS \ - -derivedDataPath $DERIVED_DATA -sdk iphonesimulator13.2 \ - -destination 'platform=iOS Simulator,name=iPhone 8,OS=13.2' \ + -derivedDataPath $DERIVED_DATA -sdk iphonesimulator13.5 \ + -destination 'platform=iOS Simulator,name=iPhone 8,OS=13.5' \ | xcpretty && echo "===============================" && echo "PinLayout-tvOS" && echo "===============================" && time xcodebuild build -project PinLayout.xcodeproj -scheme PinLayout-tvOS \ - -derivedDataPath $DERIVED_DATA -sdk appletvsimulator13.2 \ - -destination 'platform=tvOS Simulator,name=Apple TV 4K,OS=13.2' \ + -derivedDataPath $DERIVED_DATA -sdk appletvsimulator13.4 \ + -destination 'platform=tvOS Simulator,name=Apple TV 4K,OS=13.4' \ | xcpretty && echo "===============================" && @@ -31,7 +31,7 @@ echo "===============================" && echo "PinLayoutSample" && echo "===============================" && time xcodebuild build -workspace PinLayout.xcworkspace -scheme PinLayoutSample \ - -derivedDataPath $DERIVED_DATA -sdk iphonesimulator13.2 \ + -derivedDataPath $DERIVED_DATA -sdk iphonesimulator13.5 \ -destination 'platform=iOS Simulator,name=iPhone 7,OS=11.4' \ | xcpretty && @@ -39,39 +39,39 @@ echo "===============================" && echo "iOS unit test" && echo "===============================" && time xcodebuild build test -workspace PinLayout.xcworkspace -scheme PinLayout-iOS \ - -derivedDataPath $DERIVED_DATA -sdk iphonesimulator13.2 \ + -derivedDataPath $DERIVED_DATA -sdk iphonesimulator13.5 \ -destination 'platform=iOS Simulator,name=iPhone 7,OS=11.4' \ | xcpretty && - + time xcodebuild build test -workspace PinLayout.xcworkspace -scheme PinLayout-iOS \ - -derivedDataPath $DERIVED_DATA -sdk iphonesimulator13.2 \ + -derivedDataPath $DERIVED_DATA -sdk iphonesimulator13.5 \ -destination 'platform=iOS Simulator,name=iPhone 8,OS=12.2' \ | xcpretty && time xcodebuild build test -workspace PinLayout.xcworkspace -scheme PinLayout-iOS \ - -derivedDataPath $DERIVED_DATA -sdk iphonesimulator13.2 \ - -destination 'platform=iOS Simulator,name=iPhone 8,OS=13.2' \ + -derivedDataPath $DERIVED_DATA -sdk iphonesimulator13.5 \ + -destination 'platform=iOS Simulator,name=iPhone 8,OS=13.5' \ | xcpretty && -# echo "===============================" -# echo "tvOS unit test" -# echo "===============================" +# echo "===============================" +# echo "tvOS unit test" +# echo "===============================" # time xcodebuild build test -workspace PinLayout.xcworkspace -scheme PinLayout-tvOS \ -# -derivedDataPath $DERIVED_DATA -sdk appletvsimulator13.2 \ +# -derivedDataPath $DERIVED_DATA -sdk appletvsimulator13.4 \ # -destination 'platform=tvOS Simulator,name=Apple TV 4K,OS=12.2' \ -# | xcpretty +# | xcpretty # time xcodebuild build test -workspace PinLayout.xcworkspace -scheme PinLayout-tvOS \ -# -derivedDataPath $DERIVED_DATA -sdk appletvsimulator13.2 \ -# -destination 'platform=tvOS Simulator,name=Apple TV 4K,OS=13.2' \ -# | xcpretty +# -derivedDataPath $DERIVED_DATA -sdk appletvsimulator13.4 \ +# -destination 'platform=tvOS Simulator,name=Apple TV 4K,OS=13.4' \ +# | xcpretty -# echo "===============================" -# echo "macOS unit test" -# echo "===============================" +# echo "===============================" +# echo "macOS unit test" +# echo "===============================" # time xcodebuild clean test -workspace PinLayout.xcworkspace -scheme PinLayout-macOS \ # -derivedDataPath $DERIVED_DATA -sdk macosx10.15 \ -# | xcpretty +# | xcpretty echo "===============================" && echo " Cocoapods: iOS Empty project" && @@ -80,8 +80,8 @@ cd TestProjects/cocoapods/ios && rm -rf $DERIVED_DATA && pod install && time xcodebuild clean build -workspace PinLayout-iOS.xcworkspace -scheme PinLayout-iOS \ - -sdk iphonesimulator13.2 -derivedDataPath $DERIVED_DATA \ - -destination 'platform=iOS Simulator,name=iPhone 8,OS=13.2' \ + -sdk iphonesimulator13.5 -derivedDataPath $DERIVED_DATA \ + -destination 'platform=iOS Simulator,name=iPhone 8,OS=13.5' \ | xcpretty && cd ../../.. && @@ -105,8 +105,8 @@ cd TestProjects/cocoapods/tvos && rm -rf $DERIVED_DATA && pod install && time xcodebuild clean build -workspace PinLayout-tvOS.xcworkspace -scheme PinLayout-tvOS \ - -sdk appletvsimulator13.2 -derivedDataPath $DERIVED_DATA \ - -destination 'platform=tvOS Simulator,name=Apple TV,OS=13.2' \ + -sdk appletvsimulator13.4 -derivedDataPath $DERIVED_DATA \ + -destination 'platform=tvOS Simulator,name=Apple TV,OS=13.4' \ | xcpretty && cd ../../.. && @@ -120,9 +120,9 @@ 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 iphonesimulator13.2 \ + -scheme PinLayout-Carthage-iOS -sdk iphonesimulator13.5 \ -derivedDataPath $DERIVED_DATA \ - -destination 'platform=iOS Simulator,name=iPhone 8,OS=13.2' \ + -destination 'platform=iOS Simulator,name=iPhone 8,OS=13.5' \ | xcpretty && cd ../../.. && @@ -131,18 +131,18 @@ echo " Pod lib lint" && echo "===============================" && time bundle exec pod lib lint --allow-warnings -# echo "==========================================" -# echo " Swift Package Manager: iOS Empty project " -# echo "==========================================" -# cd TestProjects/swift-package-manager/ios -# rm -rf $DERIVED_DATA -# rm -rf .build +# 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 iphonesimulator13.2 -derivedDataPath $DERIVED_DATA \ -# -destination 'platform=iOS Simulator,name=iPhone 8,OS=13.2' \ -# | xcpretty -# cd ../../.. -# +# swift package show-dependencies --format json +# time xcodebuild clean build -project PinLayout-Carthage-iOS.xcodeproj -scheme PinLayout-Carthage-iOS -sdk iphonesimulator13.5 -derivedDataPath $DERIVED_DATA \ +# -destination 'platform=iOS Simulator,name=iPhone 8,OS=13.5' \ +# | xcpretty +# cd ../../.. +# # #OTHER_SWIFT_FLAGS='-Xfrontend -debug-time-function-bodies' -# xcodebuild clean test -workspace PinLayout.xcworkspace -scheme PinLayout-macOS -derivedDataPath $DERIVED_DATA -sdk macosx10.15 \ No newline at end of file +# xcodebuild clean test -workspace PinLayout.xcworkspace -scheme PinLayout-macOS -derivedDataPath $DERIVED_DATA -sdk macosx10.15 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