Compare commits

...

2 Commits

Author SHA1 Message Date
Juanpe Catalán a9c22f502a Improve multiline height calculation (#466)
* update line height logic

* update property name

* update README

* update README

* Update README.md

* fix typo
2021-11-23 11:25:59 +01:00
Juanpe ee17db61c6 Bump version 1.25.2 2021-10-21 14:49:00 +00:00
21 changed files with 216 additions and 142 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -154,7 +154,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1250;
LastUpgradeCheck = 1250;
LastUpgradeCheck = 1300;
TargetAttributes = {
F556F59E26CD201B00A80B83 = {
CreatedOnToolsVersion = 12.5.1;
@@ -395,7 +395,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -414,7 +414,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -145,7 +145,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1250;
LastUpgradeCheck = 1250;
LastUpgradeCheck = 1300;
TargetAttributes = {
F556F5F326CD221300A80B83 = {
CreatedOnToolsVersion = 12.5.1;
@@ -317,7 +317,7 @@
SDKROOT = appletvos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TVOS_DEPLOYMENT_TARGET = 9.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
};
name = Debug;
};
@@ -371,7 +371,7 @@
SDKROOT = appletvos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TVOS_DEPLOYMENT_TARGET = 9.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
VALIDATE_PRODUCT = YES;
};
name = Release;
@@ -391,7 +391,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
};
name = Debug;
};
@@ -410,7 +410,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
};
name = Release;
};
+33 -28
View File
@@ -265,13 +265,30 @@ Besides, you can decide how many lines you want. If ```numberOfLines``` is set
You can set some properties for multilines elements.
| Property | Values | Default | Preview
| Property | Type | Default | Preview
| ------- | ------- |------- | -------
| **Filling percent** of the last line.<br/>Please note that for views without multiple lines, the single line will be considered as the last line and **lastLineFillPercent** will be applied to that single line. | `0...100` | `70%`| ![](Assets/multiline_lastline.png)
| **Corner radius** of lines. (**NEW**) | `0...10` | `0` | ![](Assets/multiline_corner.png)
| **lastLineFillPercent** | `CGFloat` | `70`| ![](Assets/multiline_lastline.png)
| **linesCornerRadius** | `Int` | `0` | ![](Assets/multiline_corner.png)
| **skeletonLineSpacing** | `CGFloat` | `10` | ![](Assets/multiline_lineSpacing.png)
| **skeletonPaddingInsets** | `UIEdgeInsets` | `.zero` | ![](Assets/multiline_insets.png)
| **skeletonTextLineHeight** | `SkeletonTextLineHeight` | `.fixed(15)` | ![](Assets/multiline_lineHeight.png)
<br />
> **⚠️ DEPRECATED!**
>
> **useFontLineHeight** has been deprecated. You can use **skeletonTextLineHeight** instead:
> ```swift
> descriptionTextView.skeletonTextLineHeight = .relativeToFont
> ```
> **📣 IMPORTANT!**
>
> Please note that for views without multiple lines, the single line will be considered
> as the last line.
<br />
To modify the percent or radius **using code**, set the properties:
```swift
@@ -289,21 +306,19 @@ Or, if you prefer use **IB/Storyboard**:
The skeletons have a default appearance. So, when you don't specify the color, gradient or multilines properties, `SkeletonView` uses the default values.
Default values:
- **tintColor**: UIColor
- **tintColor**: `UIColor`
- *default: `.skeletonDefault` (same as `.clouds` but adaptive to dark mode)*
- **gradient**: SkeletonGradient
- *default: `SkeletonGradient(baseColor: .skeletonDefault)`*
- **multilineHeight**: CGFloat
- **multilineHeight**: `CGFloat`
- *default: 15*
- **useFontLineHeight**: Bool
- *default: true*
- **multilineSpacing**: CGFloat
- **multilineSpacing**: `CGFloat`
- *default: 10*
- **multilineLastLineFillPercent**: Int
- **multilineLastLineFillPercent**: `Int`
- *default: 70*
- **multilineCornerRadius**: Int
- **multilineCornerRadius**: `Int`
- *default: 0*
- **skeletonCornerRadius**: CGFloat (IBInspectable) (Make your skeleton view with corner)
- **skeletonCornerRadius**: `CGFloat` (IBInspectable) (Make your skeleton view with corner)
- *default: 0*
To get these default values you can use `SkeletonAppearance.default`. Using this property you can set the values as well:
@@ -312,12 +327,12 @@ SkeletonAppearance.default.multilineHeight = 20
SkeletonAppearance.default.tintColor = .green
```
You can also specifiy these line appearance properties on a per-label basis:
- **lastLineFillPercent**: Int
- **linesCornerRadius**: Int
- **skeletonLineSpacing**: CGFloat
- **skeletonPaddingInsets**: UIEdgeInsets
- **useFontLineHeight**: Bool
> **⚠️ DEPRECATED!**
>
> **useFontLineHeight** has been deprecated. You can use **textLineHeight** instead:
> ```swift
> SkeletonAppearance.default.textLineHeight = .relativeToFont
> ```
### 🎨 Custom colors
@@ -464,16 +479,6 @@ view.showSkeleton()
**Hierarchy in collections**
Here is an illustration that shows how you should specify which elements are skeletonables when you are using an `UITableView`:
<img src="Assets/tableview_scheme.png" width="700px">
As you can see, we have to make skeletonable the tableview, the cell and the UI elements, but we don't need to set as skeletonable the `contentView`
**Skeleton views layout**
Sometimes skeleton layout may not fit your layout because the parent view bounds have changed. ~For example, rotating the device.~
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SkeletonView"
s.version = "1.25.1"
s.version = "1.25.2"
s.summary = "An elegant way to show users that something is happening and also prepare them to which contents he is waiting"
s.description = <<-DESC
Today almost all apps have async processes, as API requests, long runing processes, etc. And while the processes are working, usually developers place a loading view to show users that something is going on.
+6
View File
@@ -99,6 +99,8 @@
F556F6E126CE367600A80B83 /* UIView+SkeletonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6DF26CE367600A80B83 /* UIView+SkeletonView.swift */; };
F556F6F626CE876300A80B83 /* UIView+Swizzling.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6DC26CE33CE00A80B83 /* UIView+Swizzling.swift */; };
F556F70826D38F3100A80B83 /* SkeletonTreeNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F70726D38F3100A80B83 /* SkeletonTreeNode.swift */; };
F5C84884274BB6F000004D1A /* SkeletonTextLineHeight.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5C84883274BB6F000004D1A /* SkeletonTextLineHeight.swift */; };
F5C84885274BB6F000004D1A /* SkeletonTextLineHeight.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5C84883274BB6F000004D1A /* SkeletonTextLineHeight.swift */; };
OBJ_101 /* Int+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_31 /* Int+Extensions.swift */; };
OBJ_103 /* UIColor+Skeleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_33 /* UIColor+Skeleton.swift */; };
OBJ_104 /* UITableView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_34 /* UITableView+Extensions.swift */; };
@@ -208,6 +210,7 @@
F556F6DC26CE33CE00A80B83 /* UIView+Swizzling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Swizzling.swift"; sourceTree = "<group>"; };
F556F6DF26CE367600A80B83 /* UIView+SkeletonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+SkeletonView.swift"; sourceTree = "<group>"; };
F556F70726D38F3100A80B83 /* SkeletonTreeNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonTreeNode.swift; sourceTree = "<group>"; };
F5C84883274BB6F000004D1A /* SkeletonTextLineHeight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonTextLineHeight.swift; sourceTree = "<group>"; };
OBJ_11 /* SkeletonLayerBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonLayerBuilder.swift; sourceTree = "<group>"; };
OBJ_12 /* SkeletonMultilineLayerBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonMultilineLayerBuilder.swift; sourceTree = "<group>"; };
OBJ_14 /* CollectionSkeleton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionSkeleton.swift; sourceTree = "<group>"; };
@@ -515,6 +518,7 @@
OBJ_55 /* SkeletonGradient.swift */,
F556F6B826CE262700A80B83 /* GradientDirection.swift */,
F556F6C126CE27FD00A80B83 /* SkeletonType.swift */,
F5C84883274BB6F000004D1A /* SkeletonTextLineHeight.swift */,
);
path = Models;
sourceTree = "<group>";
@@ -822,6 +826,7 @@
F556F6B626CE258300A80B83 /* GradientDirection+Animations.swift in Sources */,
F556F69F26CD553B00A80B83 /* UIView+Extensions.swift in Sources */,
F556F58526CD1F3900A80B83 /* Recoverable.swift in Sources */,
F5C84885274BB6F000004D1A /* SkeletonTextLineHeight.swift in Sources */,
F556F58626CD1F3900A80B83 /* RecoverableViewState.swift in Sources */,
F556F58726CD1F3900A80B83 /* SkeletonAnimationBuilder.swift in Sources */,
F556F58826CD1F3900A80B83 /* SkeletonConfig.swift in Sources */,
@@ -904,6 +909,7 @@
F556F69E26CD553B00A80B83 /* UIView+Extensions.swift in Sources */,
OBJ_117 /* Recoverable.swift in Sources */,
OBJ_118 /* RecoverableViewState.swift in Sources */,
F5C84884274BB6F000004D1A /* SkeletonTextLineHeight.swift in Sources */,
OBJ_119 /* SkeletonAnimationBuilder.swift in Sources */,
OBJ_120 /* SkeletonConfig.swift in Sources */,
OBJ_121 /* SkeletonFlowHandler.swift in Sources */,
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "9999"
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "9999"
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -26,8 +26,8 @@ public class SkeletonViewAppearance {
public var gradient = SkeletonGradient(baseColor: .skeletonDefault)
public var multilineHeight: CGFloat = 15
public var useFontLineHeight: Bool = true
public lazy var textLineHeight: SkeletonTextLineHeight = .fixed(SkeletonAppearance.default.multilineHeight)
public var multilineSpacing: CGFloat = 10
@@ -48,3 +48,47 @@ public extension UIView {
}
}
public extension UILabel {
@IBInspectable
@available(*, deprecated, renamed: "skeletonTextLineHeight")
var useFontLineHeight: Bool {
get {
textLineHeight == .relativeToFont
}
set {
textLineHeight = newValue ? .relativeToFont : .fixed(SkeletonAppearance.default.multilineHeight)
}
}
}
public extension UITextView {
@IBInspectable
@available(*, deprecated, renamed: "skeletonTextLineHeight")
var useFontLineHeight: Bool {
get {
textLineHeight == .relativeToFont
}
set {
textLineHeight = newValue ? .relativeToFont : .fixed(SkeletonAppearance.default.multilineHeight)
}
}
}
public extension SkeletonViewAppearance {
@available(*, deprecated, renamed: "textLineHeight")
var useFontLineHeight: Bool {
get {
textLineHeight == .relativeToFont
}
set {
textLineHeight = newValue ? .relativeToFont : .fixed(SkeletonAppearance.default.multilineHeight)
}
}
}
@@ -0,0 +1,30 @@
//
// Copyright SkeletonView. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// SkeletonTextLineHeight.swift
//
// Created by Juanpe Catalán on 22/11/21.
import UIKit
public enum SkeletonTextLineHeight: Equatable {
/// Calculates the line height based on the font line height.
case relativeToFont
/// Calculates the line height based on the height constraints.
///
/// If no constraints exist, the height will be set to the `multilineHeight`
/// value defined in the `SkeletonAppearance`.
case relativeToConstraints
/// Returns the specific height specified as the associated value.
case fixed(CGFloat)
}
@@ -33,10 +33,4 @@ public extension UILabel {
set { multilineSpacing = newValue }
}
@IBInspectable
var useFontLineHeight: Bool {
get { usesTextHeightForLines }
set { usesTextHeightForLines = newValue }
}
}
@@ -16,8 +16,21 @@ import UIKit
public extension UILabel {
var skeletonPaddingInsets: UIEdgeInsets {
get { return paddingInsets }
set { paddingInsets = newValue }
get {
paddingInsets
}
set {
paddingInsets = newValue
}
}
var skeletonTextLineHeight: SkeletonTextLineHeight {
get {
textLineHeight
}
set {
textLineHeight = newValue
}
}
}
@@ -32,11 +32,5 @@ public extension UITextView {
get { return multilineSpacing }
set { multilineSpacing = newValue }
}
@IBInspectable
var useFontLineHeight: Bool {
get { usesTextHeightForLines }
set { usesTextHeightForLines = newValue }
}
}
@@ -16,8 +16,21 @@ import UIKit
public extension UITextView {
var skeletonPaddingInsets: UIEdgeInsets {
get { return paddingInsets }
set { paddingInsets = newValue }
get {
paddingInsets
}
set {
paddingInsets = newValue
}
}
var skeletonTextLineHeight: SkeletonTextLineHeight {
get {
textLineHeight
}
set {
textLineHeight = newValue
}
}
}
@@ -60,7 +60,7 @@ struct SkeletonLayer {
func addTextLinesIfNeeded() {
guard let textView = holderAsTextView else { return }
let config = SkeletonMultilinesLayerConfig(lines: textView.numberOfLines,
lineHeight: textView.lineHeight,
lineHeight: textView.estimatedLineHeight,
type: type,
lastLineFillPercent: textView.lastLineFillingPercent,
multilineCornerRadius: textView.multilineCornerRadius,
@@ -76,7 +76,7 @@ struct SkeletonLayer {
func updateLinesIfNeeded() {
guard let textView = holderAsTextView else { return }
let config = SkeletonMultilinesLayerConfig(lines: textView.numberOfLines,
lineHeight: textView.lineHeight,
lineHeight: textView.estimatedLineHeight,
type: type,
lastLineFillPercent: textView.lastLineFillingPercent,
multilineCornerRadius: textView.multilineCornerRadius,
@@ -15,15 +15,16 @@ import UIKit
protocol SkeletonTextNode {
var lineHeight: CGFloat { get }
var textLineHeight: SkeletonTextLineHeight { get }
var estimatedLineHeight: CGFloat { get }
var numberOfLines: Int { get }
var textAlignment: NSTextAlignment { get }
var lastLineFillingPercent: Int { get }
var multilineCornerRadius: Int { get }
var multilineSpacing: CGFloat { get }
var paddingInsets: UIEdgeInsets { get }
var usesTextHeightForLines: Bool { get }
var shouldCenterTextVertically: Bool { get }
}
enum SkeletonTextNodeAssociatedKeys {
@@ -33,26 +34,31 @@ enum SkeletonTextNodeAssociatedKeys {
static var multilineSpacing = "multilineSpacing"
static var paddingInsets = "paddingInsets"
static var backupHeightConstraints = "backupHeightConstraints"
static var usesTextHeightForLines = "usesTextHeightForLines"
static var textLineHeight = "textLineHeight"
}
extension UILabel: SkeletonTextNode {
var lineHeight: CGFloat {
let constraintsLineHeight = backupHeightConstraints.first?.constant ?? SkeletonAppearance.default.multilineHeight
if useFontLineHeight,
let fontLineHeight = font?.lineHeight {
return fontLineHeight > constraintsLineHeight ? constraintsLineHeight : fontLineHeight
} else {
return constraintsLineHeight
var estimatedLineHeight: CGFloat {
switch textLineHeight {
case .fixed(let height):
return height
case .relativeToFont:
return fontLineHeight ?? SkeletonAppearance.default.multilineHeight
case .relativeToConstraints:
guard let constraintsLineHeight = heightConstraints.first?.constant,
numberOfLines != 0 else {
return SkeletonAppearance.default.multilineHeight
}
return constraintsLineHeight / CGFloat(numberOfLines)
}
}
var usesTextHeightForLines: Bool {
get { return ao_get(pkey: &SkeletonTextNodeAssociatedKeys.usesTextHeightForLines) as? Bool ?? SkeletonAppearance.default.useFontLineHeight }
set { ao_set(newValue, pkey: &SkeletonTextNodeAssociatedKeys.usesTextHeightForLines) }
var textLineHeight: SkeletonTextLineHeight {
get { return ao_get(pkey: &SkeletonTextNodeAssociatedKeys.textLineHeight) as? SkeletonTextLineHeight ?? SkeletonAppearance.default.textLineHeight }
set { ao_set(newValue, pkey: &SkeletonTextNodeAssociatedKeys.textLineHeight) }
}
var lastLineFillingPercent: Int {
@@ -83,25 +89,34 @@ extension UILabel: SkeletonTextNode {
var shouldCenterTextVertically: Bool {
true
}
var fontLineHeight: CGFloat? {
if let attributes = attributedText?.attributes(at: 0, effectiveRange: nil),
let fontAttribute = attributes.first(where: { $0.key == .font }) {
return fontAttribute.value as? CGFloat ?? font.lineHeight
} else {
return font.lineHeight
}
}
}
extension UITextView: SkeletonTextNode {
var lineHeight: CGFloat {
let constraintsLineHeight = heightConstraints.first?.constant ?? SkeletonAppearance.default.multilineHeight
if useFontLineHeight,
let fontLineHeight = font?.lineHeight {
return fontLineHeight > constraintsLineHeight ? constraintsLineHeight : fontLineHeight
} else {
return constraintsLineHeight
var estimatedLineHeight: CGFloat {
switch textLineHeight {
case .fixed(let height):
return height
case .relativeToFont:
return fontLineHeight ?? SkeletonAppearance.default.multilineHeight
case .relativeToConstraints:
return SkeletonAppearance.default.multilineHeight
}
}
var usesTextHeightForLines: Bool {
get { return ao_get(pkey: &SkeletonTextNodeAssociatedKeys.usesTextHeightForLines) as? Bool ?? SkeletonAppearance.default.useFontLineHeight }
set { ao_set(newValue, pkey: &SkeletonTextNodeAssociatedKeys.usesTextHeightForLines) }
var textLineHeight: SkeletonTextLineHeight {
get { return ao_get(pkey: &SkeletonTextNodeAssociatedKeys.textLineHeight) as? SkeletonTextLineHeight ?? SkeletonAppearance.default.textLineHeight }
set { ao_set(newValue, pkey: &SkeletonTextNodeAssociatedKeys.textLineHeight) }
}
var numberOfLines: Int {
@@ -137,4 +152,14 @@ extension UITextView: SkeletonTextNode {
var shouldCenterTextVertically: Bool {
false
}
var fontLineHeight: CGFloat? {
if let attributes = attributedText?.attributes(at: 0, effectiveRange: nil),
let fontAttribute = attributes.first(where: { $0.key == .font }) {
return fontAttribute.value as? CGFloat ?? font?.lineHeight
} else {
return font?.lineHeight
}
}
}
@@ -16,7 +16,7 @@ import UIKit
extension UILabel {
var desiredHeightBasedOnNumberOfLines: CGFloat {
let spaceNeededForEachLine = lineHeight * CGFloat(numberOfLines)
let spaceNeededForEachLine = estimatedLineHeight * CGFloat(numberOfLines)
let spaceNeededForSpaces = skeletonLineSpacing * CGFloat(numberOfLines - 1)
let padding = paddingInsets.top + paddingInsets.bottom
@@ -19,67 +19,17 @@ class SkeletonDebugTests: XCTestCase {
func testSkeletonDescriptionWithViewNotSkeletonableNotReturnsSkullEmojiAndChildren() {
/// given
let view = UIView()
let expectedDescription = "<UIView: \(Unmanaged.passUnretained(view).toOpaque())>"
let expectedDictionary: [String : Any] = [
"isSkeletonable" : false,
"type" : "UIView",
"reference" : "\(Unmanaged.passUnretained(view).toOpaque())"
]
/// when
let obtainedDescription = view.skeletonDescription
let obtainedDictionary = view.sk.treeNode.dictionaryRepresentation
/// then
XCTAssertEqual(expectedDescription, obtainedDescription)
}
func testSkeletonDescriptionWithViewSkeletonableReturnsSkullEmojiButNotChildren() {
/// given
let view = UIView()
view.isSkeletonable = true
let expectedDescription = "<UIView: \(Unmanaged.passUnretained(view).toOpaque()) | ☠️ >"
/// when
let obtainedDescription = view.skeletonDescription
/// then
XCTAssertEqual(expectedDescription, obtainedDescription)
}
func testSkeletonDescriptionWithViewSkeletonableAndChildrenReturnsSkullEmojiAndChildren() {
/// given
let view = UIView()
let childView = UIView()
view.addSubview(childView)
view.isSkeletonable = true
childView.isSkeletonable = true
let expectedDescription = """
<UIView: \(Unmanaged.passUnretained(view).toOpaque()) | (1) subSkeletons | ☠️ >
"""
/// when
let obtainedDescription = view.skeletonDescription
/// then
XCTAssertEqual(expectedDescription, obtainedDescription)
}
func testSkeletonHierarchyWithSkeletonableViewsReturnsFullHierarchy() {
/// given
let view = UIView()
let childView = UIView()
let subchildView = UIView()
view.addSubview(childView)
childView.addSubview(subchildView)
view.isSkeletonable = true
childView.isSkeletonable = true
subchildView.isSkeletonable = true
let expectedDescription = "⬇⬇ ☠️ Root view hierarchy with Skeletons ⬇⬇<UIView:\(Unmanaged.passUnretained(view).toOpaque()) | (1) subSkeletons | ☠️ ><UIView: \(Unmanaged.passUnretained(childView).toOpaque()) | (1) subSkeletons | ☠️ ><UIView: \(Unmanaged.passUnretained(subchildView).toOpaque()) | ☠️ >"
.replacingOccurrences(of: " ", with: "")
/// when
let obtainedDescription = view.skeletonHierarchy()
.replacingOccurrences(of: "\n", with: "")
.replacingOccurrences(of: " ", with: "")
/// then
XCTAssertEqual(expectedDescription, obtainedDescription)
XCTAssertEqual(expectedDictionary.keys, obtainedDictionary.keys)
}
}