Compare commits

...

18 Commits

Author SHA1 Message Date
Juanpe Catalán 2edd715a87 Fix skeleton flow notifications triggered multiple times (#476)
* Fix notification names

* Fix problem with multiple notifications
2022-01-07 16:55:19 +01:00
Juanpe f3cc8f0aba Bump version 1.26.0 2021-11-23 10:41:10 +00:00
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
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
30 changed files with 367 additions and 189 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 }}
+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 }}
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;
};
+36 -28
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
@@ -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. | `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,19 +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*
- **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:
@@ -310,11 +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
> **⚠️ DEPRECATED!**
>
> **useFontLineHeight** has been deprecated. You can use **textLineHeight** instead:
> ```swift
> SkeletonAppearance.default.textLineHeight = .relativeToFont
> ```
### 🎨 Custom colors
@@ -461,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.24.0"
s.version = "1.26.0"
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.
+18 -6
View File
@@ -12,6 +12,8 @@
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 */; };
@@ -86,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 */; };
@@ -97,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 */; };
@@ -149,6 +153,7 @@
/* 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>"; };
@@ -199,12 +204,13 @@
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>"; };
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>"; };
@@ -411,7 +417,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 */,
@@ -442,8 +447,10 @@
F556F68626CD49F900A80B83 /* UIView+IBInspectable.swift */,
F556F6A026CD566C00A80B83 /* UIView+SKExtensions.swift */,
F556F6C526CE2A2100A80B83 /* UILabel+IBInspectable.swift */,
F53D732226D3C3A800249D46 /* UILabel+SKExtensions.swift */,
F556F6C826CE2A4A00A80B83 /* UITextView+IBInspectable.swift */,
F556F6D826CE315A00A80B83 /* UICollectionView+Extensions.swift */,
F556F6CB26CE2A7400A80B83 /* UITextView+SKExtensions.swift */,
);
path = UIKitExtensions;
sourceTree = "<group>";
@@ -511,6 +518,7 @@
OBJ_55 /* SkeletonGradient.swift */,
F556F6B826CE262700A80B83 /* GradientDirection.swift */,
F556F6C126CE27FD00A80B83 /* SkeletonType.swift */,
F5C84883274BB6F000004D1A /* SkeletonTextLineHeight.swift */,
);
path = Models;
sourceTree = "<group>";
@@ -797,7 +805,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 */,
@@ -814,9 +822,11 @@
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 */,
F5C84885274BB6F000004D1A /* SkeletonTextLineHeight.swift in Sources */,
F556F58626CD1F3900A80B83 /* RecoverableViewState.swift in Sources */,
F556F58726CD1F3900A80B83 /* SkeletonAnimationBuilder.swift in Sources */,
F556F58826CD1F3900A80B83 /* SkeletonConfig.swift in Sources */,
@@ -877,7 +887,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 */,
@@ -895,9 +905,11 @@
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 */,
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,7 +26,9 @@ public class SkeletonViewAppearance {
public var gradient = SkeletonGradient(baseColor: .skeletonDefault)
public var multilineHeight: CGFloat = 15
public lazy var textLineHeight: SkeletonTextLineHeight = .fixed(SkeletonAppearance.default.multilineHeight)
public var multilineSpacing: CGFloat = 10
public var multilineLastLineFillPercent: Int = 70
@@ -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 }
}
}
@@ -0,0 +1,36 @@
//
// 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
//
// UILabel+SKExtensions.swift
//
// Created by Juanpe Catalán on 23/8/21.
import UIKit
public extension UILabel {
var skeletonPaddingInsets: UIEdgeInsets {
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 }
}
}
@@ -7,17 +7,30 @@
//
// 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 }
set { paddingInsets = newValue }
get {
paddingInsets
}
set {
paddingInsets = newValue
}
}
var skeletonTextLineHeight: SkeletonTextLineHeight {
get {
textLineHeight
}
set {
textLineHeight = newValue
}
}
}
@@ -51,13 +51,13 @@ extension SkeletonCollectionDataSource: UITableViewDataSource {
let fakeCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
originalTableViewDataSource?.collectionSkeletonView(tableView, prepareCellForSkeleton: fakeCell, at: indexPath)
skeletonViewIfContainerSkeletonIsActive(container: tableView, view: fakeCell)
skeletonizeViewIfContainerSkeletonIsActive(container: tableView, view: fakeCell)
return fakeCell
}
originalTableViewDataSource?.collectionSkeletonView(tableView, prepareCellForSkeleton: cell, at: indexPath)
skeletonViewIfContainerSkeletonIsActive(container: tableView, view: cell)
skeletonizeViewIfContainerSkeletonIsActive(container: tableView, view: cell)
return cell
}
}
@@ -88,13 +88,13 @@ extension SkeletonCollectionDataSource: UICollectionViewDataSource {
let fakeCell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath)
originalCollectionViewDataSource?.collectionSkeletonView(collectionView, prepareCellForSkeleton: fakeCell, at: indexPath)
skeletonViewIfContainerSkeletonIsActive(container: collectionView, view: fakeCell)
skeletonizeViewIfContainerSkeletonIsActive(container: collectionView, view: fakeCell)
return fakeCell
}
originalCollectionViewDataSource?.collectionSkeletonView(collectionView, prepareCellForSkeleton: cell, at: indexPath)
skeletonViewIfContainerSkeletonIsActive(container: collectionView, view: cell)
skeletonizeViewIfContainerSkeletonIsActive(container: collectionView, view: cell)
return cell
}
@@ -103,7 +103,7 @@ extension SkeletonCollectionDataSource: UICollectionViewDataSource {
at indexPath: IndexPath) -> UICollectionReusableView {
if let viewIdentifier = originalCollectionViewDataSource?.collectionSkeletonView(collectionView, supplementaryViewIdentifierOfKind: kind, at: indexPath) {
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: viewIdentifier, for: indexPath)
skeletonViewIfContainerSkeletonIsActive(container: collectionView, view: view)
skeletonizeViewIfContainerSkeletonIsActive(container: collectionView, view: view)
return view
}
@@ -113,12 +113,15 @@ extension SkeletonCollectionDataSource: UICollectionViewDataSource {
}
extension SkeletonCollectionDataSource {
private func skeletonViewIfContainerSkeletonIsActive(container: UIView, view: UIView) {
private func skeletonizeViewIfContainerSkeletonIsActive(container: UIView, view: UIView) {
guard container.sk.isSkeletonActive,
let skeletonConfig = container._currentSkeletonConfig else {
return
}
view.showSkeleton(skeletonConfig: skeletonConfig)
view.showSkeleton(
skeletonConfig: skeletonConfig,
notifyDelegate: false
)
}
}
@@ -9,17 +9,23 @@
import UIKit
class SkeletonCollectionDelegate: NSObject {
weak var originalTableViewDelegate: SkeletonTableViewDelegate?
weak var originalCollectionViewDelegate: SkeletonCollectionViewDelegate?
init(tableViewDelegate: SkeletonTableViewDelegate? = nil, collectionViewDelegate: SkeletonCollectionViewDelegate? = nil) {
init(
tableViewDelegate: SkeletonTableViewDelegate? = nil,
collectionViewDelegate: SkeletonCollectionViewDelegate? = nil
) {
self.originalTableViewDelegate = tableViewDelegate
self.originalCollectionViewDelegate = collectionViewDelegate
}
}
// MARK: - UITableViewDelegate
extension SkeletonCollectionDelegate: UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
headerOrFooterView(tableView, for: originalTableViewDelegate?.collectionSkeletonView(tableView, identifierForHeaderInSection: section))
}
@@ -42,24 +48,40 @@ extension SkeletonCollectionDelegate: UITableViewDelegate {
cell.hideSkeleton()
originalTableViewDelegate?.tableView?(tableView, didEndDisplaying: cell, forRowAt: indexPath)
}
private func headerOrFooterView(_ tableView: UITableView, for viewIdentifier: String? ) -> UIView? {
guard let viewIdentifier = viewIdentifier, let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: viewIdentifier) else { return nil }
skeletonViewIfContainerSkeletonIsActive(container: tableView, view: header)
return header
}
}
// MARK: - UICollectionViewDelegate
extension SkeletonCollectionDelegate: UICollectionViewDelegate { }
extension SkeletonCollectionDelegate {
private func skeletonViewIfContainerSkeletonIsActive(container: UIView, view: UIView) {
private extension SkeletonCollectionDelegate {
func skeletonizeViewIfContainerSkeletonIsActive(container: UIView, view: UIView) {
guard container.sk.isSkeletonActive,
let skeletonConfig = container._currentSkeletonConfig else {
let skeletonConfig = container._currentSkeletonConfig
else {
return
}
view.showSkeleton(skeletonConfig: skeletonConfig)
view.showSkeleton(
skeletonConfig: skeletonConfig,
notifyDelegate: false
)
}
func headerOrFooterView(_ tableView: UITableView, for viewIdentifier: String? ) -> UIView? {
guard let viewIdentifier = viewIdentifier,
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: viewIdentifier)
else {
return nil
}
skeletonizeViewIfContainerSkeletonIsActive(
container: tableView,
view: header
)
return header
}
}
@@ -60,14 +60,15 @@ 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,
multilineSpacing: textView.multilineSpacing,
paddingInsets: textView.paddingInsets,
alignment: textView.textAlignment,
isRTL: holder?.isRTL ?? false)
isRTL: holder?.isRTL ?? false,
shouldCenterVertically: textView.shouldCenterTextVertically)
maskLayer.addMultilinesLayers(for: config)
}
@@ -75,14 +76,15 @@ 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,
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,
@@ -15,14 +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 {
@@ -32,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 ?? true }
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 {
@@ -79,24 +86,37 @@ extension UILabel: SkeletonTextNode {
set { ao_set(newValue, pkey: &SkeletonTextNodeAssociatedKeys.backupHeightConstraints) }
}
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 ?? true }
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 {
@@ -129,4 +149,17 @@ extension UITextView: SkeletonTextNode {
set { ao_set(newValue, pkey: &SkeletonTextNodeAssociatedKeys.paddingInsets) }
}
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
}
}
}
@@ -21,15 +21,15 @@ class SkeletonFlowHandler: SkeletonFlowDelegate {
func didShowSkeletons(rootView: UIView) {
skeletonLog(rootView.sk.skeletonTreeDescription)
NotificationCenter.default.post(name: .skeletonWillAppearNotification, object: rootView, userInfo: nil)
NotificationCenter.default.post(name: .skeletonDidAppearNotification, object: rootView, userInfo: nil)
}
func willBeginUpdatingSkeletons(rootView: UIView) {
NotificationCenter.default.post(name: .skeletonWillAppearNotification, object: rootView, userInfo: nil)
NotificationCenter.default.post(name: .skeletonWillUpdateNotification, object: rootView, userInfo: nil)
}
func didUpdateSkeletons(rootView: UIView) {
NotificationCenter.default.post(name: .skeletonWillAppearNotification, object: rootView, userInfo: nil)
NotificationCenter.default.post(name: .skeletonDidUpdateNotification, object: rootView, userInfo: nil)
}
func willBeginLayingSkeletonsIfNeeded(rootView: UIView) {
@@ -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
@@ -32,7 +32,7 @@ extension SkeletonTreeNode where Base: UIView {
extension SkeletonTreeNode where Base: UIView {
var dictionaryRepresentation: [String: Any] {
let children = children
let skeletonableChildren = children
var nodeInfo: [String: Any] = [
"type": "\(type(of: base))",
@@ -40,8 +40,8 @@ extension SkeletonTreeNode where Base: UIView {
"isSkeletonable": base.isSkeletonable
]
if !children.isEmpty {
nodeInfo["children"] = children.map { $0.dictionaryRepresentation }
if !skeletonableChildren.isEmpty {
nodeInfo["children"] = skeletonableChildren.map { $0.dictionaryRepresentation }
}
return nodeInfo
@@ -15,13 +15,8 @@ import UIKit
extension UILabel {
var skeletonPaddingInsets: UIEdgeInsets {
get { return paddingInsets }
set { paddingInsets = newValue }
}
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
@@ -15,16 +15,30 @@ import UIKit
extension UIView {
func showSkeleton(skeletonConfig config: SkeletonConfig) {
func showSkeleton(
skeletonConfig config: SkeletonConfig,
notifyDelegate: Bool = true
) {
_isSkeletonAnimated = config.animated
_flowDelegate = SkeletonFlowHandler()
_flowDelegate?.willBeginShowingSkeletons(rootView: self)
if notifyDelegate {
_flowDelegate = SkeletonFlowHandler()
_flowDelegate?.willBeginShowingSkeletons(rootView: self)
}
recursiveShowSkeleton(skeletonConfig: config, root: self)
}
func updateSkeleton(skeletonConfig config: SkeletonConfig) {
func updateSkeleton(
skeletonConfig config: SkeletonConfig,
notifyDelegate: Bool = true
) {
_isSkeletonAnimated = config.animated
_flowDelegate?.willBeginUpdatingSkeletons(rootView: self)
if notifyDelegate {
_flowDelegate?.willBeginUpdatingSkeletons(rootView: self)
}
recursiveUpdateSkeleton(skeletonConfig: config, root: self)
}
@@ -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)
}
}