diff --git a/Assets/debug_description.png b/Assets/debug_description.png
new file mode 100644
index 00000000..766451ef
Binary files /dev/null and b/Assets/debug_description.png differ
diff --git a/Assets/debug_mode.png b/Assets/debug_mode.png
new file mode 100644
index 00000000..240c5864
Binary files /dev/null and b/Assets/debug_mode.png differ
diff --git a/Assets/hierarchy_output.png b/Assets/hierarchy_output.png
new file mode 100644
index 00000000..f1d5e91b
Binary files /dev/null and b/Assets/hierarchy_output.png differ
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6b785ccb..53ffdc77 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,20 @@
# Change Log
All notable changes to this project will be documented in this file
+## [Debug (1.4)](https://github.com/Juanpe/SkeletonView/releases/tag/1.4)
+
+### New
+
+- Create `skeletonDescription` print a skeleton representation of the view.
+- Create `SKELETON_DEBUG` environment variable, in order to print the view hierarchy when the skeleton appears.
+
+### Improvements
+- Add two new methods to `SkeletonFlowDelegate` protocol. Now you can know when the skeleton did show and when it did hide.
+- `Recursive` protocol
+
+### Bug fixes
+- Solved issue [#86](https://github.com/Juanpe/SkeletonView/issues/86) (thanks @reececomo)
+
## [Custom defaults (1.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.3)
### New
diff --git a/Example UICollectionView/AppDelegate.swift b/Example UICollectionView/AppDelegate.swift
index 5383e4ae..d5e870ea 100644
--- a/Example UICollectionView/AppDelegate.swift
+++ b/Example UICollectionView/AppDelegate.swift
@@ -1,10 +1,4 @@
-//
-// AppDelegate.swift
-// SkeletonViewExampleUICollectionView
-//
-// Created by Andrei Hogea on 14/02/2018.
// Copyright © 2018 SkeletonView. All rights reserved.
-//
import UIKit
diff --git a/Example UICollectionView/CollectionViewCell.swift b/Example UICollectionView/CollectionViewCell.swift
index 2b212b40..91f3be9c 100644
--- a/Example UICollectionView/CollectionViewCell.swift
+++ b/Example UICollectionView/CollectionViewCell.swift
@@ -1,10 +1,4 @@
-//
-// CollectionViewCell.swift
-// SkeletonView-iOS
-//
-// Created by Andrei Hogea on 14/02/2018.
// Copyright © 2018 SkeletonView. All rights reserved.
-//
import UIKit
import SkeletonView
diff --git a/Example UICollectionView/Constants.swift b/Example UICollectionView/Constants.swift
deleted file mode 100644
index 4254eaf7..00000000
--- a/Example UICollectionView/Constants.swift
+++ /dev/null
@@ -1,12 +0,0 @@
-//
-// Constants.swift
-// SkeletonViewExampleUICollectionView
-//
-// Created by Andrei Hogea on 15/02/2018.
-// Copyright © 2018 SkeletonView. All rights reserved.
-//
-
-import UIKit
-
-let colors = [(UIColor.turquoise,"turquoise"), (UIColor.emerald,"emerald"), (UIColor.peterRiver,"peterRiver"), (UIColor.amethyst,"amethyst"),(UIColor.wetAsphalt,"wetAsphalt"), (UIColor.nephritis,"nephritis"), (UIColor.belizeHole,"belizeHole"), (UIColor.wisteria,"wisteria"), (UIColor.midnightBlue,"midnightBlue"), (UIColor.sunFlower,"sunFlower"), (UIColor.carrot,"carrot"), (UIColor.alizarin,"alizarin"),(UIColor.clouds,"clouds"), (UIColor.concrete,"concrete"), (UIColor.flatOrange,"flatOrange"), (UIColor.pumpkin,"pumpkin"), (UIColor.pomegranate,"pomegranate"), (UIColor.silver,"silver"), (UIColor.asbestos,"asbestos")]
-
diff --git a/Example UICollectionView/ViewController.swift b/Example UICollectionView/ViewController.swift
index c035640e..47981438 100644
--- a/Example UICollectionView/ViewController.swift
+++ b/Example UICollectionView/ViewController.swift
@@ -1,10 +1,4 @@
- //
-// ViewController.swift
-// SkeletonViewExampleUICollectionView
-//
-// Created by Andrei Hogea on 14/02/2018.
// Copyright © 2018 SkeletonView. All rights reserved.
-//
import UIKit
import SkeletonView
diff --git a/Example/Base.lproj/Main.storyboard b/Example/Base.lproj/Main.storyboard
index 2000c24c..daa411b6 100644
--- a/Example/Base.lproj/Main.storyboard
+++ b/Example/Base.lproj/Main.storyboard
@@ -201,6 +201,9 @@
+
+
+
diff --git a/Example/Constants.swift b/Example/Constants.swift
index cf461ae0..cb6e0f29 100644
--- a/Example/Constants.swift
+++ b/Example/Constants.swift
@@ -1,10 +1,4 @@
-//
-// Constants.swift
-// SkeletonView-iOS
-//
-// Created by Renato Mendes on 28/11/2017.
-// Copyright © 2017 SkeletonView. All rights reserved.
-//
+// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
diff --git a/README.md b/README.md
index b4db9894..6ad776f4 100755
--- a/README.md
+++ b/README.md
@@ -52,6 +52,7 @@ Enjoy it! 🙂
* [Appearance](#-appearance)
* [Custom animations](#-custom-animations)
* [Hierarchy](#-hierarchy)
+ * [Debug](#-debug)
* [Documentation](#-documentation)
* [Next steps](#-next-steps)
* [Contributing](#-contributing)
@@ -366,6 +367,31 @@ Because an image is worth a thousand words:
|![](Assets/all_skeletonables.png) | ![](Assets/all_skeletonables_result.png)
+### 🔬 Debug
+
+**NEW** In order 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:
+```swift
+var skeletonDescription: 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.
+
+![](Assets/debug_mode.png)
+
+Then, when the skeleton appears, you can see the view hierarchy in the Xcode console.
+
+
+Open to see an output example
+
+
+
+
### 📚 Documentation
Coming soon...😅
@@ -379,6 +405,7 @@ Coming soon...😅
* [x] tvOS compatible
* [x] Add recovery state
* [x] Custom default appearance
+* [x] Debug mode
* [ ] Custom collections compatible
* [ ] Add animations when it shows/hides the skeletons
* [ ] MacOS and WatchOS compatible
diff --git a/SkeletonView.podspec b/SkeletonView.podspec
index 9d549a3e..f93ba09d 100644
--- a/SkeletonView.podspec
+++ b/SkeletonView.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SkeletonView"
- s.version = "1.3"
+ s.version = "1.4"
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.
diff --git a/SkeletonView.xcodeproj/project.pbxproj b/SkeletonView.xcodeproj/project.pbxproj
index eb9f3e65..e361b1ae 100644
--- a/SkeletonView.xcodeproj/project.pbxproj
+++ b/SkeletonView.xcodeproj/project.pbxproj
@@ -38,11 +38,13 @@
42ABD07A210B54E200BEEFF4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD072210B54E100BEEFF4 /* Assets.xcassets */; };
42ABD07B210B54E200BEEFF4 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD073210B54E100BEEFF4 /* ViewController.swift */; };
42ABD07C210B54E200BEEFF4 /* Base.lproj in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD074210B54E100BEEFF4 /* Base.lproj */; };
- 42ABD07D210B54E200BEEFF4 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD075210B54E100BEEFF4 /* Constants.swift */; };
42ABD07E210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD076210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist */; };
42ABD07F210B54E200BEEFF4 /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */; };
8748240D20C6A88A00E92179 /* UIView+IBInspectable.swift in Headers */ = {isa = PBXBuildFile; fileRef = F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */; settings = {ATTRIBUTES = (Public, ); }; };
8748240E20C6A88E00E92179 /* ContainsMultilineText.swift in Headers */ = {isa = PBXBuildFile; fileRef = F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8785E3A1211C9C9800CC9DFD /* SkeletonDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */; };
+ 8785E3A2211C9CA500CC9DFD /* SkeletonDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */; };
+ 8785E3A3211C9CE800CC9DFD /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88DEA97D1FCDBD1F006C80EF /* Constants.swift */; };
88DEA97F1FCDBD78006C80EF /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88DEA97D1FCDBD1F006C80EF /* Constants.swift */; };
8933C7851EB5B820000D00A4 /* SkeletonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* SkeletonView.swift */; };
F51DE1091FBF70A70037919A /* SkeletonAnimationBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */; };
@@ -71,6 +73,8 @@
F58A6E6C20A8C54100612494 /* RecoverableViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F58A6E6A20A8C54100612494 /* RecoverableViewState.swift */; };
F58A6E6E20A8C66300612494 /* Recoverable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F58A6E6D20A8C66300612494 /* Recoverable.swift */; };
F58A6E6F20A8C66300612494 /* Recoverable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F58A6E6D20A8C66300612494 /* Recoverable.swift */; };
+ F5A5E8B4211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5A5E8B3211DCE7000FD20D0 /* Int+Whitespaces.swift */; };
+ F5A5E8B5211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5A5E8B3211DCE7000FD20D0 /* Int+Whitespaces.swift */; };
F5D3FB0B209DCAA300003FCF /* SubviewsSkeletonables.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5D3FB0A209DCAA300003FCF /* SubviewsSkeletonables.swift */; };
F5D3FB0C209DCAA300003FCF /* SubviewsSkeletonables.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5D3FB0A209DCAA300003FCF /* SubviewsSkeletonables.swift */; };
F5F622411FAC6E31007C062A /* UIColor+Skeleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F622401FAC6E31007C062A /* UIColor+Skeleton.swift */; };
@@ -142,10 +146,10 @@
42ABD072210B54E100BEEFF4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
42ABD073210B54E100BEEFF4 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
42ABD074210B54E100BEEFF4 /* Base.lproj */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Base.lproj; sourceTree = ""; };
- 42ABD075210B54E100BEEFF4 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; };
42ABD076210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SkeletonViewExampleCollectionview-Info.plist"; sourceTree = ""; };
42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCell.swift; sourceTree = ""; };
52D6D97C1BEFF229002C0205 /* SkeletonView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SkeletonView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonDebug.swift; sourceTree = ""; };
88DEA97D1FCDBD1F006C80EF /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; };
8933C7841EB5B820000D00A4 /* SkeletonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkeletonView.swift; sourceTree = ""; };
F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonAnimationBuilder.swift; sourceTree = ""; };
@@ -167,6 +171,7 @@
F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+UIApplicationDelegate.swift"; sourceTree = ""; };
F58A6E6A20A8C54100612494 /* RecoverableViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoverableViewState.swift; sourceTree = ""; };
F58A6E6D20A8C66300612494 /* Recoverable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Recoverable.swift; sourceTree = ""; };
+ F5A5E8B3211DCE7000FD20D0 /* Int+Whitespaces.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Whitespaces.swift"; sourceTree = ""; };
F5D3FB0A209DCAA300003FCF /* SubviewsSkeletonables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubviewsSkeletonables.swift; sourceTree = ""; };
F5F622401FAC6E31007C062A /* UIColor+Skeleton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Skeleton.swift"; sourceTree = ""; };
F5F622421FAC81FD007C062A /* CALayer+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+Extensions.swift"; sourceTree = ""; };
@@ -226,7 +231,6 @@
42ABD072210B54E100BEEFF4 /* Assets.xcassets */,
42ABD074210B54E100BEEFF4 /* Base.lproj */,
42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */,
- 42ABD075210B54E100BEEFF4 /* Constants.swift */,
42ABD071210B54E100BEEFF4 /* Main.storyboard */,
42ABD073210B54E100BEEFF4 /* ViewController.swift */,
);
@@ -267,15 +271,32 @@
path = Configs;
sourceTree = "";
};
+ 8785E39E211C9C6D00CC9DFD /* Appearance */ = {
+ isa = PBXGroup;
+ children = (
+ F5307E2F1FB0EC9D00EE67C5 /* SkeletonAppearance.swift */,
+ );
+ path = Appearance;
+ sourceTree = "";
+ };
+ 8785E39F211C9C7C00CC9DFD /* Debug */ = {
+ isa = PBXGroup;
+ children = (
+ 8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */,
+ );
+ path = Debug;
+ sourceTree = "";
+ };
8933C7811EB5B7E0000D00A4 /* Sources */ = {
isa = PBXGroup;
children = (
+ 8785E39F211C9C7C00CC9DFD /* Debug */,
+ 8785E39E211C9C6D00CC9DFD /* Appearance */,
F58A6E7020A8C87100612494 /* Recoverable */,
F5307E331FB1068500EE67C5 /* Collections */,
F5307E341FB106A500EE67C5 /* Extensions */,
F5307E351FB106BF00EE67C5 /* Helpers */,
F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */,
- F5307E2F1FB0EC9D00EE67C5 /* SkeletonAppearance.swift */,
F587FB83202CBFC8002DB5FE /* SkeletonFlow.swift */,
F5307E2B1FAF6BC900EE67C5 /* SkeletonGradient.swift */,
F5F899E81FAB9D2B002E8FDA /* SkeletonLayer.swift */,
@@ -343,6 +364,7 @@
F5307E2D1FB0E5E400EE67C5 /* UIView+Frame.swift */,
F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */,
F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */,
+ F5A5E8B3211DCE7000FD20D0 /* Int+Whitespaces.swift */,
);
path = Extensions;
sourceTree = "";
@@ -593,6 +615,7 @@
17DD0E1E207FB32100C56334 /* PrepareForSkeletonProtocol.swift in Sources */,
F51ED28520973CC9008B2434 /* SkeletonReusableCell.swift in Sources */,
17DD0E17207FB28F00C56334 /* SkeletonLayer.swift in Sources */,
+ 8785E3A2211C9CA500CC9DFD /* SkeletonDebug.swift in Sources */,
17DD0E08207FB28900C56334 /* CollectionSkeletonProtocol.swift in Sources */,
F58A6E6C20A8C54100612494 /* RecoverableViewState.swift in Sources */,
17DD0E0B207FB28900C56334 /* SkeletonTableViewProtocols.swift in Sources */,
@@ -614,6 +637,7 @@
F51ED28420973CC6008B2434 /* GenericCollectionView.swift in Sources */,
17DD0E0A207FB28900C56334 /* SkeletonCollectionViewProtocols.swift in Sources */,
17DD0E1C207FB32100C56334 /* AssociationPolicy.swift in Sources */,
+ F5A5E8B5211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */,
17DD0E1D207FB32100C56334 /* ContainsMultilineText.swift in Sources */,
17DD0E0F207FB28C00C56334 /* CALayer+Extensions.swift in Sources */,
17DD0E16207FB28F00C56334 /* SkeletonGradient.swift in Sources */,
@@ -627,7 +651,7 @@
42ABD07B210B54E200BEEFF4 /* ViewController.swift in Sources */,
42ABD078210B54E200BEEFF4 /* AppDelegate.swift in Sources */,
42ABD07F210B54E200BEEFF4 /* CollectionViewCell.swift in Sources */,
- 42ABD07D210B54E200BEEFF4 /* Constants.swift in Sources */,
+ 8785E3A3211C9CE800CC9DFD /* Constants.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -639,6 +663,7 @@
F54CF5E42024CEB000330B0D /* UIView+CollectionSkeleton.swift in Sources */,
F5307E371FB1076E00EE67C5 /* SkeletonTableViewProtocols.swift in Sources */,
8933C7851EB5B820000D00A4 /* SkeletonView.swift in Sources */,
+ 8785E3A1211C9C9800CC9DFD /* SkeletonDebug.swift in Sources */,
F5307E301FB0EC9D00EE67C5 /* SkeletonAppearance.swift in Sources */,
F58A6E6B20A8C54100612494 /* RecoverableViewState.swift in Sources */,
F54CF5E52024CEB000330B0D /* UITableView+CollectionSkeleton.swift in Sources */,
@@ -660,6 +685,7 @@
F5307E3B1FB123C100EE67C5 /* ContainsMultilineText.swift in Sources */,
F5F899E91FAB9D2B002E8FDA /* SkeletonLayer.swift in Sources */,
F5307E2E1FB0E5E400EE67C5 /* UIView+Frame.swift in Sources */,
+ F5A5E8B4211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */,
F51DF871206E91B300D23301 /* SkeletonReusableCell.swift in Sources */,
F51DF879206E9F5500D23301 /* SkeletonCollectionDelegate.swift in Sources */,
F51DE1091FBF70A70037919A /* SkeletonAnimationBuilder.swift in Sources */,
diff --git a/SkeletonViewExample copy-Info.plist b/SkeletonViewExample copy-Info.plist
deleted file mode 100644
index 9751b3b3..00000000
--- a/SkeletonViewExample copy-Info.plist
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
- CFBundleDevelopmentRegion
- $(DEVELOPMENT_LANGUAGE)
- CFBundleExecutable
- $(EXECUTABLE_NAME)
- CFBundleIdentifier
- $(PRODUCT_BUNDLE_IDENTIFIER)
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- $(PRODUCT_NAME)
- CFBundlePackageType
- APPL
- CFBundleShortVersionString
- 1.3
- CFBundleVersion
- 1
- LSRequiresIPhoneOS
-
- UILaunchStoryboardName
- LaunchScreen
- UIMainStoryboardFile
- Main
- UIRequiredDeviceCapabilities
-
- armv7
-
- UISupportedInterfaceOrientations
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- UISupportedInterfaceOrientations~ipad
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationPortraitUpsideDown
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
-
-
diff --git a/Sources/SkeletonAppearance.swift b/Sources/Appearance/SkeletonAppearance.swift
similarity index 100%
rename from Sources/SkeletonAppearance.swift
rename to Sources/Appearance/SkeletonAppearance.swift
diff --git a/Sources/Debug/SkeletonDebug.swift b/Sources/Debug/SkeletonDebug.swift
new file mode 100644
index 00000000..2ab78adc
--- /dev/null
+++ b/Sources/Debug/SkeletonDebug.swift
@@ -0,0 +1,50 @@
+
+// Copyright © 2018 SkeletonView. All rights reserved.
+
+import Foundation
+import UIKit
+
+enum SkeletonEnvironmentKey: String {
+ case debugMode = "SKELETON_DEBUG"
+}
+
+extension Dictionary {
+ subscript (_ key: SkeletonEnvironmentKey) -> Value? {
+ return self[key.rawValue as! Key]
+ }
+}
+
+func printSkeletonHierarchy(in view: UIView) {
+ skeletonLog(view.skeletonHierarchy())
+}
+
+func skeletonLog(_ message: String) {
+ if let _ = ProcessInfo.processInfo.environment[.debugMode] {
+ print(message)
+ }
+}
+
+extension UIView {
+
+ public var skeletonDescription: String {
+ var description = "<\(type(of: self)): \(Unmanaged.passUnretained(self).toOpaque())"
+ let subSkeletons = subviewsSkeletonables
+ if subSkeletons.count != 0 {
+ description += " | (\(subSkeletons.count)) subSkeletons"
+ }
+ if isSkeletonable {
+ description += " | ☠️ "
+ }
+ return description + ">"
+ }
+
+ public func skeletonHierarchy(index: Int = 0) -> String {
+ var description = index == 0 ? "\n ⬇⬇ ☠️ Root view hierarchy with Skeletons ⬇⬇ \n" : ""
+ description += "\(index == 0 ? "\n" : 3.whitespaces) \(skeletonDescription) \n"
+ subviewsToSkeleton.forEach {
+ description += (index + 2).whitespaces
+ description += $0.skeletonHierarchy(index: index + 1)
+ }
+ return description
+ }
+}
diff --git a/Sources/Extensions/CALayer+Extensions.swift b/Sources/Extensions/CALayer+Extensions.swift
index 4f73bccb..25a6ce7f 100644
--- a/Sources/Extensions/CALayer+Extensions.swift
+++ b/Sources/Extensions/CALayer+Extensions.swift
@@ -10,18 +10,20 @@ import UIKit
extension CALayer {
@objc func tint(withColors colors: [UIColor]) {
- recursiveSearch(inArray: skeletonSublayers,
- leafBlock: { backgroundColor = colors.first?.cgColor }) {
- $0.tint(withColors: colors)
+ skeletonSublayers.recursiveSearch(leafBlock: {
+ backgroundColor = colors.first?.cgColor
+ }) {
+ $0.tint(withColors: colors)
}
}
}
extension CAGradientLayer {
override func tint(withColors colors: [UIColor]) {
- recursiveSearch(inArray: skeletonSublayers,
- leafBlock: { self.colors = colors.map { $0.cgColor } }) {
- $0.tint(withColors: colors)
+ skeletonSublayers.recursiveSearch(leafBlock: {
+ self.colors = colors.map { $0.cgColor }
+ }) {
+ $0.tint(withColors: colors)
}
}
}
@@ -91,16 +93,18 @@ public extension CALayer {
}
func playAnimation(_ anim: SkeletonLayerAnimation, key: String) {
- recursiveSearch(inArray: skeletonSublayers,
- leafBlock: { add(anim(self), forKey: key) }) {
- $0.playAnimation(anim, key: key)
+ skeletonSublayers.recursiveSearch(leafBlock: {
+ add(anim(self), forKey: key)
+ }) {
+ $0.playAnimation(anim, key: key)
}
}
func stopAnimation(forKey key: String) {
- recursiveSearch(inArray: skeletonSublayers,
- leafBlock: { removeAnimation(forKey: key) }) {
- $0.stopAnimation(forKey: key)
+ skeletonSublayers.recursiveSearch(leafBlock: {
+ removeAnimation(forKey: key)
+ }) {
+ $0.stopAnimation(forKey: key)
}
}
}
diff --git a/Sources/Extensions/Int+Whitespaces.swift b/Sources/Extensions/Int+Whitespaces.swift
new file mode 100644
index 00000000..7d5bef58
--- /dev/null
+++ b/Sources/Extensions/Int+Whitespaces.swift
@@ -0,0 +1,14 @@
+// Copyright © 2018 SkeletonView. All rights reserved.
+
+import Foundation
+
+extension Int {
+
+ var whitespace: String {
+ return whitespaces
+ }
+
+ var whitespaces: String {
+ return String(repeating: " ", count: self)
+ }
+}
diff --git a/Sources/Helpers/RecursiveProtocol.swift b/Sources/Helpers/RecursiveProtocol.swift
index 9b77e63b..29c73e9d 100644
--- a/Sources/Helpers/RecursiveProtocol.swift
+++ b/Sources/Helpers/RecursiveProtocol.swift
@@ -1,37 +1,28 @@
-//
-// HelperProtocols.swift
-// SkeletonView-iOS
-//
-// Created by Juanpe Catalán on 06/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
-//
import UIKit
typealias VoidBlock = () -> Void
typealias RecursiveBlock = (T) -> Void
+protocol IterableElement {}
+extension UIView: IterableElement {}
+extension CALayer: IterableElement {}
+
//MARK: Recursive
protocol Recursive {
- associatedtype Element
- func recursiveSearch(inArray array:[Element], leafBlock: VoidBlock, recursiveBlock: RecursiveBlock)
+ associatedtype Element: IterableElement
+ func recursiveSearch(leafBlock: VoidBlock, recursiveBlock: RecursiveBlock)
}
-extension Recursive {
- func recursiveSearch(inArray array:[Element], leafBlock: VoidBlock, recursiveBlock: RecursiveBlock) {
- guard array.count > 0 else {
+extension Array: Recursive where Element: IterableElement {
+ func recursiveSearch(leafBlock: VoidBlock, recursiveBlock: RecursiveBlock) {
+ guard count > 0 else {
leafBlock()
return
}
- array.forEach { recursiveBlock($0) }
+ forEach { recursiveBlock($0) }
}
}
-extension UIView: Recursive {
- typealias Element = UIView
-}
-extension CALayer: Recursive {
- typealias Element = CALayer
-}
-
diff --git a/Sources/SkeletonFlow.swift b/Sources/SkeletonFlow.swift
index c9526e83..8d57a995 100644
--- a/Sources/SkeletonFlow.swift
+++ b/Sources/SkeletonFlow.swift
@@ -1,26 +1,30 @@
-//
-// SkeletonFlow.swift
-// SkeletonView-iOS
-//
-// Created by Juanpe Catalán on 08/02/2018.
// Copyright © 2018 SkeletonView. All rights reserved.
-//
import UIKit
protocol SkeletonFlowDelegate {
func willBeginShowingSkeletons(withRootView rootView: UIView)
+ func didShowSkeletons(withRootView rootView: UIView)
func willBeginHidingSkeletons(withRootView rootView: UIView)
+ func didHideSkeletons(withRootView rootView: UIView)
+
}
class SkeletonFlowHandler: SkeletonFlowDelegate {
-
+
func willBeginShowingSkeletons(withRootView rootView: UIView) {
rootView.addAppNotificationsObservers()
}
+
+ func didShowSkeletons(withRootView rootView: UIView) {
+ printSkeletonHierarchy(in: rootView)
+ }
func willBeginHidingSkeletons(withRootView rootView: UIView) {
rootView.removeAppNoticationsObserver()
+ }
+
+ func didHideSkeletons(withRootView rootView: UIView) {
rootView.flowDelegate = nil
}
}
diff --git a/Sources/SkeletonView.swift b/Sources/SkeletonView.swift
index 8f51576e..37eedb5e 100644
--- a/Sources/SkeletonView.swift
+++ b/Sources/SkeletonView.swift
@@ -22,22 +22,20 @@ public extension UIView {
func hideSkeleton(reloadDataAfter reload: Bool = true) {
flowDelegate?.willBeginHidingSkeletons(withRootView: self)
- recursiveHideSkeleton(reloadDataAfter: reload)
+ recursiveHideSkeleton(reloadDataAfter: reload, root: self)
}
func startSkeletonAnimation(_ anim: SkeletonLayerAnimation? = nil) {
skeletonIsAnimated = true
- recursiveSearch(inArray: subviewsSkeletonables,
- leafBlock: startSkeletonLayerAnimationBlock(anim)) {
- $0.startSkeletonAnimation(anim)
- }
+ subviewsSkeletonables.recursiveSearch(leafBlock: startSkeletonLayerAnimationBlock(anim)) { subview in
+ subview.startSkeletonAnimation(anim)
+ }
}
func stopSkeletonAnimation() {
skeletonIsAnimated = false
- recursiveSearch(inArray: subviewsSkeletonables,
- leafBlock: stopSkeletonLayerAnimationBlock) {
- $0.stopSkeletonAnimation()
+ subviewsSkeletonables.recursiveSearch(leafBlock: stopSkeletonLayerAnimationBlock) { subview in
+ subview.stopSkeletonAnimation()
}
}
}
@@ -48,33 +46,39 @@ extension UIView {
skeletonIsAnimated = animated
flowDelegate = SkeletonFlowHandler()
flowDelegate?.willBeginShowingSkeletons(withRootView: self)
- recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation)
+ recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation, root: self)
}
- fileprivate func recursiveShowSkeleton(withType type: SkeletonType, usingColors colors: [UIColor], animated: Bool, animation: SkeletonLayerAnimation?) {
+ fileprivate func recursiveShowSkeleton(withType type: SkeletonType, usingColors colors: [UIColor], animated: Bool, animation: SkeletonLayerAnimation?, root: UIView? = nil) {
addDummyDataSourceIfNeeded()
- recursiveSearch(inArray: subviewsSkeletonables,
- leafBlock: {
- guard !isSkeletonActive else { return }
- isUserInteractionEnabled = false
- saveViewState()
- (self as? PrepareForSkeleton)?.prepareViewForSkeleton()
- addSkeletonLayer(withType: type, usingColors: colors, animated: animated, animation: animation)
- }) {
- $0.recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation)
+
+ subviewsSkeletonables.recursiveSearch(leafBlock: {
+ guard !isSkeletonActive else { return }
+ isUserInteractionEnabled = false
+ saveViewState()
+ (self as? PrepareForSkeleton)?.prepareViewForSkeleton()
+ addSkeletonLayer(withType: type, usingColors: colors, animated: animated, animation: animation)
+ }) { subview in
+ subview.recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation)
+ }
+
+ if let root = root {
+ flowDelegate?.didShowSkeletons(withRootView: root)
}
}
- fileprivate func recursiveHideSkeleton(reloadDataAfter reload: Bool) {
+ fileprivate func recursiveHideSkeleton(reloadDataAfter reload: Bool, root: UIView? = nil) {
removeDummyDataSourceIfNeeded()
isUserInteractionEnabled = true
- recursiveSearch(inArray: subviewsSkeletonables,
- leafBlock: {
- recoverViewState(forced: false)
- removeSkeletonLayer()
- }, recursiveBlock: {
- $0.recursiveHideSkeleton(reloadDataAfter: reload)
- })
+ subviewsSkeletonables.recursiveSearch(leafBlock: {
+ recoverViewState(forced: false)
+ removeSkeletonLayer()
+ }) { subview in
+ subview.recursiveHideSkeleton(reloadDataAfter: reload)
+ }
+ if let root = root {
+ flowDelegate?.didHideSkeletons(withRootView: root)
+ }
}
fileprivate func startSkeletonLayerAnimationBlock(_ anim: SkeletonLayerAnimation? = nil) -> VoidBlock {
diff --git a/Sources/SubviewsSkeletonables.swift b/Sources/SubviewsSkeletonables.swift
index 289f87f8..aa950a91 100644
--- a/Sources/SubviewsSkeletonables.swift
+++ b/Sources/SubviewsSkeletonables.swift
@@ -1,45 +1,48 @@
-//
-// SubviewsSkeletonables.swift
-// SkeletonView
-//
-// Created by Juanpe Catalán on 05/05/2018.
// Copyright © 2018 SkeletonView. All rights reserved.
-//
import UIKit
extension UIView {
@objc var subviewsSkeletonables: [UIView] {
- return subviews.filter { $0.isSkeletonable }
+ return subviewsToSkeleton.filter { $0.isSkeletonable }
+ }
+
+ @objc var subviewsToSkeleton: [UIView] {
+ return subviews
}
}
extension UITableView {
- override var subviewsSkeletonables: [UIView] {
- return visibleCells.filter { $0.isSkeletonable }
+
+ override var subviewsToSkeleton: [UIView] {
+ return visibleCells
}
}
extension UITableViewCell {
- override var subviewsSkeletonables: [UIView] {
- return contentView.subviews.filter { $0.isSkeletonable }
+
+ override var subviewsToSkeleton: [UIView] {
+ return contentView.subviews
}
}
extension UICollectionView {
- override var subviewsSkeletonables: [UIView] {
- return subviews.filter { $0.isSkeletonable }
+
+ override var subviewsToSkeleton: [UIView] {
+ return subviews
}
}
extension UICollectionViewCell {
- override var subviewsSkeletonables: [UIView] {
- return contentView.subviews.filter { $0.isSkeletonable }
+
+ override var subviewsToSkeleton: [UIView] {
+ return contentView.subviews
}
}
extension UIStackView {
- override var subviewsSkeletonables: [UIView] {
- return arrangedSubviews.filter { $0.isSkeletonable }
+
+ override var subviewsToSkeleton: [UIView] {
+ return arrangedSubviews
}
}