Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ab26cd869 | |||
| 8e369c9df2 | |||
| 5fc720d3ab | |||
| 34ce9365f6 | |||
| 3708c0da7f | |||
| 9103c14cc5 | |||
| 5ce17f7a40 | |||
| dfc2d60daa |
@@ -3,6 +3,7 @@ name: CI
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -76,8 +76,8 @@ class ViewController: UIViewController {
|
||||
}
|
||||
|
||||
@IBAction func showOrHideSkeleton(_ sender: Any) {
|
||||
showOrHideSkeletonButton.setTitle((view.isSkeletonActive ? "Show skeleton" : "Hide skeleton"), for: .normal)
|
||||
view.isSkeletonActive ? hideSkeleton() : showSkeleton()
|
||||
showOrHideSkeletonButton.setTitle((view.sk.isSkeletonActive ? "Show skeleton" : "Hide skeleton"), for: .normal)
|
||||
view.sk.isSkeletonActive ? hideSkeleton() : showSkeleton()
|
||||
}
|
||||
|
||||
@IBAction func transitionDurationStepperAction(_ sender: Any) {
|
||||
|
||||
@@ -518,6 +518,14 @@ By default, the user interaction is disabled for skeletonized items, but if you
|
||||
view.isUserInteractionDisabledWhenSkeletonIsActive = false // The view will be active when the skeleton will be active.
|
||||
```
|
||||
|
||||
**Don't use the font line height for the skeleton lines in labels**
|
||||
|
||||
False to disable skeleton to auto-adjust to font height for a `UILabel` or `UITextView`. By default, the skeleton lines height is auto-adjusted to font height to more accurately reflect the text in the label rect rather than using the bounding box.
|
||||
|
||||
```swift
|
||||
label.useFontLineHeight = false
|
||||
```
|
||||
|
||||
**Delayed show skeleton**
|
||||
|
||||
You can delay the presentation of the skeleton if the views update quickly.
|
||||
@@ -540,14 +548,11 @@ func showGradientSkeleton(usingGradient: SkeletonGradient,
|
||||
|
||||
To facilitate the debug tasks when something is not working fine. **`SkeletonView`** has some new tools.
|
||||
|
||||
First, `UIView` has available a new property with his skeleton info:
|
||||
First, `UIView` has available a property with his skeleton info:
|
||||
```swift
|
||||
var skeletonDescription: String
|
||||
var sk.skeletonTreeDescription: String
|
||||
|
||||
```
|
||||
The skeleton representation looks like this:
|
||||
|
||||

|
||||
|
||||
Besides, you can activate the new **debug mode**. You just add the environment variable `SKELETON_DEBUG` and activate it.
|
||||
|
||||
@@ -555,11 +560,21 @@ Besides, you can activate the new **debug mode**. You just add the environment v
|
||||
|
||||
Then, when the skeleton appears, you can see the view hierarchy in the Xcode console.
|
||||
|
||||
<details>
|
||||
<summary>Open to see an output example </summary>
|
||||
<img src="Assets/hierarchy_output.png" />
|
||||
</details>
|
||||
|
||||
```
|
||||
{
|
||||
"type" : "UIView", // UITableView, UILabel...
|
||||
"isSkeletonable" : true,
|
||||
"reference" : "0x000000014751ce30",
|
||||
"children" : [
|
||||
{
|
||||
"type" : "UIView",
|
||||
"isSkeletonable" : true,
|
||||
"children" : [ ... ],
|
||||
"reference" : "0x000000014751cfa0"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Supported OS & SDK Versions**
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "SkeletonView"
|
||||
s.version = "1.23.0"
|
||||
s.version = "1.24.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.
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
F53D731826D399E100249D46 /* SkeletonTreeNode+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F53D731726D399E100249D46 /* SkeletonTreeNode+Extensions.swift */; };
|
||||
F53D731926D399E100249D46 /* SkeletonTreeNode+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F53D731726D399E100249D46 /* SkeletonTreeNode+Extensions.swift */; };
|
||||
F53D731B26D3A35100249D46 /* SkeletonExtended.swift in Sources */ = {isa = PBXBuildFile; fileRef = F53D731A26D3A35100249D46 /* SkeletonExtended.swift */; };
|
||||
F53D731C26D3A35100249D46 /* SkeletonExtended.swift in Sources */ = {isa = PBXBuildFile; fileRef = F53D731A26D3A35100249D46 /* SkeletonExtended.swift */; };
|
||||
F53D731F26D3AC4000249D46 /* SkeletonTreeNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F70726D38F3100A80B83 /* SkeletonTreeNode.swift */; };
|
||||
F556F56626CD1F3900A80B83 /* SkeletonAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* SkeletonAppearance.swift */; };
|
||||
F556F56726CD1F3900A80B83 /* SkeletonLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* SkeletonLayerBuilder.swift */; };
|
||||
F556F56826CD1F3900A80B83 /* SkeletonMultilineLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* SkeletonMultilineLayerBuilder.swift */; };
|
||||
@@ -57,8 +62,8 @@
|
||||
F556F69626CD509E00A80B83 /* Notification+SkeletonFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F69426CD509E00A80B83 /* Notification+SkeletonFlow.swift */; };
|
||||
F556F69E26CD553B00A80B83 /* UIView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F69D26CD553B00A80B83 /* UIView+Extensions.swift */; };
|
||||
F556F69F26CD553B00A80B83 /* UIView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F69D26CD553B00A80B83 /* UIView+Extensions.swift */; };
|
||||
F556F6A126CD566C00A80B83 /* UIView+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6A026CD566C00A80B83 /* UIView+Debug.swift */; };
|
||||
F556F6A226CD566C00A80B83 /* UIView+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6A026CD566C00A80B83 /* UIView+Debug.swift */; };
|
||||
F556F6A126CD566C00A80B83 /* UIView+SKExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6A026CD566C00A80B83 /* UIView+SKExtensions.swift */; };
|
||||
F556F6A226CD566C00A80B83 /* UIView+SKExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6A026CD566C00A80B83 /* UIView+SKExtensions.swift */; };
|
||||
F556F6A426CD5A9000A80B83 /* CALayer+Animations.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6A326CD5A9000A80B83 /* CALayer+Animations.swift */; };
|
||||
F556F6A526CD5A9000A80B83 /* CALayer+Animations.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6A326CD5A9000A80B83 /* CALayer+Animations.swift */; };
|
||||
F556F6A726CD5B0400A80B83 /* CALayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6A626CD5B0400A80B83 /* CALayer+Extensions.swift */; };
|
||||
@@ -90,9 +95,8 @@
|
||||
F556F6DD26CE33CE00A80B83 /* UIView+Swizzling.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6DC26CE33CE00A80B83 /* UIView+Swizzling.swift */; };
|
||||
F556F6E026CE367600A80B83 /* UIView+SkeletonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6DF26CE367600A80B83 /* UIView+SkeletonView.swift */; };
|
||||
F556F6E126CE367600A80B83 /* UIView+SkeletonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6DF26CE367600A80B83 /* UIView+SkeletonView.swift */; };
|
||||
F556F6F226CE818B00A80B83 /* UIView+Flags.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6F126CE818B00A80B83 /* UIView+Flags.swift */; };
|
||||
F556F6F326CE818B00A80B83 /* UIView+Flags.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6F126CE818B00A80B83 /* UIView+Flags.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 */; };
|
||||
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 */; };
|
||||
@@ -143,6 +147,8 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
F53D731726D399E100249D46 /* SkeletonTreeNode+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SkeletonTreeNode+Extensions.swift"; sourceTree = "<group>"; };
|
||||
F53D731A26D3A35100249D46 /* SkeletonExtended.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonExtended.swift; sourceTree = "<group>"; };
|
||||
F556F51026CD1B7900A80B83 /* SkeletonView.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = SkeletonView.podspec; sourceTree = "<group>"; };
|
||||
F556F51126CD1B8000A80B83 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
|
||||
F556F51426CD1BFF00A80B83 /* README_zh.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README_zh.md; path = Translations/README_zh.md; sourceTree = "<group>"; };
|
||||
@@ -181,7 +187,7 @@
|
||||
F556F69126CD506C00A80B83 /* Deprecated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Deprecated.swift; sourceTree = "<group>"; };
|
||||
F556F69426CD509E00A80B83 /* Notification+SkeletonFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+SkeletonFlow.swift"; sourceTree = "<group>"; };
|
||||
F556F69D26CD553B00A80B83 /* UIView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extensions.swift"; sourceTree = "<group>"; };
|
||||
F556F6A026CD566C00A80B83 /* UIView+Debug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Debug.swift"; sourceTree = "<group>"; };
|
||||
F556F6A026CD566C00A80B83 /* UIView+SKExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+SKExtensions.swift"; sourceTree = "<group>"; };
|
||||
F556F6A326CD5A9000A80B83 /* CALayer+Animations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+Animations.swift"; sourceTree = "<group>"; };
|
||||
F556F6A626CD5B0400A80B83 /* CALayer+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+Extensions.swift"; sourceTree = "<group>"; };
|
||||
F556F6AA26CD5C4900A80B83 /* SkeletonMultilinesLayerConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonMultilinesLayerConfig.swift; sourceTree = "<group>"; };
|
||||
@@ -198,7 +204,7 @@
|
||||
F556F6D826CE315A00A80B83 /* UICollectionView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionView+Extensions.swift"; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
F556F6F126CE818B00A80B83 /* UIView+Flags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Flags.swift"; sourceTree = "<group>"; };
|
||||
F556F70726D38F3100A80B83 /* SkeletonTreeNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonTreeNode.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>"; };
|
||||
@@ -356,6 +362,7 @@
|
||||
F556F68C26CD4EB400A80B83 /* FoundationExtensions */,
|
||||
F556F6B726CE25B100A80B83 /* Models */,
|
||||
F556F64F26CD2DFD00A80B83 /* SkeletonView.swift */,
|
||||
F53D731A26D3A35100249D46 /* SkeletonExtended.swift */,
|
||||
F556F68526CD49E900A80B83 /* UIKitExtensions */,
|
||||
);
|
||||
path = API;
|
||||
@@ -364,6 +371,7 @@
|
||||
F556F64B26CD2CD600A80B83 /* Internal */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F556F70626D38E8300A80B83 /* SkeletonTree */,
|
||||
F556F6D326CE2F3700A80B83 /* Collections */,
|
||||
F556F65226CD2E0A00A80B83 /* Debug */,
|
||||
F556F67E26CD476300A80B83 /* FoundationExtensions */,
|
||||
@@ -411,6 +419,7 @@
|
||||
F556F6DF26CE367600A80B83 /* UIView+SkeletonView.swift */,
|
||||
F556F6DC26CE33CE00A80B83 /* UIView+Swizzling.swift */,
|
||||
OBJ_61 /* UIView+Transitions.swift */,
|
||||
F53D731726D399E100249D46 /* SkeletonTreeNode+Extensions.swift */,
|
||||
);
|
||||
path = UIKitExtensions;
|
||||
sourceTree = "<group>";
|
||||
@@ -431,11 +440,10 @@
|
||||
children = (
|
||||
F556F6A326CD5A9000A80B83 /* CALayer+Animations.swift */,
|
||||
F556F68626CD49F900A80B83 /* UIView+IBInspectable.swift */,
|
||||
F556F6A026CD566C00A80B83 /* UIView+Debug.swift */,
|
||||
F556F6A026CD566C00A80B83 /* UIView+SKExtensions.swift */,
|
||||
F556F6C526CE2A2100A80B83 /* UILabel+IBInspectable.swift */,
|
||||
F556F6C826CE2A4A00A80B83 /* UITextView+IBInspectable.swift */,
|
||||
F556F6D826CE315A00A80B83 /* UICollectionView+Extensions.swift */,
|
||||
F556F6F126CE818B00A80B83 /* UIView+Flags.swift */,
|
||||
);
|
||||
path = UIKitExtensions;
|
||||
sourceTree = "<group>";
|
||||
@@ -544,6 +552,14 @@
|
||||
path = Appearance;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F556F70626D38E8300A80B83 /* SkeletonTree */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F556F70726D38F3100A80B83 /* SkeletonTreeNode.swift */,
|
||||
);
|
||||
path = SkeletonTree;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
OBJ_15 /* CollectionViews */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -771,6 +787,7 @@
|
||||
F556F6D026CE2AB800A80B83 /* SkeletonTextNode.swift in Sources */,
|
||||
F556F65126CD2DFD00A80B83 /* SkeletonView.swift in Sources */,
|
||||
F556F56826CD1F3900A80B83 /* SkeletonMultilineLayerBuilder.swift in Sources */,
|
||||
F53D731926D399E100249D46 /* SkeletonTreeNode+Extensions.swift in Sources */,
|
||||
F556F56926CD1F3900A80B83 /* CollectionSkeleton.swift in Sources */,
|
||||
F556F56A26CD1F3900A80B83 /* SkeletonCollectionViewProtocols.swift in Sources */,
|
||||
F556F6C026CE277F00A80B83 /* PrepareViewForSkeleton.swift in Sources */,
|
||||
@@ -794,6 +811,7 @@
|
||||
F556F57E26CD1F3900A80B83 /* AssociationPolicy.swift in Sources */,
|
||||
F556F6B026CE244100A80B83 /* DispatchQueue+Extensions.swift in Sources */,
|
||||
F556F58026CD1F3900A80B83 /* Recursive.swift in Sources */,
|
||||
F53D731C26D3A35100249D46 /* SkeletonExtended.swift in Sources */,
|
||||
F556F6F626CE876300A80B83 /* UIView+Swizzling.swift in Sources */,
|
||||
F556F58126CD1F3900A80B83 /* Swizzling.swift in Sources */,
|
||||
F556F6B626CE258300A80B83 /* GradientDirection+Animations.swift in Sources */,
|
||||
@@ -805,14 +823,14 @@
|
||||
F556F58926CD1F3900A80B83 /* SkeletonFlowHandler.swift in Sources */,
|
||||
F556F58A26CD1F3900A80B83 /* SkeletonGradient.swift in Sources */,
|
||||
F556F6C326CE27FD00A80B83 /* SkeletonType.swift in Sources */,
|
||||
F556F6F326CE818B00A80B83 /* UIView+Flags.swift in Sources */,
|
||||
F556F58B26CD1F3900A80B83 /* SkeletonLayer.swift in Sources */,
|
||||
F556F6A226CD566C00A80B83 /* UIView+Debug.swift in Sources */,
|
||||
F556F6A226CD566C00A80B83 /* UIView+SKExtensions.swift in Sources */,
|
||||
F556F69326CD506C00A80B83 /* Deprecated.swift in Sources */,
|
||||
F556F6BA26CE262700A80B83 /* GradientDirection.swift in Sources */,
|
||||
F556F58D26CD1F3900A80B83 /* SubviewsSkeletonables.swift in Sources */,
|
||||
F556F58E26CD1F3900A80B83 /* SkeletonTransitionStyle.swift in Sources */,
|
||||
F556F6AC26CD5C4900A80B83 /* SkeletonMultilinesLayerConfig.swift in Sources */,
|
||||
F53D731F26D3AC4000249D46 /* SkeletonTreeNode.swift in Sources */,
|
||||
F556F68126CD47CF00A80B83 /* ProcessInfo+Extensions.swift in Sources */,
|
||||
F556F68826CD49F900A80B83 /* UIView+IBInspectable.swift in Sources */,
|
||||
F556F6C726CE2A2100A80B83 /* UILabel+IBInspectable.swift in Sources */,
|
||||
@@ -849,7 +867,7 @@
|
||||
F556F6CF26CE2AB800A80B83 /* SkeletonTextNode.swift in Sources */,
|
||||
F556F65026CD2DFD00A80B83 /* SkeletonView.swift in Sources */,
|
||||
OBJ_88 /* SkeletonMultilineLayerBuilder.swift in Sources */,
|
||||
F556F6F226CE818B00A80B83 /* UIView+Flags.swift in Sources */,
|
||||
F53D731B26D3A35100249D46 /* SkeletonExtended.swift in Sources */,
|
||||
OBJ_89 /* CollectionSkeleton.swift in Sources */,
|
||||
OBJ_90 /* SkeletonCollectionViewProtocols.swift in Sources */,
|
||||
F556F6BF26CE277F00A80B83 /* PrepareViewForSkeleton.swift in Sources */,
|
||||
@@ -872,7 +890,9 @@
|
||||
OBJ_109 /* UIView+AppLifecycleNotifications.swift in Sources */,
|
||||
OBJ_110 /* AssociationPolicy.swift in Sources */,
|
||||
F556F6AF26CE244100A80B83 /* DispatchQueue+Extensions.swift in Sources */,
|
||||
F53D731826D399E100249D46 /* SkeletonTreeNode+Extensions.swift in Sources */,
|
||||
OBJ_112 /* Recursive.swift in Sources */,
|
||||
F556F70826D38F3100A80B83 /* SkeletonTreeNode.swift in Sources */,
|
||||
OBJ_113 /* Swizzling.swift in Sources */,
|
||||
F556F6B526CE258300A80B83 /* GradientDirection+Animations.swift in Sources */,
|
||||
F556F69E26CD553B00A80B83 /* UIView+Extensions.swift in Sources */,
|
||||
@@ -885,7 +905,7 @@
|
||||
F556F6C226CE27FD00A80B83 /* SkeletonType.swift in Sources */,
|
||||
OBJ_123 /* SkeletonLayer.swift in Sources */,
|
||||
F556F6E026CE367600A80B83 /* UIView+SkeletonView.swift in Sources */,
|
||||
F556F6A126CD566C00A80B83 /* UIView+Debug.swift in Sources */,
|
||||
F556F6A126CD566C00A80B83 /* UIView+SKExtensions.swift in Sources */,
|
||||
F556F69226CD506C00A80B83 /* Deprecated.swift in Sources */,
|
||||
F556F6B926CE262700A80B83 /* GradientDirection.swift in Sources */,
|
||||
OBJ_125 /* SubviewsSkeletonables.swift in Sources */,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
//
|
||||
// Created by Juanpe Catalán on 18/8/21.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public extension Notification.Name {
|
||||
|
||||
@@ -34,3 +34,17 @@ public extension Notification.Name {
|
||||
static let didHideSkeletons = Notification.Name.skeletonDidDisappearNotification
|
||||
|
||||
}
|
||||
|
||||
public extension UIView {
|
||||
|
||||
@available(*, deprecated, renamed: "sk.treeNodesDescription")
|
||||
var skeletonDescription: String {
|
||||
sk.skeletonTreeDescription
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "sk.isSkeletonActive")
|
||||
var isSkeletonActive: Bool {
|
||||
sk.isSkeletonActive
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// SkeletonExtended.swift
|
||||
//
|
||||
// Created by Juanpe Catalán on 23/8/21.
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Type that acts as a generic extension point for all `SkeletonViewExtended` types.
|
||||
public struct SkeletonViewExtension<ExtendedType> {
|
||||
/// Stores the type or meta-type of any extended type.
|
||||
public private(set) var type: ExtendedType
|
||||
|
||||
/// Create an instance from the provided value.
|
||||
///
|
||||
/// - Parameter type: Instance being extended.
|
||||
public init(_ type: ExtendedType) {
|
||||
self.type = type
|
||||
}
|
||||
}
|
||||
|
||||
/// Protocol describing the `sk` extension points for SkeletonView extended types.
|
||||
public protocol SkeletonViewExtended {
|
||||
/// Type being extended.
|
||||
associatedtype ExtendedType
|
||||
|
||||
/// Instance SkeletonView extension point.
|
||||
var sk: SkeletonViewExtension<ExtendedType> { get set }
|
||||
}
|
||||
|
||||
extension SkeletonViewExtended {
|
||||
/// Instance SkeletonView extension point.
|
||||
public var sk: SkeletonViewExtension<Self> {
|
||||
get { SkeletonViewExtension(self) }
|
||||
// swiftlint:disable:next unused_setter_value
|
||||
set {}
|
||||
}
|
||||
}
|
||||
@@ -33,4 +33,10 @@ public extension UILabel {
|
||||
set { multilineSpacing = newValue }
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var useFontLineHeight: Bool {
|
||||
get { usesTextHeightForLines }
|
||||
set { usesTextHeightForLines = newValue }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,4 +33,10 @@ public extension UITextView {
|
||||
set { multilineSpacing = newValue }
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var useFontLineHeight: Bool {
|
||||
get { usesTextHeightForLines }
|
||||
set { usesTextHeightForLines = newValue }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// UIView+Debug.swift
|
||||
//
|
||||
// Created by Juanpe Catalán on 18/8/21.
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIView {
|
||||
|
||||
/// Returns a string that describes the hierarchy of the skeleton, indicating
|
||||
/// whether the receiver is skeletonable and all skeletonable children.
|
||||
var skeletonDescription: String {
|
||||
var description = "<\(type(of: self)): \(Unmanaged.passUnretained(self).toOpaque())"
|
||||
let subSkeletons = subviewsSkeletonables
|
||||
|
||||
if !subSkeletons.isEmpty {
|
||||
description += " | (\(subSkeletons.count)) subSkeletons"
|
||||
}
|
||||
|
||||
if isSkeletonable {
|
||||
description += " | ☠️ "
|
||||
}
|
||||
|
||||
return description + ">"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// UIView+Flags.swift
|
||||
//
|
||||
// Created by Juanpe Catalán on 19/8/21.
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIView {
|
||||
|
||||
var isSkeletonActive: Bool {
|
||||
return _status == .on || subviewsSkeletonables.contains(where: { $0.isSkeletonActive })
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// UIView+SKExtensions.swift
|
||||
//
|
||||
// Created by Juanpe Catalán on 18/8/21.
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension SkeletonViewExtension where ExtendedType: UIView {
|
||||
|
||||
/// Returns a string that describes the hierarchy of the skeleton, indicating
|
||||
/// whether the receiver is skeletonable and all skeletonable children.
|
||||
var skeletonTreeDescription: String {
|
||||
guard let theJSONData = try? JSONSerialization.data(withJSONObject: treeNode.dictionaryRepresentation, options: [.prettyPrinted]) else {
|
||||
skeletonLog("Skeleton tree generation has failed!")
|
||||
return ""
|
||||
}
|
||||
|
||||
return String(data: theJSONData, encoding: .utf8)!
|
||||
}
|
||||
|
||||
var isSkeletonActive: Bool {
|
||||
type._status == .on || type.subviewsSkeletonables.contains(where: { $0.sk.isSkeletonActive })
|
||||
}
|
||||
|
||||
}
|
||||
@@ -114,7 +114,7 @@ extension SkeletonCollectionDataSource: UICollectionViewDataSource {
|
||||
|
||||
extension SkeletonCollectionDataSource {
|
||||
private func skeletonViewIfContainerSkeletonIsActive(container: UIView, view: UIView) {
|
||||
guard container.isSkeletonActive,
|
||||
guard container.sk.isSkeletonActive,
|
||||
let skeletonConfig = container._currentSkeletonConfig else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ extension SkeletonCollectionDelegate: UICollectionViewDelegate { }
|
||||
|
||||
extension SkeletonCollectionDelegate {
|
||||
private func skeletonViewIfContainerSkeletonIsActive(container: UIView, view: UIView) {
|
||||
guard container.isSkeletonActive,
|
||||
guard container.sk.isSkeletonActive,
|
||||
let skeletonConfig = container._currentSkeletonConfig else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -25,10 +25,6 @@ extension Dictionary {
|
||||
}
|
||||
}
|
||||
|
||||
func printSkeletonHierarchy(in view: UIView) {
|
||||
skeletonLog(view.skeletonHierarchy())
|
||||
}
|
||||
|
||||
func skeletonLog(_ message: String) {
|
||||
#if DEBUG
|
||||
if ProcessInfo.processInfo.environment[.debugMode] != nil {
|
||||
@@ -36,17 +32,3 @@ func skeletonLog(_ message: String) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
func skeletonHierarchy(indentationLevel level: Int = 0) -> String {
|
||||
var description = level == 0 ? "\n ⬇⬇ ☠️ Root view hierarchy with Skeletons ⬇⬇ \n" : ""
|
||||
description += "\(level == 0 ? "\n" : 3.whitespaces) \(skeletonDescription) \n"
|
||||
subviewsToSkeleton.forEach {
|
||||
description += (level + 2).whitespaces
|
||||
description += $0.skeletonHierarchy(indentationLevel: level + 1)
|
||||
}
|
||||
return description
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,27 +22,36 @@ protocol SkeletonTextNode {
|
||||
var multilineCornerRadius: Int { get }
|
||||
var multilineSpacing: CGFloat { get }
|
||||
var paddingInsets: UIEdgeInsets { get }
|
||||
|
||||
var usesTextHeightForLines: Bool { get }
|
||||
}
|
||||
|
||||
enum SkeletonTextNodeAssociatedKeys {
|
||||
|
||||
static var lastLineFillingPercent = "lastLineFillingPercent"
|
||||
static var multilineCornerRadius = "multilineCornerRadius"
|
||||
static var multilineSpacing = "multilineSpacing"
|
||||
static var paddingInsets = "paddingInsets"
|
||||
static var backupHeightConstraints = "backupHeightConstraints"
|
||||
static var usesTextHeightForLines = "usesTextHeightForLines"
|
||||
|
||||
}
|
||||
|
||||
extension UILabel: SkeletonTextNode {
|
||||
|
||||
var lineHeight: CGFloat {
|
||||
if let fontLineHeight = font?.lineHeight {
|
||||
if let heightConstraints = backupHeightConstraints.first?.constant {
|
||||
return (fontLineHeight > heightConstraints) ? heightConstraints : fontLineHeight
|
||||
}
|
||||
return fontLineHeight
|
||||
let constraintsLineHeight = backupHeightConstraints.first?.constant ?? SkeletonAppearance.default.multilineHeight
|
||||
|
||||
if useFontLineHeight,
|
||||
let fontLineHeight = font?.lineHeight {
|
||||
return fontLineHeight > constraintsLineHeight ? constraintsLineHeight : fontLineHeight
|
||||
} else {
|
||||
return constraintsLineHeight
|
||||
}
|
||||
return SkeletonAppearance.default.multilineHeight
|
||||
}
|
||||
|
||||
var usesTextHeightForLines: Bool {
|
||||
get { return ao_get(pkey: &SkeletonTextNodeAssociatedKeys.usesTextHeightForLines) as? Bool ?? true }
|
||||
set { ao_set(newValue, pkey: &SkeletonTextNodeAssociatedKeys.usesTextHeightForLines) }
|
||||
}
|
||||
|
||||
var lastLineFillingPercent: Int {
|
||||
@@ -75,15 +84,19 @@ extension UILabel: SkeletonTextNode {
|
||||
extension UITextView: SkeletonTextNode {
|
||||
|
||||
var lineHeight: CGFloat {
|
||||
if let fontLineHeight = font?.lineHeight {
|
||||
if let heightConstraints = heightConstraints.first?.constant {
|
||||
return (fontLineHeight > heightConstraints) ? heightConstraints : fontLineHeight
|
||||
}
|
||||
|
||||
return fontLineHeight
|
||||
}
|
||||
let constraintsLineHeight = heightConstraints.first?.constant ?? SkeletonAppearance.default.multilineHeight
|
||||
|
||||
return SkeletonAppearance.default.multilineHeight
|
||||
if useFontLineHeight,
|
||||
let fontLineHeight = font?.lineHeight {
|
||||
return fontLineHeight > constraintsLineHeight ? constraintsLineHeight : fontLineHeight
|
||||
} else {
|
||||
return constraintsLineHeight
|
||||
}
|
||||
}
|
||||
|
||||
var usesTextHeightForLines: Bool {
|
||||
get { return ao_get(pkey: &SkeletonTextNodeAssociatedKeys.usesTextHeightForLines) as? Bool ?? true }
|
||||
set { ao_set(newValue, pkey: &SkeletonTextNodeAssociatedKeys.usesTextHeightForLines) }
|
||||
}
|
||||
|
||||
var numberOfLines: Int {
|
||||
|
||||
@@ -20,7 +20,7 @@ class SkeletonFlowHandler: SkeletonFlowDelegate {
|
||||
}
|
||||
|
||||
func didShowSkeletons(rootView: UIView) {
|
||||
printSkeletonHierarchy(in: rootView)
|
||||
skeletonLog(rootView.sk.skeletonTreeDescription)
|
||||
NotificationCenter.default.post(name: .skeletonWillAppearNotification, object: rootView, userInfo: nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// SkeletonTreeNode.swift
|
||||
//
|
||||
// Created by Juanpe Catalán on 23/8/21.
|
||||
|
||||
import UIKit
|
||||
|
||||
public struct SkeletonTreeNode<Base> {
|
||||
/// Base object to extend.
|
||||
let base: Base
|
||||
|
||||
/// Creates extensions with base object.
|
||||
///
|
||||
/// - parameter base: Base object.
|
||||
init(_ base: Base) {
|
||||
self.base = base
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// SkeletonTreeNode+Extensions.swift
|
||||
//
|
||||
// Created by Juanpe Catalán on 23/8/21.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIView: SkeletonViewExtended { }
|
||||
|
||||
extension SkeletonTreeNode where Base: UIView {
|
||||
|
||||
var children: [SkeletonTreeNode<UIView>] {
|
||||
base.subviewsSkeletonables.map { $0.sk.treeNode }
|
||||
}
|
||||
|
||||
var parent: SkeletonTreeNode<UIView>? {
|
||||
base.superview?.sk.treeNode
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Debug
|
||||
|
||||
extension SkeletonTreeNode where Base: UIView {
|
||||
|
||||
var dictionaryRepresentation: [String: Any] {
|
||||
let skeletonableChildren = children
|
||||
|
||||
var nodeInfo: [String: Any] = [
|
||||
"type": "\(type(of: base))",
|
||||
"reference": "\(Unmanaged.passUnretained(base).toOpaque())",
|
||||
"isSkeletonable": base.isSkeletonable
|
||||
]
|
||||
|
||||
if !skeletonableChildren.isEmpty {
|
||||
nodeInfo["children"] = skeletonableChildren.map { $0.dictionaryRepresentation }
|
||||
}
|
||||
|
||||
return nodeInfo
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -38,7 +38,7 @@ extension UIView {
|
||||
}
|
||||
|
||||
@objc func appDidEnterBackground() {
|
||||
UserDefaults.standard.set((isSkeletonActive && _isSkeletonAnimated), forKey: Constants.needAnimatedSkeletonKey)
|
||||
UserDefaults.standard.set((sk.isSkeletonActive && _isSkeletonAnimated), forKey: Constants.needAnimatedSkeletonKey)
|
||||
}
|
||||
|
||||
@objc func willTerminateNotification() {
|
||||
|
||||
@@ -13,6 +13,14 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
extension SkeletonViewExtension where ExtendedType: UIView {
|
||||
|
||||
var treeNode: SkeletonTreeNode<ExtendedType> {
|
||||
SkeletonTreeNode<ExtendedType>(self.type)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
/// Flags
|
||||
@@ -159,7 +167,7 @@ extension UIView {
|
||||
}
|
||||
|
||||
func removeSkeletonLayer() {
|
||||
guard isSkeletonActive,
|
||||
guard sk.isSkeletonActive,
|
||||
let skeletonLayer = _skeletonLayer,
|
||||
let transitionStyle = _currentSkeletonConfig?.transition else { return }
|
||||
skeletonLayer.stopAnimation()
|
||||
|
||||
@@ -30,7 +30,7 @@ extension UIView {
|
||||
|
||||
func recursiveLayoutSkeletonIfNeeded(root: UIView? = nil) {
|
||||
subviewsSkeletonables.recursiveSearch(leafBlock: {
|
||||
guard isSkeletonable, isSkeletonActive else { return }
|
||||
guard isSkeletonable, sk.isSkeletonActive else { return }
|
||||
layoutSkeletonLayerIfNeeded()
|
||||
if let config = _currentSkeletonConfig, config.animated, !_isSkeletonAnimated {
|
||||
startSkeletonAnimation(config.animation)
|
||||
@@ -45,7 +45,7 @@ extension UIView {
|
||||
}
|
||||
|
||||
func recursiveHideSkeleton(reloadDataAfter reload: Bool, transition: SkeletonTransitionStyle, root: UIView? = nil) {
|
||||
guard isSkeletonActive else { return }
|
||||
guard sk.isSkeletonActive else { return }
|
||||
if isHiddenWhenSkeletonIsActive {
|
||||
isHidden = false
|
||||
}
|
||||
@@ -70,7 +70,7 @@ extension UIView {
|
||||
private extension UIView {
|
||||
|
||||
func showSkeletonIfNotActive(skeletonConfig config: SkeletonConfig) {
|
||||
guard !isSkeletonActive else { return }
|
||||
guard !sk.isSkeletonActive else { return }
|
||||
saveViewState()
|
||||
|
||||
prepareViewForSkeleton()
|
||||
@@ -81,7 +81,7 @@ private extension UIView {
|
||||
if isHiddenWhenSkeletonIsActive {
|
||||
isHidden = true
|
||||
}
|
||||
guard isSkeletonable && !isSkeletonActive else { return }
|
||||
guard isSkeletonable && !sk.isSkeletonActive else { return }
|
||||
_currentSkeletonConfig = config
|
||||
swizzleLayoutSubviews()
|
||||
swizzleTraitCollectionDidChange()
|
||||
@@ -98,7 +98,7 @@ private extension UIView {
|
||||
}
|
||||
|
||||
func recursiveUpdateSkeleton(skeletonConfig config: SkeletonConfig, root: UIView? = nil) {
|
||||
guard isSkeletonActive else { return }
|
||||
guard sk.isSkeletonActive else { return }
|
||||
_currentSkeletonConfig = config
|
||||
updateDummyDataSourceIfNeeded()
|
||||
subviewsSkeletonables.recursiveSearch(leafBlock: {
|
||||
|
||||
@@ -18,13 +18,13 @@ extension UIView {
|
||||
@objc func skeletonLayoutSubviews() {
|
||||
guard Thread.isMainThread else { return }
|
||||
skeletonLayoutSubviews()
|
||||
guard isSkeletonActive else { return }
|
||||
guard sk.isSkeletonActive else { return }
|
||||
layoutSkeletonIfNeeded()
|
||||
}
|
||||
|
||||
@objc func skeletonTraitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
skeletonTraitCollectionDidChange(previousTraitCollection)
|
||||
guard isSkeletonable, isSkeletonActive, let config = _currentSkeletonConfig else { return }
|
||||
guard isSkeletonable, sk.isSkeletonActive, let config = _currentSkeletonConfig else { return }
|
||||
updateSkeleton(skeletonConfig: config)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user