Compare commits

..

19 Commits

Author SHA1 Message Date
Sharma Elanthiraiyan ff1a7e299b Vertically center align UILabel's skeleton. (#456)
- Introduce `shouldCenterTextVertically`  in `SkeletonTextNode` to center align UILabels and keep UITextViews unaltered.
- Shift down CALayers after assigning frames in `updateMultilinesLayers` function in `CALayer+Extensions.swift`
2021-10-21 16:47:42 +02:00
Juanpe Catalán 4994907234 Update README.md 2021-10-20 09:39:41 +02:00
Juanpe 17fb1b9950 Bump version 1.25.1 2021-09-14 05:58:22 +00:00
Juanpe Catalán 138dc8bf82 Update CD.yml 2021-09-14 07:55:33 +02:00
Juanpe Catalán 0b308f5ef5 Update release.yml 2021-09-14 07:52:13 +02:00
Sharma Elanthiraiyan af94b7e30b Global setter for useFontLineHeight (#454)
- Add useFontLineHeight to SkeletonViewAppearance.
- Update README file to include useFontLineHeight under SkeletonAppearance.
2021-09-14 07:14:50 +02:00
Juanpe 14c138ec3e Bump version 1.25.0 2021-09-09 07:46:08 +00:00
Sharma Elanthiraiyan 2691572392 Apply lastLineFillPercent for single line views as well. (#449)
* Apply lastLineFillPercent for single line views as well.
Fixes #430

* Update readme to include single_lastline.png

* Remove singleline_lastline.png asset added to demo last line in single line views.

* Simplify lastLineFillPercent related update in readme file.

* Fix grammar in readme.
2021-09-09 09:39:26 +02:00
Juanpe 1b9586e2d7 Bump version 1.24.4 2021-08-23 12:00:05 +00:00
Juanpe Catalán a819e69cee make skeletonPaddingInsets public again (#446) 2021-08-23 13:58:33 +02:00
Juanpe 88cd082d8e Bump version 1.24.3 2021-08-23 10:36:24 +00:00
Juanpe Catalán 1ab26cd869 Merge branch 'main' of https://github.com/Juanpe/SkeletonView 2021-08-23 12:29:16 +02:00
Juanpe Catalán 8e369c9df2 fix build fails 2021-08-23 12:29:01 +02:00
Juanpe 5fc720d3ab Bump version 1.24.2 2021-08-23 10:24:52 +00:00
Juanpe Catalán 34ce9365f6 Update main.yml 2021-08-23 12:22:06 +02:00
Juanpe Catalán 3708c0da7f Improve debug description and create SkeletonExtensions (#444)
* create sk extension and improve debug

* update README

* fix readme

* Update README.md

* include SkeletonTreeNode
2021-08-23 12:20:47 +02:00
Juanpe 9103c14cc5 Bump version 1.24.0 2021-08-19 15:48:14 +00:00
Juanpe Catalán 5ce17f7a40 Add new property useFontLineHeight to disable the auto-adjustment (#443) 2021-08-19 17:46:59 +02:00
Juanpe dfc2d60daa Bump version 1.23.1 2021-08-19 12:48:19 +00:00
31 changed files with 364 additions and 136 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
name: CD
on:
pull_request:
pull_request_target:
branches: [main]
types: [closed]
@@ -61,4 +61,4 @@ jobs:
🎉 New release ${{ steps.publish_release.outputs.tag_name }} is out 🚀
Check out all the changes here:
${{ steps.publish_release.outputs.html_url }}
${{ steps.publish_release.outputs.html_url }}
+1
View File
@@ -3,6 +3,7 @@ name: CI
on:
pull_request:
branches: [main]
workflow_dispatch:
jobs:
build:
+13 -1
View File
@@ -35,4 +35,16 @@ jobs:
run: |
set -eo pipefail
pod lib lint --allow-warnings
pod trunk push --allow-warnings
pod trunk push --allow-warnings
- name: Tweet the release
uses: ethomson/send-tweet-action@v1
with:
consumer-key: ${{ secrets.TWITTER_CONSUMER_API_KEY }}
consumer-secret: ${{ secrets.TWITTER_CONSUMER_API_SECRET }}
access-token: ${{ secrets.TWITTER_ACCESS_TOKEN }}
access-token-secret: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
status: |
🎉 New release ${{ steps.publish_release.outputs.tag_name }} is out 🚀
Check out all the changes here:
${{ steps.publish_release.outputs.html_url }}
@@ -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) {
+32 -14
View File
@@ -63,9 +63,9 @@ Enjoy it! 🙂
## 🎬 Guides
| [![](https://img.youtube.com/vi/75kgOhWsPNA/maxresdefault.jpg)](https://youtu.be/75kgOhWsPNA)|[![](https://img.youtube.com/vi/MVCiM_VdxVA/maxresdefault.jpg)](https://youtu.be/MVCiM_VdxVA)|[![](https://img.youtube.com/vi/Qq3Evspeea8/maxresdefault.jpg)](https://youtu.be/Qq3Evspeea8)|[![](https://img.youtube.com/vi/ZOoPtBwDRT0/maxresdefault.jpg)](https://youtu.be/ZOoPtBwDRT0)|[![](https://img.youtube.com/vi/Zx1Pg1gPfxA/maxresdefault.jpg)](https://www.youtube.com/watch?v=Zx1Pg1gPfxA)
|:---: | :---: |:---: | :---: | :---:
|[**SkeletonView Guides - Getting started**](https://youtu.be/75kgOhWsPNA)|[**How to Create Loading View with Skeleton View in Swift 5.2**](https://youtu.be/MVCiM_VdxVA) by iKh4ever Studio|[**Create Skeleton Loading View in App (Swift 5) - Xcode 11, 2020**](https://youtu.be/Qq3Evspeea8) by iOS Academy| [**Add An Elegant Loading Animation in Swift***](https://youtu.be/ZOoPtBwDRT0) by Gary Tokman| [**Cómo crear una ANIMACIÓN de CARGA de DATOS en iOS**](https://www.youtube.com/watch?v=Zx1Pg1gPfxA) by MoureDev
| [![](https://img.youtube.com/vi/75kgOhWsPNA/maxresdefault.jpg)](https://youtu.be/75kgOhWsPNA)|[![](https://img.youtube.com/vi/MVCiM_VdxVA/maxresdefault.jpg)](https://youtu.be/MVCiM_VdxVA)|[![](https://img.youtube.com/vi/Qq3Evspeea8/maxresdefault.jpg)](https://youtu.be/Qq3Evspeea8)|[![](https://img.youtube.com/vi/Zx1Pg1gPfxA/maxresdefault.jpg)](https://www.youtube.com/watch?v=Zx1Pg1gPfxA)
|:---: | :---: | :---: | :---:
|[**SkeletonView Guides - Getting started**](https://youtu.be/75kgOhWsPNA)|[**How to Create Loading View with Skeleton View in Swift 5.2**](https://youtu.be/MVCiM_VdxVA) by iKh4ever Studio|[**Create Skeleton Loading View in App (Swift 5) - Xcode 11, 2020**](https://youtu.be/Qq3Evspeea8) by iOS Academy| [**Cómo crear una ANIMACIÓN de CARGA de DATOS en iOS**](https://www.youtube.com/watch?v=Zx1Pg1gPfxA) by MoureDev
## 📲 Installation
@@ -268,7 +268,7 @@ You can set some properties for multilines elements.
| Property | Values | Default | Preview
| ------- | ------- |------- | -------
| **Filling percent** of the last line. | `0...100` | `70%` | ![](Assets/multiline_lastline.png)
| **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)
@@ -295,6 +295,8 @@ Default values:
- *default: `SkeletonGradient(baseColor: .skeletonDefault)`*
- **multilineHeight**: CGFloat
- *default: 15*
- **useFontLineHeight**: Bool
- *default: true*
- **multilineSpacing**: CGFloat
- *default: 10*
- **multilineLastLineFillPercent**: Int
@@ -315,6 +317,7 @@ You can also specifiy these line appearance properties on a per-label basis:
- **linesCornerRadius**: Int
- **skeletonLineSpacing**: CGFloat
- **skeletonPaddingInsets**: UIEdgeInsets
- **useFontLineHeight**: Bool
### 🎨 Custom colors
@@ -518,6 +521,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 +551,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:
![](Assets/debug_description.png)
Besides, you can activate the new **debug mode**. You just add the environment variable `SKELETON_DEBUG` and activate it.
@@ -555,11 +563,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 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SkeletonView"
s.version = "1.23.0"
s.version = "1.25.1"
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.
+44 -18
View File
@@ -7,6 +7,13 @@
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 */; };
F53D732326D3C3A800249D46 /* UILabel+SKExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F53D732226D3C3A800249D46 /* UILabel+SKExtensions.swift */; };
F53D732426D3C3A800249D46 /* UILabel+SKExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F53D732226D3C3A800249D46 /* UILabel+SKExtensions.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 +64,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 */; };
@@ -81,8 +88,8 @@
F556F6C726CE2A2100A80B83 /* UILabel+IBInspectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6C526CE2A2100A80B83 /* UILabel+IBInspectable.swift */; };
F556F6C926CE2A4A00A80B83 /* UITextView+IBInspectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6C826CE2A4A00A80B83 /* UITextView+IBInspectable.swift */; };
F556F6CA26CE2A4A00A80B83 /* UITextView+IBInspectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6C826CE2A4A00A80B83 /* UITextView+IBInspectable.swift */; };
F556F6CC26CE2A7400A80B83 /* UITextView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6CB26CE2A7400A80B83 /* UITextView+Extensions.swift */; };
F556F6CD26CE2A7400A80B83 /* UITextView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6CB26CE2A7400A80B83 /* UITextView+Extensions.swift */; };
F556F6CC26CE2A7400A80B83 /* UITextView+SKExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6CB26CE2A7400A80B83 /* UITextView+SKExtensions.swift */; };
F556F6CD26CE2A7400A80B83 /* UITextView+SKExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6CB26CE2A7400A80B83 /* UITextView+SKExtensions.swift */; };
F556F6CF26CE2AB800A80B83 /* SkeletonTextNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6CE26CE2AB800A80B83 /* SkeletonTextNode.swift */; };
F556F6D026CE2AB800A80B83 /* SkeletonTextNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6CE26CE2AB800A80B83 /* SkeletonTextNode.swift */; };
F556F6D926CE315A00A80B83 /* UICollectionView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F556F6D826CE315A00A80B83 /* UICollectionView+Extensions.swift */; };
@@ -90,9 +97,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 +149,9 @@
/* 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>"; };
F53D732226D3C3A800249D46 /* UILabel+SKExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+SKExtensions.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 +190,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>"; };
@@ -193,12 +202,12 @@
F556F6C126CE27FD00A80B83 /* SkeletonType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonType.swift; sourceTree = "<group>"; };
F556F6C526CE2A2100A80B83 /* UILabel+IBInspectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+IBInspectable.swift"; sourceTree = "<group>"; };
F556F6C826CE2A4A00A80B83 /* UITextView+IBInspectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+IBInspectable.swift"; sourceTree = "<group>"; };
F556F6CB26CE2A7400A80B83 /* UITextView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+Extensions.swift"; sourceTree = "<group>"; };
F556F6CB26CE2A7400A80B83 /* UITextView+SKExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+SKExtensions.swift"; sourceTree = "<group>"; };
F556F6CE26CE2AB800A80B83 /* SkeletonTextNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonTextNode.swift; sourceTree = "<group>"; };
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 +365,7 @@
F556F68C26CD4EB400A80B83 /* FoundationExtensions */,
F556F6B726CE25B100A80B83 /* Models */,
F556F64F26CD2DFD00A80B83 /* SkeletonView.swift */,
F53D731A26D3A35100249D46 /* SkeletonExtended.swift */,
F556F68526CD49E900A80B83 /* UIKitExtensions */,
);
path = API;
@@ -364,6 +374,7 @@
F556F64B26CD2CD600A80B83 /* Internal */ = {
isa = PBXGroup;
children = (
F556F70626D38E8300A80B83 /* SkeletonTree */,
F556F6D326CE2F3700A80B83 /* Collections */,
F556F65226CD2E0A00A80B83 /* Debug */,
F556F67E26CD476300A80B83 /* FoundationExtensions */,
@@ -403,7 +414,6 @@
F556F6BB26CE272600A80B83 /* UILabel+Extensions.swift */,
OBJ_25 /* UITableView+CollectionSkeleton.swift */,
OBJ_34 /* UITableView+Extensions.swift */,
F556F6CB26CE2A7400A80B83 /* UITextView+Extensions.swift */,
OBJ_39 /* UIView+AppLifecycleNotifications.swift */,
F556F68226CD48F700A80B83 /* UIView+AssociatedObjects.swift */,
OBJ_26 /* UIView+CollectionSkeleton.swift */,
@@ -411,6 +421,7 @@
F556F6DF26CE367600A80B83 /* UIView+SkeletonView.swift */,
F556F6DC26CE33CE00A80B83 /* UIView+Swizzling.swift */,
OBJ_61 /* UIView+Transitions.swift */,
F53D731726D399E100249D46 /* SkeletonTreeNode+Extensions.swift */,
);
path = UIKitExtensions;
sourceTree = "<group>";
@@ -431,11 +442,12 @@
children = (
F556F6A326CD5A9000A80B83 /* CALayer+Animations.swift */,
F556F68626CD49F900A80B83 /* UIView+IBInspectable.swift */,
F556F6A026CD566C00A80B83 /* UIView+Debug.swift */,
F556F6A026CD566C00A80B83 /* UIView+SKExtensions.swift */,
F556F6C526CE2A2100A80B83 /* UILabel+IBInspectable.swift */,
F53D732226D3C3A800249D46 /* UILabel+SKExtensions.swift */,
F556F6C826CE2A4A00A80B83 /* UITextView+IBInspectable.swift */,
F556F6D826CE315A00A80B83 /* UICollectionView+Extensions.swift */,
F556F6F126CE818B00A80B83 /* UIView+Flags.swift */,
F556F6CB26CE2A7400A80B83 /* UITextView+SKExtensions.swift */,
);
path = UIKitExtensions;
sourceTree = "<group>";
@@ -544,6 +556,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 +791,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 */,
@@ -780,7 +801,7 @@
F556F56E26CD1F3900A80B83 /* SkeletonCollectionDataSource.swift in Sources */,
F556F56F26CD1F3900A80B83 /* SkeletonCollectionDelegate.swift in Sources */,
F556F68426CD48F700A80B83 /* UIView+AssociatedObjects.swift in Sources */,
F556F6CD26CE2A7400A80B83 /* UITextView+Extensions.swift in Sources */,
F556F6CD26CE2A7400A80B83 /* UITextView+SKExtensions.swift in Sources */,
F556F57026CD1F3900A80B83 /* SkeletonTableViewProtocols.swift in Sources */,
F556F57126CD1F3900A80B83 /* UITableView+CollectionSkeleton.swift in Sources */,
F556F57226CD1F3900A80B83 /* UIView+CollectionSkeleton.swift in Sources */,
@@ -794,8 +815,10 @@
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 */,
F53D732426D3C3A800249D46 /* UILabel+SKExtensions.swift in Sources */,
F556F6B626CE258300A80B83 /* GradientDirection+Animations.swift in Sources */,
F556F69F26CD553B00A80B83 /* UIView+Extensions.swift in Sources */,
F556F58526CD1F3900A80B83 /* Recoverable.swift in Sources */,
@@ -805,14 +828,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 +872,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 */,
@@ -859,7 +882,7 @@
OBJ_94 /* SkeletonCollectionDataSource.swift in Sources */,
OBJ_95 /* SkeletonCollectionDelegate.swift in Sources */,
F556F68326CD48F700A80B83 /* UIView+AssociatedObjects.swift in Sources */,
F556F6CC26CE2A7400A80B83 /* UITextView+Extensions.swift in Sources */,
F556F6CC26CE2A7400A80B83 /* UITextView+SKExtensions.swift in Sources */,
OBJ_96 /* SkeletonTableViewProtocols.swift in Sources */,
OBJ_97 /* UITableView+CollectionSkeleton.swift in Sources */,
OBJ_98 /* UIView+CollectionSkeleton.swift in Sources */,
@@ -872,9 +895,12 @@
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 */,
F53D732326D3C3A800249D46 /* UILabel+SKExtensions.swift in Sources */,
F556F69E26CD553B00A80B83 /* UIView+Extensions.swift in Sources */,
OBJ_117 /* Recoverable.swift in Sources */,
OBJ_118 /* RecoverableViewState.swift in Sources */,
@@ -885,7 +911,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 */,
@@ -27,6 +27,8 @@ public class SkeletonViewAppearance {
public var multilineHeight: CGFloat = 15
public var useFontLineHeight: Bool = true
public var multilineSpacing: CGFloat = 10
public var multilineLastLineFillPercent: Int = 70
+15 -1
View File
@@ -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 }
}
}
@@ -7,16 +7,17 @@
//
// https://opensource.org/licenses/MIT
//
// UIView+Flags.swift
// UILabel+SKExtensions.swift
//
// Created by Juanpe Catalán on 19/8/21.
// Created by Juanpe Catalán on 23/8/21.
import UIKit
public extension UIView {
public extension UILabel {
var isSkeletonActive: Bool {
return _status == .on || subviewsSkeletonables.contains(where: { $0.isSkeletonActive })
var skeletonPaddingInsets: UIEdgeInsets {
get { return paddingInsets }
set { paddingInsets = newValue }
}
}
@@ -33,4 +33,10 @@ public extension UITextView {
set { multilineSpacing = newValue }
}
@IBInspectable
var useFontLineHeight: Bool {
get { usesTextHeightForLines }
set { usesTextHeightForLines = newValue }
}
}
@@ -7,13 +7,13 @@
//
// https://opensource.org/licenses/MIT
//
// UITextView+Extensions.swift
// UITextView+SKExtensions.swift
//
// Created by Juanpe Catalán on 19/8/21.
import UIKit
extension UITextView {
public extension UITextView {
var skeletonPaddingInsets: UIEdgeInsets {
get { return paddingInsets }
@@ -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 + ">"
}
}
@@ -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
}
}
@@ -67,7 +67,8 @@ struct SkeletonLayer {
multilineSpacing: textView.multilineSpacing,
paddingInsets: textView.paddingInsets,
alignment: textView.textAlignment,
isRTL: holder?.isRTL ?? false)
isRTL: holder?.isRTL ?? false,
shouldCenterVertically: textView.shouldCenterTextVertically)
maskLayer.addMultilinesLayers(for: config)
}
@@ -82,7 +83,8 @@ struct SkeletonLayer {
multilineSpacing: textView.multilineSpacing,
paddingInsets: textView.paddingInsets,
alignment: textView.textAlignment,
isRTL: holder?.isRTL ?? false)
isRTL: holder?.isRTL ?? false,
shouldCenterVertically: textView.shouldCenterTextVertically)
maskLayer.updateMultilinesLayers(for: config)
}
@@ -24,7 +24,8 @@ struct SkeletonMultilinesLayerConfig {
var paddingInsets: UIEdgeInsets
var alignment: NSTextAlignment
var isRTL: Bool
var shouldCenterVertically: Bool
/// Returns padding insets taking into account if the RTL is activated
var calculatedPaddingInsets: UIEdgeInsets {
UIEdgeInsets(top: paddingInsets.top,
@@ -22,27 +22,37 @@ protocol SkeletonTextNode {
var multilineCornerRadius: Int { get }
var multilineSpacing: CGFloat { get }
var paddingInsets: UIEdgeInsets { get }
var usesTextHeightForLines: Bool { get }
var shouldCenterTextVertically: 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 ?? SkeletonAppearance.default.useFontLineHeight }
set { ao_set(newValue, pkey: &SkeletonTextNodeAssociatedKeys.usesTextHeightForLines) }
}
var lastLineFillingPercent: Int {
@@ -70,20 +80,28 @@ extension UILabel: SkeletonTextNode {
set { ao_set(newValue, pkey: &SkeletonTextNodeAssociatedKeys.backupHeightConstraints) }
}
var shouldCenterTextVertically: Bool {
true
}
}
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 ?? SkeletonAppearance.default.useFontLineHeight }
set { ao_set(newValue, pkey: &SkeletonTextNodeAssociatedKeys.usesTextHeightForLines) }
}
var numberOfLines: Int {
@@ -116,4 +134,7 @@ extension UITextView: SkeletonTextNode {
set { ao_set(newValue, pkey: &SkeletonTextNodeAssociatedKeys.paddingInsets) }
}
var shouldCenterTextVertically: Bool {
false
}
}
@@ -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
}
}
@@ -108,7 +108,7 @@ private extension CALayer {
func calculatedWidthForLine(at index: Int, totalLines: Int, lastLineFillPercent: Int, paddingInsets: UIEdgeInsets) -> CGFloat {
var width = bounds.width - paddingInsets.left - paddingInsets.right
if index == totalLines - 1 && totalLines != 1 {
if index == totalLines - 1 {
width = width * CGFloat(lastLineFillPercent) / 100
}
return width
@@ -188,6 +188,19 @@ extension CALayer {
isRTL: config.isRTL
)
}
guard config.shouldCenterVertically,
let maxY = currentSkeletonSublayers.last?.frame.maxY else {
return
}
let verticallyCenterAlignedFrames = currentSkeletonSublayers.map { layer -> CGRect in
let moveDownBy = (bounds.height - (maxY + paddingInsets.top + paddingInsets.bottom)) / 2
return layer.frame.offsetBy(dx: 0, dy: moveDownBy)
}
for (index, layer) in currentSkeletonSublayers.enumerated() {
layer.frame = verticallyCenterAlignedFrames[index]
}
}
func updateLayerFrame(for index: Int, totalLines: Int, size: CGSize, multilineSpacing: CGFloat, paddingInsets: UIEdgeInsets, alignment: NSTextAlignment, isRTL: Bool) {
@@ -197,7 +210,7 @@ extension CALayer {
width: size.width,
height: size.height)
if index == totalLines - 1 && totalLines != 1 {
if index == totalLines - 1 {
frame = alignLayerFrame(newFrame, alignment: alignment, isRTL: isRTL)
} else {
frame = newFrame
@@ -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
}
}
@@ -15,11 +15,6 @@ import UIKit
extension UILabel {
var skeletonPaddingInsets: UIEdgeInsets {
get { return paddingInsets }
set { paddingInsets = newValue }
}
var desiredHeightBasedOnNumberOfLines: CGFloat {
let spaceNeededForEachLine = lineHeight * CGFloat(numberOfLines)
let spaceNeededForSpaces = skeletonLineSpacing * CGFloat(numberOfLines - 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)
}