☠️ An elegant way to show users that something is happening and also prepare them to which contents they are awaiting

Overview

codebeat badge SkeletonView Playground

FeaturesGuidesInstallationUsageMiscellaneousContributing

🌎 README is available in other languages: 🇪🇸 . 🇨🇳 . 🇧🇷 . 🇰🇷 . 🇫🇷

Today almost all apps have async processes, such as API requests, long running processes, etc. While the processes are working, usually developers place a loading view to show users that something is going on.

SkeletonView has been conceived to address this need, an elegant way to show users that something is happening and also prepare them for which contents are waiting.

Enjoy it! 🙂

🌟 Features

  • Easy to use
  • All UIViews are skeletonables
  • Fully customizable
  • Universal (iPhone & iPad)
  • Interface Builder friendly
  • Simple Swift syntax
  • Lightweight readable codebase

🎬 Guides

SkeletonView Guides - Getting started How to Create Loading View with Skeleton View in Swift 5.2 by iKh4ever Studio Create Skeleton Loading View in App (Swift 5) - Xcode 11, 2020 by iOS Academy Add An Elegant Loading Animation in Swift* by Gary Tokman Cómo crear una ANIMACIÓN de CARGA de DATOS en iOS by MoureDev

📲 Installation

pod 'SkeletonView'
github "Juanpe/SkeletonView"
dependencies: [
  .package(url: "https://github.com/Juanpe/SkeletonView.git", from: "1.7.0")
]

🐒 Usage

Only 3 steps needed to use SkeletonView:

1️⃣ Import SkeletonView in proper place.

import SkeletonView

2️⃣ Now, set which views will be skeletonables. You achieve this in two ways:

Using code:

avatarImageView.isSkeletonable = true

Using IB/Storyboards:

3️⃣ Once you've set the views, you can show the skeleton. To do so, you have 4 choices:

(1) view.showSkeleton()                 // Solid
(2) view.showGradientSkeleton()         // Gradient
(3) view.showAnimatedSkeleton()         // Solid animated
(4) view.showAnimatedGradientSkeleton() // Gradient animated

Preview

Solid Gradient Solid Animated Gradient Animated

📣 IMPORTANT!

SkeletonView is recursive, so if you want show the skeleton in all skeletonable views, you only need to call the show method in the main container view. For example, with UIViewControllers.

🌿 Collections

SkeletonView is compatible with UITableView and UICollectionView.

UITableView

If you want to show the skeleton in a UITableView, you need to conform to SkeletonTableViewDataSource protocol.

public protocol SkeletonTableViewDataSource: UITableViewDataSource {
    func numSections(in collectionSkeletonView: UITableView) -> Int
    func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
    func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
}

As you can see, this protocol inherits from UITableViewDataSource, so you can replace this protocol with the skeleton protocol.

This protocol has a default implementation:

func numSections(in collectionSkeletonView: UITableView) -> Int
// Default: 1
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
// Default:
// It calculates how many cells need to populate whole tableview

There is only one method you need to implement to let Skeleton know the cell identifier. This method doesn't have default implementation:

func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier

Example

func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
   return "CellIdentifier"
}

Besides, you can skeletonize both the headers and footers. You need to conform to SkeletonTableViewDelegate protocol.

public protocol SkeletonTableViewDelegate: UITableViewDelegate {
    func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier? // default: nil
    func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier? // default: nil
}

📣 IMPORTANT!

1️⃣ If you are using resizable cells (tableView.rowHeight = UITableViewAutomaticDimension), it's mandatory define the estimatedRowHeight.

2️⃣ When you add elements in a UITableViewCell you should add it to contentView and not to the cell directly.

self.contentView.addSubview(titleLabel)          
self.addSubview(titleLabel) 

UICollectionView

For UICollectionView, you need to conform to SkeletonCollectionViewDataSource protocol.

public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
    func numSections(in collectionSkeletonView: UICollectionView) -> Int // default: 1
    func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier
    func collectionSkeletonView(_ skeletonView: UICollectionView, supplementaryViewIdentifierOfKind: String, at indexPath: IndexPath) -> ReusableCellIdentifier? // default: nil
}

The rest of the process is the same as UITableView

🔠 Texts

When using elements with text, SkeletonView draws lines to simulate text. Besides, you can decide how many lines you want. If numberOfLines is set to zero, it will calculate how many lines needed to populate the whole skeleton and it will be drawn. Instead, if you set it to one, two or any number greater than zero, it will only draw this number of lines.

You can set some properties for multilines elements.

Property Values Default Preview
Filling percent of the last line. 0...100 70%
Corner radius of lines. (NEW) 0...10 0

To modify the percent or radius using code, set the properties:

descriptionTextView.lastLineFillPercent = 50
descriptionTextView.linesCornerRadius = 5

Or, if you prefer use IB/Storyboard:

🦋 Appearance

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
    • default: .skeletonDefault (same as .clouds but adaptive to dark mode)
  • gradient: SkeletonGradient
    • default: SkeletonGradient(baseColor: .skeletonDefault)
  • multilineHeight: CGFloat
    • default: 15
  • multilineSpacing: CGFloat
    • default: 10
  • multilineLastLineFillPercent: Int
    • default: 70
  • multilineCornerRadius: Int
    • default: 0
  • 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:

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

🎨 Custom colors

You can decide which color the skeleton is tinted with. You only need to pass as a parameter the color or gradient you want.

Using solid colors

view.showSkeleton(usingColor: UIColor.gray) // Solid
// or
view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0))

Using gradients

let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
view.showGradientSkeleton(usingGradient: gradient) // Gradient

Besides, SkeletonView features 20 flat colors 🤙🏼

UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...

Image captured from website https://flatuicolors.com

🏃‍♀️ Animations

SkeletonView has two built-in animations, pulse for solid skeletons and sliding for gradients.

Besides, if you want to do your own skeleton animation, it's really easy.

Skeleton provides the showAnimatedSkeleton function which has a SkeletonLayerAnimation closure where you can define your custom animation.

public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation

You can call the function like this:

view.showAnimatedSkeleton { (layer) -> CAAnimation in
  let animation = CAAnimation()
  // Customize here your animation

  return animation
}

It's available SkeletonAnimationBuilder. It's a builder to make SkeletonLayerAnimation.

Today, you can create sliding animations for gradients, deciding the direction and setting the duration of the animation (default = 1.5s).

// func makeSlidingAnimation(withDirection direction: GradientDirection, duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation

let animation = SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: .leftToRight)
view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)

GradientDirection is an enum, with theses cases:

Direction Preview
.leftRight
.rightLeft
.topBottom
.bottomTop
.topLeftBottomRight
.bottomRightTopLeft

😉 TRICK!

Exist another way to create sliding animations, just using this shortcut:

let animation = GradientDirection.leftToRight.slidingAnimation()

🏄 Transitions

SkeletonView has built-in transitions to show or hide the skeletons in a smoother way 🤙

To use the transition, simply add the transition parameter to your showSkeleton() or hideSkeleton() function with the transition time, like this:

view.showSkeleton(transition: .crossDissolve(0.25))     //Show skeleton cross dissolve transition with 0.25 seconds fade time
view.hideSkeleton(transition: .crossDissolve(0.25))     //Hide skeleton cross dissolve transition with 0.25 seconds fade time

The default value is crossDissolve(0.25)

Preview

None Cross dissolve

Miscellaneous

Hierarchy

Since SkeletonView is recursive, and we want skeleton to be very efficient, we want to stop recursion as soon as possible. For this reason, you must set the container view as Skeletonable, because Skeleton will stop looking for skeletonable subviews as soon as a view is not Skeletonable, breaking then the recursion.

Because an image is worth a thousand words:

In this example we have a UIViewController with a ContainerView and a UITableView. When the view is ready, we show the skeleton using this method:

view.showSkeleton()

isSkeletonable= ☠️

Configuration Result

Hierarchy in collections

Here is an illustration that shows how you should specify which elements are skeletonables when you are using an UITableView:

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.

You can relayout the skeleton views like so:

override func viewDidLayoutSubviews() {
    view.layoutSkeletonIfNeeded()
}

📣 IMPORTANT!

You shouldn't call this method. From version 1.8.1 you don't need to call this method, the library does automatically. So, you can use this method ONLY in the cases when you need to update the layout of the skeleton manually.

Update skeleton

You can change the skeleton configuration at any time like its colour, animation, etc. with the following methods:

(1) view.updateSkeleton()                 // Solid
(2) view.updateGradientSkeleton()         // Gradient
(3) view.updateAnimatedSkeleton()         // Solid animated
(4) view.updateAnimatedGradientSkeleton() // Gradient animated

Hiding views when the animation starts

Sometimes you wanna hide some view when the animation starts, so there is a quick property that you can use to make this happen:

view.isHiddenWhenSkeletonIsActive = true  // This works only when isSkeletonable = true

Debug

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:

var skeletonDescription: String

The skeleton representation looks like this:

Besides, you can activate the new debug mode. You just add the environment variable SKELETON_DEBUG and activate it.

Then, when the skeleton appears, you can see the view hierarchy in the Xcode console.

Open to see an output example

Supported OS & SDK Versions

  • iOS 9.0+
  • tvOS 9.0+
  • Swift 5

❤️ Contributing

This is an open source project, so feel free to contribute. How?

  • Open an issue.
  • Send feedback via email.
  • Propose your own fixes, suggestions and open a pull request with the changes.

See all contributors

For more information, please read the contributing guidelines.

📢 Mentions

👨🏻‍💻 Author

Juanpe Catalán

Buy me a coffee

👮🏻 License

MIT License

Copyright (c) 2017 Juanpe Catalán

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Comments
  • Crash when pushing view controller with date picker on top of view controller with skeleton started and stopped

    Crash when pushing view controller with date picker on top of view controller with skeleton started and stopped

    What did you do?

    tried to push a view controller with a DatePickerView with mode set to .time when there was a view controller with skeletonable views in the navigation controller stack

    What did you expect to happen?

    The view controller will be pushed and I will be able to see the DatePickerView

    What happened instead?

    The app crashed on appDelegate start

    Call Stack

    
    	0   CoreFoundation                      0x00000001176578db __exceptionPreprocess + 331
    	1   libobjc.A.dylib                     0x0000000116640ac5 objc_exception_throw + 48
    	2   CoreFoundation                      0x00000001175a5fac _CFThrowFormattedException + 194
    	3   CoreFoundation                      0x00000001175824e2 -[__NSArrayM objectAtIndex:] + 178
    	4   UIKitCore                           0x000000011bb40d70 -[UIPickerView selectedRowInComponent:] + 76
    	5   UIKitCore                           0x000000011bb289c1 -[_UIDatePickerMode_Time _shouldEnableValueForRow:inComponent:calendarUnit:] + 234
    	6   UIKitCore                           0x000000011bb268b9 -[_UIDatePickerMode _shouldEnableValueForRow:column:] + 68
    	7   UIKitCore                           0x000000011bb2646d -[_UIDatePickerMode viewForRow:inComponent:reusingView:] + 942
    	8   UIKitCore                           0x000000011bb32086 -[_UIDatePickerView pickerView:viewForRow:forComponent:reusingView:] + 60
    	9   UIKitCore                           0x000000011bb4149c -[UIPickerView tableView:cellForRowAtIndexPath:] + 620
    	10  UIKitCore                           0x000000011bb34a06 -[UIPickerColumnView tableView:cellForRowAtIndexPath:] + 168
    	11  UIKitCore                           0x000000011bde5f60 -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] + 764
    	12  UIKitCore                           0x000000011bde6499 -[UITableView _createPreparedCellForGlobalRow:willDisplay:] + 73
    	13  UIKitCore                           0x000000011bdae654 -[UITableView _updateVisibleCellsNow:isRecursive:] + 2870
    	14  UIKitCore                           0x000000011bdc2c28 -[UITableView _visibleCellsUsingPresentationValues:] + 518
    	15  SkeletonView                        0x00000001152ba663 $sSo11UITableViewC08SkeletonB0E010subviewsToC0SaySo6UIViewCGvg + 35
    	16  SkeletonView                        0x00000001152ba5f5 $sSo11UITableViewC08SkeletonB0E010subviewsToC0SaySo6UIViewCGvgTo + 53
    	17  SkeletonView                        0x00000001152ba2d6 $sSo6UIViewC12SkeletonViewE21subviewsSkeletonablesSayABGvg + 54
    	18  SkeletonView                        0x00000001152ba255 $sSo6UIViewC12SkeletonViewE21subviewsSkeletonablesSayABGvgTo + 53
    	19  SkeletonView                        0x00000001152cb0d6 $sSo6UIViewC12SkeletonViewE02isB6ActiveSbvg + 182
    	20  SkeletonView                        0x00000001152b71b3 $sSo6UIViewC12SkeletonViewE32skeletonTraitCollectionDidChangeyySo07UITraitF0CSgF + 131
    	21  SkeletonView                        0x00000001152b7294 $sSo6UIViewC12SkeletonViewE32skeletonTraitCollectionDidChangeyySo07UITraitF0CSgFTo + 68
    	22  UIKitCore                           0x000000011c0144b2 -[UIScrollView traitCollectionDidChange:] + 73
    	23  UIKitCore                           0x000000011bdb9bd0 -[UITableView traitCollectionDidChange:] + 74
    	24  UIKitCore                           0x000000011c05f27a -[UIView _traitCollectionDidChangeInternal:] + 780
    	25  UIKitCore                           0x000000011c05f341 -[UIView _wrappedProcessTraitCollectionDidChange:forceNotification:] + 137
    	26  UIKitCore                           0x000000011c05f44e -[UIView _wrappedProcessTraitCollectionDidChange:forceNotification:] + 406
    	27  UIKitCore                           0x000000011c05f44e -[UIView _wrappedProcessTraitCollectionDidChange:forceNotification:] + 406
    	28  UIKitCore                           0x000000011bfc12b7 -[UIView(AdditionalLayoutSupport) _withUnsatisfiableConstraintsLoggingSuspendedIfEngineDelegateExists:] + 104
    	29  UIKitCore                           0x000000011c05f654 -[UIView _processDidChangeRecursivelyFromOldTraits:toCurrentTraits:forceNotification:] + 129
    	30  UIKitCore                           0x000000011c072bdc __45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke + 733
    	31  Foundation                          0x0000000110f94f46 -[NSISEngine withBehaviors:performModifications:] + 110
    	32  UIKitCore                           0x000000011c072890 -[UIView(Hierarchy) _postMovedFromSuperview:] + 822
    	33  UIKitCore                           0x000000011c082ae5 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 1948
    	34  UIKitCore                           0x000000011bb3eb5c __30-[UIPickerView layoutSubviews]_block_invoke + 5320
    	35  UIKitCore                           0x000000011c07aec1 +[UIView(Animation) performWithoutAnimation:] + 90
    	36  UIKitCore                           0x000000011bb3d665 -[UIPickerView layoutSubviews] + 119
    	37  UIKitCore                           0x000000011bb32397 -[_UIDatePickerView layoutSubviews] + 101
    	38  UIKitCore                           0x000000011bb40d51 -[UIPickerView selectedRowInComponent:] + 45
    	39  UIKitCore                           0x000000011bb3bc09 -[UIPickerView _updateSelectedRows] + 135
    	40  UIKitCore                           0x000000011bb3bca3 -[UIPickerView didMoveToWindow] + 101
    	41  UIKitCore                           0x000000011c07ff9d -[UIView(Internal) _didMoveFromWindow:toWindow:] + 1817
    	42  UIKitCore                           0x000000011c07fb47 -[UIView(Internal) _didMoveFromWindow:toWindow:] + 707
    	43  UIKitCore                           0x000000011b5ffde4 -[UIControl _didMoveFromWindow:toWindow:] + 67
    	44  UIKitCore                           0x000000011c07fb47 -[UIView(Internal) _didMoveFromWindow:toWindow:] + 707
    	45  UIKitCore                           0x000000011c07fb47 -[UIView(Internal) _didMoveFromWindow:toWindow:] + 707
    	46  UIKitCore                           0x000000011c07fb47 -[UIView(Internal) _didMoveFromWindow:toWindow:] + 707
    	47  UIKitCore                           0x000000011c07fb47 -[UIView(Internal) _didMoveFromWindow:toWindow:] + 707
    	48  UIKitCore                           0x000000011c072996 __45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke + 151
    	49  Foundation                          0x0000000110f94f46 -[NSISEngine withBehaviors:performModifications:] + 110
    	50  UIKitCore                           0x000000011c072890 -[UIView(Hierarchy) _postMovedFromSuperview:] + 822
    	51  UIKitCore                           0x000000011c082ae5 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 1948
    	52  UIKitCore                           0x000000011bdb05d3 -[UITableView _addSubview:positioned:relativeTo:] + 124
    	53  UIKitCore                           0x000000011c01e24d -[UIScrollView(UIScrollViewInternal) _addContentSubview:atBack:] + 564
    	54  UIKitCore                           0x000000011bdb032c -[UITableView _addContentSubview:atBack:] + 256
    	55  UIKitCore                           0x000000011bdd3ca2 __53-[UITableView _configureCellForDisplay:forIndexPath:]_block_invoke + 2476
    	56  UIKitCore                           0x000000011c07aec1 +[UIView(Animation) performWithoutAnimation:] + 90
    	57  UIKitCore                           0x000000011bdd320f -[UITableView _configureCellForDisplay:forIndexPath:] + 237
    	58  UIKitCore                           0x000000011bde5ff3 -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] + 911
    	59  UIKitCore                           0x000000011bde6499 -[UITableView _createPreparedCellForGlobalRow:willDisplay:] + 73
    	60  UIKitCore                           0x000000011bdae654 -[UITableView _updateVisibleCellsNow:isRecursive:] + 2870
    	61  UIKitCore                           0x000000011bdc2c28 -[UITableView _visibleCellsUsingPresentationValues:] + 518
    	62  SkeletonView                        0x00000001152ba663 $sSo11UITableViewC08SkeletonB0E010subviewsToC0SaySo6UIViewCGvg + 35
    	63  SkeletonView                        0x00000001152ba5f5 $sSo11UITableViewC08SkeletonB0E010subviewsToC0SaySo6UIViewCGvgTo + 53
    	64  SkeletonView                        0x00000001152ba2d6 $sSo6UIViewC12SkeletonViewE21subviewsSkeletonablesSayABGvg + 54
    	65  SkeletonView                        0x00000001152ba255 $sSo6UIViewC12SkeletonViewE21subviewsSkeletonablesSayABGvgTo + 53
    	66  SkeletonView                        0x00000001152cb0d6 $sSo6UIViewC12SkeletonViewE02isB6ActiveSbvg + 182
    	67  SkeletonView                        0x00000001152b71b3 $sSo6UIViewC12SkeletonViewE32skeletonTraitCollectionDidChangeyySo07UITraitF0CSgF + 131
    	68  SkeletonView                        0x00000001152b7294 $sSo6UIViewC12SkeletonViewE32skeletonTraitCollectionDidChangeyySo07UITraitF0CSgFTo + 68
    	69  UIKitCore                           0x000000011c0144b2 -[UIScrollView traitCollectionDidChange:] + 73
    	70  UIKitCore                           0x000000011bdb9bd0 -[UITableView traitCollectionDidChange:] + 74
    	71  UIKitCore                           0x000000011c05f27a -[UIView _traitCollectionDidChangeInternal:] + 780
    	72  UIKitCore                           0x000000011c05f341 -[UIView _wrappedProcessTraitCollectionDidChange:forceNotification:] + 137
    	73  UIKitCore                           0x000000011c05f44e -[UIView _wrappedProcessTraitCollectionDidChange:forceNotification:] + 406
    	74  UIKitCore                           0x000000011bfc12b7 -[UIView(AdditionalLayoutSupport) _withUnsatisfiableConstraintsLoggingSuspendedIfEngineDelegateExists:] + 104
    	75  UIKitCore                           0x000000011c05f654 -[UIView _processDidChangeRecursivelyFromOldTraits:toCurrentTraits:forceNotification:] + 129
    	76  UIKitCore                           0x000000011c088e01 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1313
    	77  QuartzCore                          0x00000001130a5d22 -[CALayer layoutSublayers] + 173
    	78  QuartzCore                          0x00000001130aa9fc _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 396
    	79  QuartzCore                          0x00000001130b6d58 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 72
    	80  QuartzCore                          0x000000011302624a _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 328
    	81  QuartzCore                          0x000000011305d606 _ZN2CA11Transaction6commitEv + 610
    	82  QuartzCore                          0x0000000112f928a7 _ZN2CA7Display11DisplayLink14dispatch_itemsEyyy + 951
    	83  QuartzCore                          0x00000001130615a9 _ZL22display_timer_callbackP12__CFMachPortPvlS1_ + 297
    	84  CoreFoundation                      0x0000000117593266 __CFMachPortPerform + 150
    	85  CoreFoundation                      0x00000001175bf5e9 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 41
    	86  CoreFoundation                      0x00000001175bec4b __CFRunLoopDoSource1 + 459
    	87  CoreFoundation                      0x00000001175b91da __CFRunLoopRun + 2490
    	88  CoreFoundation                      0x00000001175b84d2 CFRunLoopRunSpecific + 626
    	89  GraphicsServices                    0x000000011e7602fe GSEventRunModal + 65
    	90  UIKitCore                           0x000000011bba9fc2 UIApplicationMain + 140
    	91  Koinz                               0x000000010d1adc3b main + 75
    	92  libdyld.dylib                       0x00000001190de541 start + 1
    	93  ???                                 0x0000000000000007 0x0 + 7
    

    Steps to reproduce the behavior

    1. Push a view controller with skeletonable views(isSkeletonable = true and the skeleton started and then it was stopped)
    2. Push on it a new view controller with a DateTimePicker with mode set to .Time Note: it works on ios13 but crashes on lower than ios13

    SkeletonView Environment

    SkeletonView version: 1.8.5 Xcode version: 11.3.1 Swift version: Swift 5

    🤔 possible bug 
    opened by minaMagedNaeem 30
  • Wrong skeleton view width on iPhone 8 plus

    Wrong skeleton view width on iPhone 8 plus

    What did you do?

    Just create a Table View with a skeletonable cell that has views that set its width with Leading and Trailing constraints with 0dp.

    What did you expect to happen?

    The skeleton view should match the screen width

    What happened instead?

    The skeleton view does not match the screen width in an iPhone 8 plus, but it does in an iPhone 5.

    iPhone 8 plus (wrong skeleton views width)

    iPhone 5 (correct skeleton views width)

    Steps to reproduce the behaviour

    Just start a new project, follow the steps to add skeletonable to table view and in the skeleton cell, add an imageview that should match cell width defining Leading and Trailing with 0 value.

    SkeletonView Environment

    SkeletonView version: 1.2 Xcode version: 9.4 Swift version: 4.1

    opened by eduardbosch 27
  • Support for using SkeletonView with RxDataSources

    Support for using SkeletonView with RxDataSources

    RxDataSources is a popular library to work with UITableView & UICollectionView using the reactive approach of RxSwift. Both projects would benefit greatly from the ability to use SkeletonView together with RxDataSources.

    💡 enhancement given up 
    opened by DevilDimon 27
  • Allow updating skeleton layers bounds and color

    Allow updating skeleton layers bounds and color

    Hi 🙂

    I've added the possibility to update the skeleton views bounds and colours without recreating them if not necessary.

    This will fix the issue https://github.com/Juanpe/SkeletonView/issues/64 by just adding this lines in the view controller:

    override func viewDidLayoutSubviews() {
        view.updateGradientSkeleton()
    }
    

    Also, if updateSkeleton is called without different skeleton layer type, it's recreated to match the new parameters.

    I've added the Example changes in a different commit to allow reverting them easily if you want to merge it as I've read that you don't accept changes to the example project right now.

    opened by eduardbosch 24
  • Fix: Skeleton loading for UICollectionView

    Fix: Skeleton loading for UICollectionView

    fixed Skeleton loading for UICollectionView by adding a prepareSkeleton function for UICollectionView (Fixes Issue: https://github.com/Juanpe/SkeletonView/issues/41)

    This will prepare the UICollectionView by laying out the cells so we can apply skeleton to them Before the cells used to be layed out after the animation stated causing the cells not to animate

    Also I have created an Example Project for the UICollectionView

    This will not break any of the existing functionality

    opened by kjoneandrei 19
  • Cannot round corners of Labels

    Cannot round corners of Labels

    Hi, I have labels and a text View and I try to use the properties

    ".linesCornerRadius = 10"

    and

    ".lastLineFillPercent = 100"

    but nothing change. IMG_1258

    What did you expect to happen?

    Is there any way to have the skeleton of labels and text views like this? 33116319-9d82fa54-cf64-11e7-9eab-3021f6d8d3a2

    Thanks.

    SkeletonView Environment

    SkeletonView version: 'https://github.com/Juanpe/SkeletonView.git', :branch => 'feature/Allow_updating_skeleton_layers' Xcode version: 10.2.1 Swift version: 4.0

    🐞 bug ✅ fixed awaiting user input 
    opened by hrosado20 18
  • Issue displaying skeleton view on all table view cells

    Issue displaying skeleton view on all table view cells

    ⚠️ Please fill out this template when filing an issue.

    🙏🏼 Please check if it already exists other issue related with yours.

    What did you do?

    Added skeleton view to my table view, implemented all of the required methods, and set the estimatedRowHeight as suggested by others.

    What did you expect to happen?

    I pass in a number for the numberOfRowsInSection method and it will display the skeleton view on that specified number of rows.

    What happened instead?

    Only the first three table view cells get covered by the skeleton view and the rest of the cells are just blank templates of the table view cell I created in the storyboard. Maybe the height of my table needs to be changed because it's using the estimated number of rows instead?

    Steps to reproduce the behaviour

    Create a table view and assign the number of rows in section and not every cell gets covered with a skeleton view.

    SkeletonView Environment

    SkeletonView version: Latest Xcode version: 9.4 Swift version: 4

    🤔 possible bug awaiting user input given up 
    opened by JordanPJamieson 18
  • SkeletonView does not update with AutoLayout

    SkeletonView does not update with AutoLayout

    What did you do?

    Tried to use isSkeletonable on a view laid out by auto-layout

    What did you expect to happen?

    The view would render in the auto-layout calculated position with shimmer

    What happened instead?

    The view did not show up

    OR

    If I set a background color, it shows up as background color, but no shimmer

    Steps to reproduce the behavior

    100% reproducible

    let fakeText = UIView()
    view.addSubview(fakeText)
    fakeText.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint(item: fakeText, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: CGFloat(16)).isActive = true
    NSLayoutConstraint(item: fakeText, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: CGFloat(16)).isActive = true
    NSLayoutConstraint(item: fakeText, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1, constant: CGFloat(16)).isActive = true
    
    // Trying to let autolayout determine the width the component does not render with a shimmer
    // NSLayoutConstraint(item: fakeText, attribute: .width, relatedBy: .equal, toItem: view, attribute: .width, multiplier: 1, constant: -CGFloat(16)).isActive = true
    
    // Setting the width statically it will render
    NSLayoutConstraint(item: fakeText, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: CGFloat(200)).isActive = true
    
    // If I set a background color, the component will render in the expected position with red background in the width relationship case.
    // This demonstrates it as not an auto-layout issue.
    // In the static width case, setting background has no effect.
    fakeText.backgroundColor = UIColor.red
    
    fakeText.isSkeletonable = true
    

    SkeletonView Environment

    SkeletonView version: 1.4.2 Xcode version: 10.1 Swift version: 4.2

    awaiting user input given up 
    opened by bpollock-vida 17
  • Collection View not showing skeleton for cells

    Collection View not showing skeleton for cells

    ⚠️ Please fill out this template when filing an issue.

    🙏🏼 Please check if it already exists other issue related with yours.

    What did you do?

    I am trying to show skeleton in collection view , i added an image view and a label to a cell set them both skeletonable and also set the cell to skeletonable and in viewDidLoad called showAnimatedGradientSkeleton() on collectionView.

    What did you expect to happen?

    I thought it should show the collectionView cells as skeleton.

    What happened instead?

    but it shows the whole collection view as skeleton and the cells dont appear, basically i just have a gray view with a gradient animation showing up.

    Steps to reproduce the behavior

    Add a collection view , add a cell to it , add imageview and label to cell set isSkeletonable to true for all of them. Implement the protocol and then run

    SkeletonView Environment

    SkeletonView version: 1.2.1 Xcode version: 9.3.1 Swift version: 4.0 simulator screen shot - iphone 7 - 2018-06-25 at 18 17 53

    given up 
    opened by samsidd 16
  • Custom Cell , it display the default cell before getting data from API

    Custom Cell , it display the default cell before getting data from API

    Hi , I have a Custom Cell and I notice when I open the app, the data will download from API but If I scroll down or if there is no internet I can see the default cell what I mean by default is what I did design in Storyboard

    this image will let you understand what I mean https://cl.ly/997eabe6cb92

    what I expect ? either all cell be SkeletonView or the user can not scrolling !

    🤔 possible bug 
    opened by X901 14
  • HideSkeleton doesn't hide all tableView cells

    HideSkeleton doesn't hide all tableView cells

    What did you do?

    I have this code that makes an API call and hides the skeleton view inside reloadUI

    // all this inside viewDidLoad ⬇️
    
    view.showAnimatedSkeleton()
    
    homeViewModel.getData { [weak self] _ in
          self?.reloadUI()
     }
    

    My reloadUI method

    fileprivate func reloadUI() {
            DispatchQueue.main.async {
                self.shouldAnimateSkeleton = false
                self.view.hideSkeleton()
                self.tableView.reloadData()
            }
        }
    

    What did you expect to happen?

    All cells should be hiding

    What happened instead?

    Instead, just some cells hid. Actually all the views you are looking at, are cells (where view.hideSkeleton() worked). The first one, the account one, the ranking one, the one with the dog. Simulator Screen Shot - iPhone 8 - 2020-06-25 at 11 32 01

    SkeletonView Environment

    SkeletonView version: 1.8.7 Xcode version: 11.5 Swift version: 5.2

    🐞 bug ✅ fixed awaiting user input given up 
    opened by OmarJalil 13
  • UITableView subviewsToSkeleton is empty when tableview is not showing in screen.

    UITableView subviewsToSkeleton is empty when tableview is not showing in screen.

    Description

    I have a page view, it has two child view, a tableView and a collectionView, both of then are isSkeletonable. when I call show skeletionView in tableview, then scroll to collectionView, then call hide skeletionView, collectionView works fine, skeletionView is disappeared, but when I scroll back to tableView, tableView cell's skeletionView is showing. In SubviewsSkeletonables.swift, I see tableView extension, why you check this window != nil when return subviewsToSkeleton? When tableView is not showing in screen, it's subviews' window is nil, its the bug reason. Why you didn't check window != nil in UICollectionView extension? I'm not sure it is a bug or a discussion.

    Can you parse why you want to check window != nil separately in UITableView extension.

    What type of issue is this? (place an x in one of the [ ])

    • [ ] bug
    • [ ] enhancement (feature request)
    • [ ] question
    • [ ] documentation related
    • [x] discussion

    Requirements (place an x in each of the [ ])

    • [x] I've read and understood the Contributing guidelines and have done my best effort to follow them.
    • [x] I've read and agree to the Code of Conduct.
    • [x] I've searched for any related issues and avoided creating a duplicate issue.

    Bug Report

    Filling out the following details about bugs will help us solve your issue sooner.

    SkeletonView Environment:

    **SkeletonView version:**1.30.4 **Xcode version:**13.3.1 **Swift version:**5.6.1

    Steps to reproduce:

    1.Show tableView in screen and call show skeletionView. 2.Scroll to next page then call hide skeletionView. 3.Scroll back to tableView, this skeletionView is still showing.

    Expected result:

    TableView's skeletionView should hide.

    Actual result:

    TableView's skeletionView is still showing.

    Attachments:

    Logs, screenshots, sample project, funny gif, etc.

    opened by SunZhiC 0
  • BUG cannot change text after view.showSkeleton()

    BUG cannot change text after view.showSkeleton()

    Description

    Describe your issue here.

    What type of issue is this? (place an x in one of the [ ])

    • [ X] bug
    • [ ] enhancement (feature request)
    • [ ] question
    • [ ] documentation related
    • [ ] discussion

    Requirements (place an x in each of the [ ])

    • [ x] I've read and understood the Contributing guidelines and have done my best effort to follow them.
    • [ x] I've read and agree to the Code of Conduct.
    • [ x] I've searched for any related issues and avoided creating a duplicate issue.

    Bug Report

    Filling out the following details about bugs will help us solve your issue sooner.

    SkeletonView Environment:

    SkeletonView version: **Xcode version:13 **Swift version:13

    Steps to reproduce:

    Please replace this with the steps to reproduce the behavior.

    1. Setup Skeleton View 2.Change text after setup skeleton view

    Expected result:

    text is changed

    Actual result:

    text is not changed

    Attachments:

    Logs, screenshots, sample project, funny gif, etc.

    opened by gilbertsubayastrapay 1
  • Fix `UIButton` prepare/recover and improve `.none` transition

    Fix `UIButton` prepare/recover and improve `.none` transition

    Summary

    The current RecoverableButtonViewState model only works for very simple buttons where just the title is defined. This means that on more complex scenarios buttons aren't properly "prepared" to enter skeleton mode and remain visible for a while until they are covered with the skeleton layer, where the remaining elements are already in skeleton mode (e.g. labels). Furthermore, when configuring styling on buttons they reorder subviews/sublayers so that the skeleton layer didn't always show on top.

    Same thing is true if layer.borderColor is non-nil - the border can be seen for a while until the skeleton layer covers it.

    Additionally, using SkeletonTransitionStyle.none would still allow animations to be performed if any change done in prepareViewForSkeleton triggers a layout pass. This is especially noticeable when pushing a screen in a navigation controller and enabling skeleton mode in viewDidLoad.

    Changes

    • Add state, attributedTitle, titleColor, image and backgroundImage to RecoverableButtonViewState, clear them on prepareViewForSkeleton and recover them on recoverViewState (according to state).

    • Set the SkeletonLayer.maskLayer's zPosition to Float.greatestFiniteMagnitude so that it always sits on top of the view's content.

    • Add borderColor to RecoverableViewState, clear it on prepareViewForSkeleton and recover it in recoverViewState.

    • Wrap startTransition block in a UIView.performWithoutAnimation when SkeletonTransitionStyle.none.

    • Remove DispatchQueue.main usage in CALayer animation extensions, as they cause animations to become out of sync with other UI events/animations like navigation pushes and/or other elements that are animated while skeleton is being shown/hidden. Removing them ensures better animation ordering/orchestration.

    Demo

    https://user-images.githubusercontent.com/1391324/202869650-e0f87962-f7a7-451e-b218-64d54b9cb86b.mov

    https://user-images.githubusercontent.com/1391324/202869594-e5bc47c6-0687-4fcc-9c54-103919b6a1d4.mov

    Requirements

    opened by p4checo 0
  • Custom headers/footers

    Custom headers/footers

    Description

    Need table delegate methods to work with custom headers and footers:

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        
    }
    
    func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
        
    }
    

    P.S. Or tell me how to do it right. Thank you!

    What type of issue is this?

    • [ ] bug
    • [ ] enhancement (feature request)
    • [ ] question
    • [ ] documentation related
    • [x] discussion

    Requirements

    • [ ] I've read and understood the Contributing guidelines and have done my best effort to follow them.
    • [ ] I've read and agree to the Code of Conduct.
    • [x] I've searched for any related issues and avoided creating a duplicate issue.

    SkeletonView Environment:

    SkeletonView version: Last Xcode version: 14.0 Swift version: 5.6+

    Expected result:

    Ability to return a custom header with filled data

    Actual result:

    Returns the default header without data. Skeleton works as it should.

    opened by RSafargalin 0
  • UILabels not respecting constraints

    UILabels not respecting constraints

    Hello, I'm fetching data from API, and while it's happening, the skeleton looks like this:

    Simulator Screen Shot - iPhone 13 - 2022-10-20 at 17 13 25

    When it should look like this: Simulator Screen Shot - iPhone 13 - 2022-10-20 at 17 12 41

    The second image happens because I'm not stopping skeleton animation when the data from API is received.

    Any tips on what might be wrong?

    opened by hiagochagas 2
Releases(1.30.4)
Owner
Juanpe Catalán
iOS Developer at Domestika
Juanpe Catalán
A port of SwiftUILab's Advanced Animations that also supports macOS

SwiftUILab Advanced Animations on the Mac as well A port of SwiftUILab's Advanced Animations that also supports macOS Here's the Ghist of the original

Mihaela Mihaljevic Jakic 10 Jan 2, 2023
An elegant and flexible tweening library for iOS and tvOS.

PMTween is an elegant and flexible tweening library for Objective-C, currently supporting the iOS and tvOS platforms. It offers sensible default funct

Brett Walker 349 Nov 25, 2022
A powerful, elegant, and modular animation library for Swift.

MotionMachine provides a modular, powerful, and generic platform for manipulating values, whether that be animating UI elements or interpolating prope

Brett Walker 387 Dec 9, 2022
SwiftUI iOS application allowing users to create profiles and meet and chat with people

FindR SwiftUI iOS application allowing users to create profiles and meet and cha

Devang Papinwar 3 Dec 7, 2022
Elegant SVG animation kit for swift

Elephant This is SVG animation presentation kit for iOS. Example You can run example app. Please open Example-iOS/Elephant-iOS.xcworkspace! Usage You

Kazumasa Shimomura 127 Dec 14, 2022
A radical & elegant animation library for iOS.

Installation • Usage • Debugging • Animatable Properties • License Dance is a powerful and straightforward animation framework built upon the new UIVi

Saoud Rizwan 648 Dec 14, 2022
☠️SkeletonUI aims to bring an elegant, declarative syntax to skeleton loading animations.

SkeletonUI aims to bring an elegant, declarative syntax to skeleton loading animations. Get rid of loading screens or spinners and start using skeletons to represent final content shapes.

Carlos Solana Martínez 551 Dec 18, 2022
Accessbility workshop by hacking with swift. Make every app more usefull for the all the users

Accessibility Accessbility workshop by hacking with swift. Make every app more u

Pavel Surový 0 Dec 26, 2021
Apple's SwiftUI Essentials Series. An iOS app that helps users manage their daily scrums.

Scrumdinger (Work in progress) Apple's SwiftUI Essentials Series An iOS app that helps users manage their daily scrums. To help keep scrums short and

Vinícius Moreira 1 Feb 14, 2022
Design-system-demo - This example code is bare-bones to show you what this framework can do

Basic Style Dictionary This example code is bare-bones to show you what this fra

Tylen St Hilaire 0 Feb 3, 2022
null 1 Jan 26, 2022
Letters animation allows you to click on different letters and accordingly it will animate letters in a cool way. It has a very attractive UI and is very easy to use.

Letters Animation Cool Letters Animation in iOS written in Swift. Preview Table of content :- Description How to add in your project Requirement Licen

MindInventory 31 Oct 4, 2022
Numbers animation allows you to click on different numbers and accordingly it will animate numbers in a cool way. It has a very attractive UI and is very easy to use.

Numbers Animation Cool Numbers Animation in iOS written in Swift. Preview Table of content :- Description How to add in your project Requirement Licen

MindInventory 31 Oct 4, 2022
Reading animation allows you to click on the different page numbers and accordingly it will animate page changes in a cool way. It has a very attractive UI and is very easy to use.

Reading Animation Cool Reading Animation in iOS written in Swift. Preview Table of content :- Description How to add in your project Requirement Licen

MindInventory 42 Oct 4, 2022
DaisyChain is a micro framework which makes UIView animations chaining dead simple.

DaisyChain DaisyChain is a micro framework which makes UIView animations chaining dead simple. It uses the exact same interface you are familiars with

Ali Karagoz 31 Nov 3, 2022
This library is for adding animation to iOS tabbar items, which is inherited from UITabBarController.

This library is for adding animation to iOS tabbar items, which is inherited from UITabBarController. Installation Just add the Sources folder to your

Yuşa Doğru 162 Jan 6, 2023
A very basic calculator in swiftUI which uses NSExpression

Calculatrice-Avec-NS Une calculatrice très basic en swiftUI qui utilise NSExpres

Bilal Larose 0 Dec 19, 2021
RetroBubbleText - A simple effect used in Retrogram which renders text with a fun bubble-style outline in SwiftUI

Retro Bubble Text This is a simple effect used in Retrogram which renders text w

Simeon Saëns 5 Aug 29, 2022