Skip to content

Commit

Permalink
feat: Create skeletonHierarchy
Browse files Browse the repository at this point in the history
  • Loading branch information
Juanpe Catalán committed Aug 10, 2018
1 parent 4138677 commit 90b993a
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 83 deletions.
3 changes: 3 additions & 0 deletions Example/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@
<constraint firstItem="XgY-1a-UGc" firstAttribute="bottom" secondItem="6Tk-OE-BBY" secondAttribute="bottom" id="vnZ-9k-MfI"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="NO"/>
</userDefinedRuntimeAttributes>
</view>
<navigationItem key="navigationItem" id="BEI-dU-kr2"/>
<connections>
Expand Down
44 changes: 20 additions & 24 deletions Sources/Debug/SkeletonDebug.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,36 @@ extension Dictionary {
}
}

func log(_ message: String) {
func printSkeletonHierarchy(in view: UIView) {
skeletonLog(view.skeletonHierarchy())
}

func skeletonLog(_ message: String) {
if let _ = ProcessInfo.processInfo.environment[.debugMode] {
print(message)
}
}

extension UIView {
public func skeletonHierarchy(index: Int = 0, onlySkeleletons: Bool = true) -> String {
var description = index == 0 ? "\n\n ------------------ [☠️](Hierarchy) ------------------ \n " : ""
description += "\(index == 0 ? "\n" : " ")<\(type(of: self)): \(Unmanaged.passUnretained(self).toOpaque())> "
if self.subviewsSkeletonables.count != 0 {
description += "with [\(self.subviewsSkeletonables.count)] subSkeletons:"
} else {
description = " ☠️ - " + description.replacingOccurrences(of: " ", with: "")

public func skeletonDescription(short: Bool = false) -> String {
var description = "<\(type(of: self)): \(Unmanaged.passUnretained(self).toOpaque())"
let subSkeletons = subviewsSkeletonables
if !short, subSkeletons.count != 0 {
description += " | (\(subSkeletons.count)) subSkeletons"
}
description += "\n"
(onlySkeleletons ? self.subviews : self.subviewsSkeletonables).forEach {
description += String(repeating: " ", count: index)
description += $0.skeletonHierarchy(index: index + 3)
if isSkeletonable {
description += " | ☠️ "
}
return description
return description + ">"
}

public func allViewsHierarchy(index: Int = 0) -> String {
var description = "\(index == 0 ? "\n" : " ")<\(type(of: self)): \(Unmanaged.passUnretained(self).toOpaque())> "
if self.subviewsSkeletonables.count != 0 {
description += "with [\(self.subviewsSkeletonables.count)] subSkeletons:"
} else {
description = " \(self.isSkeletonable ? "☠️ - " : "")" + description.replacingOccurrences(of: " ", with: "")
}
description += "\n"
self.subviews.forEach {

public func skeletonHierarchy(index: Int = 0) -> String {
var description = index == 0 ? "\n ⬇⬇ ☠️ Root view hierarchy with Skeletons ⬇⬇ \n" : ""
description += "\(index == 0 ? "\n" : " ") \(skeletonDescription()) \n"
subviewsToSkeleton.forEach {
description += String(repeating: " ", count: index)
description += $0.allViewsHierarchy(index: index + 3)
description += $0.skeletonHierarchy(index: index + 3)
}
return description
}
Expand Down
28 changes: 16 additions & 12 deletions Sources/Extensions/CALayer+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
Expand Down Expand Up @@ -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)
}
}
}
14 changes: 11 additions & 3 deletions Sources/SkeletonFlow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,27 @@ 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) {
log("☠️ will begin showing \(rootView.skeletonHierarchy())")
rootView.addAppNotificationsObservers()
}

func didShowSkeletons(withRootView rootView: UIView) {
printSkeletonHierarchy(in: rootView)
}

func willBeginHidingSkeletons(withRootView rootView: UIView) {
log("☠️ will begin hidding using as root container ")
rootView.removeAppNoticationsObserver()
}

func didHideSkeletons(withRootView rootView: UIView) {
rootView.flowDelegate = nil
}
}
58 changes: 31 additions & 27 deletions Sources/SkeletonView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
}
Expand All @@ -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 {
Expand Down
37 changes: 20 additions & 17 deletions Sources/SubviewsSkeletonables.swift
Original file line number Diff line number Diff line change
@@ -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
}
}

0 comments on commit 90b993a

Please sign in to comment.