Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add automatic sizeThatFits computation for views #216

Merged
merged 23 commits into from
Jun 30, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 0 additions & 4 deletions Example/PinLayoutSample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
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 */; };
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 */; };
Expand Down Expand Up @@ -148,7 +147,6 @@
24F75B591EE5644E008DB567 /* IntroView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntroView.swift; sourceTree = "<group>"; };
24F75B5A1EE5644E008DB567 /* IntroViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntroViewController.swift; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
Expand All @@ -173,7 +171,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DE6C3D736B571B80E207DF6A /* Pods_PinLayoutSample.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -183,7 +180,6 @@
160FB83905049FCEDD18DC8A /* Frameworks */ = {
isa = PBXGroup;
children = (
AAD69688AA2A3F0994F3074E /* Pods_PinLayoutSample.framework */,
246812FC1F8D013500462E53 /* NotificationCenter.framework */,
);
name = Frameworks;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,13 @@ class ChoiceSelectorView: UIView {

override func layoutSubviews() {
super.layoutSubviews()
_ = layout()
layout()
antoinelamy marked this conversation as resolved.
Show resolved Hide resolved
}

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()
}

private func layout() -> CGSize {
private func layout() {
let margin: CGFloat = 12

if frame.width > 500 {
if bounds.width > 500 {
// The UISegmentedControl is at the top-right corner and the label takes the remaining horizontal space.
segmentedControl.pin.top().right().margin(margin)
textLabel.pin.top().left().before(of: segmentedControl).margin(margin).sizeToFit(.width)
Expand All @@ -69,7 +61,9 @@ 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)
override func sizeThatFits(_ size: CGSize) -> CGSize {
return autoSizeThatFits(size) { layout() }
lucdion marked this conversation as resolved.
Show resolved Hide resolved
}
}
9 changes: 8 additions & 1 deletion PinLayout.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -230,6 +233,7 @@
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 = "<group>"; };
C80435D220D0891C00EB1BD7 /* SizeCalculable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SizeCalculable.swift; sourceTree = "<group>"; };
C80435D420D0898000EB1BD7 /* Layoutable+PinLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Layoutable+PinLayout.swift"; sourceTree = "<group>"; };
C8291E40247A242600E95886 /* AutoSizeCalculable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoSizeCalculable.swift; sourceTree = "<group>"; };
C82DC20B20CE9F6800B7ACF5 /* Layoutable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Layoutable.swift; sourceTree = "<group>"; };
C83588BF20DBC5E600D6E8F9 /* CALayerSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CALayerSpec.swift; sourceTree = "<group>"; };
C83600A520E2949200A3D891 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; usesTabs = 1; };
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -418,7 +423,6 @@
DE878C8BA276883C90524382 /* Pods-PinLayoutTests-tvOS.debug.xcconfig */,
89672C5CCF1F123104855629 /* Pods-PinLayoutTests-tvOS.release.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
Expand Down Expand Up @@ -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 */,
);
Expand Down Expand Up @@ -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 */,
);
Expand Down Expand Up @@ -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 */,
);
Expand Down
4 changes: 2 additions & 2 deletions Podfile.lock
Original file line number Diff line number Diff line change
@@ -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)

Expand All @@ -22,7 +22,7 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
Nimble: 622629381bda1dd5678162f21f1368cec7cbba60
PinLayout: 641bc9679f73d3da35e04bb5180547cf55fd92d7
PinLayout: e41a50260a76508b7a7fcee8955c76639adc69bc
Quick: 4be43f6634acfa727dd106bdf3929ce125ffa79d
SwiftLint: 5553187048b900c91aa03552807681bb6b027846

Expand Down
12 changes: 12 additions & 0 deletions Sources/AutoSizeCalculable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#if os(iOS) || os(tvOS)
import UIKit
#else
import AppKit
#endif

public protocol AutoSizeCalculable {
var autoSizingRect: CGRect? { get set }
var autoSizingRectWithMargins: CGRect? { get set }

func autoSizeThatFits(_ size: CGSize, layoutClosure: () -> Void) -> CGSize
}
45 changes: 45 additions & 0 deletions Sources/Extensions/UIView+PinLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -59,6 +61,11 @@ extension UIView: Layoutable, SizeCalculable {
public func setRect(_ rect: CGRect, keepTransform: Bool) {
let adjustedRect = Coordinates<View>.adjustRectToDisplayScale(rect)

guard !Pin.autoSizingInProgress else {
antoinelamy marked this conversation as resolved.
Show resolved Hide resolved
autoSizingRect = adjustedRect
return
}

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
Expand Down Expand Up @@ -95,4 +102,42 @@ extension UIView: Layoutable, SizeCalculable {
}
}

extension UIView: AutoSizeCalculable {
private struct pinlayoutAssociatedKeys {
static var pinlayoutAutoSizingRect = UnsafeMutablePointer<Int8>.allocate(capacity: 1)
static var pinlayoutAutoSizingRectWithMargins = UnsafeMutablePointer<Int8>.allocate(capacity: 1)
}

public 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)
}
}

public 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 autoSizeThatFits(_ size: CGSize, layoutClosure: () -> Void) -> CGSize {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really would like to extract that function out of the UIView conformance extension but the only thing that prevent me to do so is the call to let adjustedRect = Coordinates<View>.adjustRectToDisplayScale(rect). I think it would make sense to add a displayScale property on the Layoutable protocol and perform that transformation before calling setRect. We really want the resulting rect to be the same in both layout and auto sizing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have perform the changes to do this but I believe it would make more sense in a separate PR as it also fix an issue in the current production code.

Pin.autoSizingInProgress = true
lucdion marked this conversation as resolved.
Show resolved Hide resolved
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)
}

Pin.autoSizingInProgress = false
return boundingRect.size
}
}

#endif
8 changes: 7 additions & 1 deletion Sources/Impl/PinLayout+Layouting.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,13 @@ extension PinLayout {
if !validateComputedHeight(newRect.size.height) {
newRect.size.height = view.getRect(keepTransform: keepTransform).height
}


if Pin.autoSizingInProgress, var autoSizeCalculable = view as? AutoSizeCalculable {
let marginInsets = UIEdgeInsets(top: -_marginTop, left: -_marginLeft, bottom: -_marginBottom, right: -_marginRight)
let rectWithMargins = newRect.inset(by: marginInsets)
autoSizeCalculable.autoSizingRectWithMargins = rectWithMargins
}

antoinelamy marked this conversation as resolved.
Show resolved Hide resolved
antoinelamy marked this conversation as resolved.
Show resolved Hide resolved
/*
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).
Expand Down
2 changes: 2 additions & 0 deletions Sources/Pin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import Foundation
self.layoutDirection = direction
}

internal static var autoSizingInProgress: Bool = false

//
// Warnings
//
Expand Down