Compare commits

...

27 Commits

Author SHA1 Message Date
Juanpe Catalán 8448a53b79 feat: updated podspec file 2017-11-22 20:56:09 +01:00
Juanpe Catalán 42f0886c77 feat: update README file to include latest changes 2017-11-22 20:54:12 +01:00
Juanpe Catalán 4e63a2a1e3 feat: added filling percent to last line in elements with multilines 2017-11-22 20:39:37 +01:00
Juanpe Catalán 9523446c53 fix: issue #14 2017-11-22 20:36:12 +01:00
Juanpe Catalán dd66df733c Merge pull request #11 from jontelang/multiline-last-line-shorter
feat: making the last line in a skeleton-text-view shorter [step 1]
2017-11-22 19:44:43 +01:00
Juanpe Catalán 8021f1260b feat: updated README file to add a new demo app image 2017-11-22 09:16:50 +01:00
Juanpe Catalán 3f3b4f498b feat: updated example project 2017-11-22 09:13:58 +01:00
Juanpe Catalán 6a07b423a9 feat: added example app to show a rounded view 2017-11-22 09:12:16 +01:00
Jonathan Winger Lang 6bd03081d6 Add config to enable / disable making the last line in a skeleton-text-view shorter 2017-11-20 18:29:02 +07:00
Juanpe Catalán 8f891f02b0 feat: upgrade framework version 2017-11-18 19:26:03 +01:00
Juanpe Catalán 9ef6d3e48d feat: change signature makeSlidingAnimation method 2017-11-18 19:23:17 +01:00
Juanpe Catalán 1862179c44 feat: updated README file to include SkeletonAnimationBuilder 2017-11-18 19:17:57 +01:00
Juanpe Catalán fc66cc5c0b feat: added SkeletonAnimationBuilder to create layer animations 2017-11-18 18:36:07 +01:00
Juanpe Catalán ef48129ef0 feat: updated README file 2017-11-17 19:47:50 +01:00
Juanpe Catalán e24969220f feat: added table of contents 2017-11-17 19:27:53 +01:00
Juanpe Catalán e123bed684 feat: added share link in README file 2017-11-16 21:44:15 +01:00
Juanpe Catalán dbb570dab7 feat: rename GradientVector to GradientPoint 2017-11-16 21:41:22 +01:00
Juanpe Catalán cdbb4dc6a0 feat: added GradientDirection 2017-11-16 18:53:25 +01:00
Juanpe b6c1c89645 feat: added travis shield on README file 2017-11-15 12:56:41 +01:00
Juanpe f583fe3316 feat: added travis file to CI 2017-11-15 12:39:39 +01:00
Juanpe befa9b0442 feat: update twitter account 2017-11-15 12:21:56 +01:00
Juanpe Catalán 07075f02d1 Update issue_template.md 2017-11-13 15:56:40 +01:00
Juanpe Catalán b8f52e7ce5 Create issue_template.md 2017-11-13 15:54:32 +01:00
Juanpe Catalán 9e3396d934 feat: updated README file 2017-11-10 15:50:31 +01:00
Juanpe Catalán 5a68d03af4 feat: Updated podspec file 2017-11-10 15:49:42 +01:00
Juanpe Catalán 91b4ac2bf6 Merge pull request #2 from LeonardoCardoso/master
feat: Change some private keywords, to be Swift 3 compatible
2017-11-10 15:43:15 +01:00
Leonardo f6d28d9492 fix: accessibility level 2017-11-09 13:26:26 +01:00
28 changed files with 278 additions and 39 deletions
+23
View File
@@ -0,0 +1,23 @@
⚠️ Please fill out this template when filing an issue.
### What did you do?
*Please replace this with what you did.*
### What did you expect to happen?
*Please replace this with what you expected to happen.*
### What happened instead?
*Please replace this with of what happened instead.*
### Steps to reproduce the behavior
*Please replace this with the steps to reproduce the behavior.*
### SkeletonView Environment
**SkeletonView version:**
**Xcode version:**
**Swift version:**
Executable
+6
View File
@@ -0,0 +1,6 @@
language: objective-c
osx_image: xcode9
script:
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-iOS -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=11'
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonViewExample -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=11'
after_success:
Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0.1</string>
<string>1.0.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.1</string>
<string>1.0.4</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
+5 -1
View File
@@ -96,6 +96,9 @@
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="lastLineFillPercent">
<integer key="value" value="40"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="avatar" translatesAutoresizingMaskIntoConstraints="NO" id="nMj-pU-5wJ">
@@ -195,6 +198,7 @@
</view>
<navigationItem key="navigationItem" id="BEI-dU-kr2"/>
<connections>
<outlet property="avatarImage" destination="nMj-pU-5wJ" id="9fa-Z7-vYi"/>
<outlet property="colorSelectedView" destination="iGp-rp-t1d" id="0Zm-9d-jRU"/>
<outlet property="skeletonTypeSelector" destination="xOL-Sq-r4i" id="yTr-8L-I4Y"/>
<outlet property="switchAnimated" destination="vz0-qg-GcZ" id="d2R-8x-lRb"/>
@@ -203,7 +207,7 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-594.39999999999998" y="85.007496251874073"/>
<point key="canvasLocation" x="-482" y="-6"/>
</scene>
</scenes>
<resources>
+8
View File
@@ -14,6 +14,14 @@ let colors = [(UIColor.turquoise,"turquoise"), (UIColor.emerald,"emerald"), (UIC
class ViewController: UIViewController {
@IBOutlet weak var tableview: UITableView!
@IBOutlet weak var avatarImage: UIImageView! {
didSet {
avatarImage.layer.cornerRadius = avatarImage.frame.width/2
avatarImage.layer.masksToBounds = true
}
}
@IBOutlet weak var colorSelectedView: UIView! {
didSet {
colorSelectedView.layer.cornerRadius = 5
+84 -21
View File
@@ -1,6 +1,9 @@
![](Assets/header.jpg)
<p align="center">
<a href="https://travis-ci.org/Juanpe/SkeletonView">
<img src="https://img.shields.io/travis/Juanpe/SkeletonView.svg">
</a>
<img src="https://img.shields.io/badge/Swift-4.0-orange.svg" />
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
@@ -8,14 +11,39 @@
<a href="https://github.com/Carthage/Carthage">
<img src="https://img.shields.io/badge/carthage-compatible-4BC51D.svg?style=flat" alt="Carthage" />
</a>
<a href="https://twitter.com/juanpe_catalan">
<img src="https://img.shields.io/badge/contact-@juanpe_catalan-blue.svg?style=flat" alt="Twitter: @juanpe_catalan" />
<a href="https://twitter.com/JuanpeCatalan">
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
</a>
<a href="https://opensource.org/licenses/MIT">
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License" />
</a>
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License" />
</a>
<a href="https://twitter.com/intent/tweet?text=Wow%20This%20library%20is%20awesome:&url=https%3A%2F%2Fgithub.com%2FJuanpe%2FSkeletonView">
<img src="https://img.shields.io/twitter/url/https/github.com/Juanpe/SkeletonView.svg?style=social" alt="License" />
</a>
</p>
Today almost all apps have async processes, such as Api requests, long running processes, etc. And 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 to which contents he is waiting.
Enjoy it! 🙂
* [Features](#-features)
* [Requirements](#-supported-os--sdk-versions)
* [Example Project](#-example)
* [Installation](#-installation)
* [Cocoapods](#using-cocoapods)
* [Carthage](#using-carthage)
* [How to use](#-how-to-use)
* [Collections](#-collections)
* [Multiline text](#-multiline-text)
* [Custom colors](#-custom-colors)
* [Custom animations](#-custom-animations)
* [Hierarchy](#-hierarchy)
* [Documentation](#-documentation)
* [Contributed](#-contributed)
* [Author](#-author)
* [License](#-license)
## 🌟 Features
@@ -31,23 +59,13 @@
### 📋 Supported OS & SDK Versions
* iOS 9.0+
* Swift 4
### 🎤 Introduction
Today almost all apps have async processes, such as Api requests, long running processes, etc. And 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 to which contents he is waiting.
Enjoy it! 🙂
###### Project generated with [SwiftPlate](https://github.com/JohnSundell/SwiftPlate)
* Swift 4 (Swift 3 compatible)
### 🔮 Example
To run the example project, clone the repo and run `SkeletonViewExample` target.
![](Assets/demoApp.png)
![](Assets/demoApp2.png)
## 📲 Installation
@@ -144,7 +162,7 @@ public protocol SkeletonTableViewDataSource: UITableViewDataSource {
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
}
```
As you can see, this protocol inherits from ```UITableViewDataSource``, so you can replace this protocol with the skeleton protocol.
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:
@@ -174,18 +192,32 @@ There is only one method you need to implement to let Skeleton know the cell ide
### 📰 Multiline text
![](Assets/multilines.png)
![](Assets/multilines2.png)
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.
**NEW** Now, you can set the filling percent of the last line. **Default: 80%**
To modify the percent **using code**, set the property:
```swift
descriptionTextView.lastLineFillPercent = 50
```
Or, if you prefer use **IB/Storyboard**:
![](Assets/lastline_storyboard.png)
### 🎨 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**
``` swift
view.showSkeleton(usingColor: UIColor.midnightBlue) // Solid
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**
``` swift
@@ -193,7 +225,9 @@ let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
view.showGradientSkeleton(usingGradient: gradient) // Gradient
```
```SkeletonView``` features 20 flat colors 🤙🏼:
Besides, ```SkeletonView``` features 20 flat colors 🤙🏼
```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...```
![](Assets/flatcolors.png)
###### Image captured from website [https://flatuicolors.com](https://flatuicolors.com)
@@ -222,6 +256,33 @@ view.showAnimatedSkeleton { (layer) -> CAAnimation in
}
```
**NEW** 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).
```swift
// 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 this cases:
| Direction | Preview
|------- | -------
| .leftRight | ![](Assets/sliding_left_to_right.gif)
| .rightLeft | ![](Assets/sliding_right_to_left.gif)
| .topBottom | ![](Assets/sliding_top_to_bottom.gif)
| .bottomTop | ![](Assets/sliding_bottom_to_top.gif)
| .topLeftBottomRight | ![](Assets/sliding_topLeft_to_bottomRight.gif)
| .bottomRightTopLeft | ![](Assets/sliding_bottomRight_to_topLeft.gif)
> **😉 TRICK!**
Exist another way to create sliding animations, just using this shortcut:
>>```let animation = GradientDirection.leftToRight.slidingAnimation()```
### 👨‍👧‍👦 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.
@@ -250,9 +311,11 @@ This is an open source project, so feel free to contribute. How?
See [all contributors](https://github.com/Juanpe/SkeletonView/graphs/contributors)
###### Project generated with [SwiftPlate](https://github.com/JohnSundell/SwiftPlate)
## 👨🏻‍💻 Author
[1.1]: http://i.imgur.com/tXSoThF.png
[1]: http://www.twitter.com/juanpe_catalan
[1]: http://www.twitter.com/JuanpeCatalan
* Juanpe Catalán [![alt text][1.1]][1]
+2 -2
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SkeletonView"
s.version = "1.0.1"
s.version = "1.0.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.
@@ -9,7 +9,7 @@ Pod::Spec.new do |s|
s.homepage = "https://github.com/Juanpe/SkeletonView"
s.license = { :type => "MIT", :file => "LICENSE" }
s.author = { "Juanpe Catalán" => "juanpecm@gmail.com" }
s.social_media_url = "https://twitter.com/juanpe_catalan"
s.social_media_url = "https://twitter.com/JuanpeCatalan"
s.ios.deployment_target = "9.0"
s.source = { :git => "https://github.com/Juanpe/SkeletonView.git", :tag => s.version.to_s }
s.source_files = "Sources/**/*"
+4
View File
@@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
8933C7851EB5B820000D00A4 /* SkeletonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* SkeletonView.swift */; };
F51DE1091FBF70A70037919A /* SkeletonAnimationBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */; };
F5307E2C1FAF6BC900EE67C5 /* SkeletonGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2B1FAF6BC900EE67C5 /* SkeletonGradient.swift */; };
F5307E2E1FB0E5E400EE67C5 /* UIView+Frame.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2D1FB0E5E400EE67C5 /* UIView+Frame.swift */; };
F5307E301FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2F1FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift */; };
@@ -61,6 +62,7 @@
52D6D97C1BEFF229002C0205 /* SkeletonView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SkeletonView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8933C7841EB5B820000D00A4 /* SkeletonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkeletonView.swift; sourceTree = "<group>"; };
AD2FAA261CD0B6D800659CF4 /* SkeletonView.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = SkeletonView.plist; sourceTree = "<group>"; };
F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonAnimationBuilder.swift; sourceTree = "<group>"; };
F5307E2B1FAF6BC900EE67C5 /* SkeletonGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonGradient.swift; sourceTree = "<group>"; };
F5307E2D1FB0E5E400EE67C5 /* UIView+Frame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Frame.swift"; sourceTree = "<group>"; };
F5307E2F1FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonDefaultConfig.swift; sourceTree = "<group>"; };
@@ -140,6 +142,7 @@
F5307E331FB1068500EE67C5 /* Collections */,
F5307E341FB106A500EE67C5 /* Extensions */,
F5307E351FB106BF00EE67C5 /* Helpers */,
F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */,
F5307E2F1FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift */,
F5307E2B1FAF6BC900EE67C5 /* SkeletonGradient.swift */,
F5F899E81FAB9D2B002E8FDA /* SkeletonLayer.swift */,
@@ -349,6 +352,7 @@
F5307E3B1FB123C100EE67C5 /* ContainsMultilineText.swift in Sources */,
F5F899E91FAB9D2B002E8FDA /* SkeletonLayer.swift in Sources */,
F5307E2E1FB0E5E400EE67C5 /* UIView+Frame.swift in Sources */,
F51DE1091FBF70A70037919A /* SkeletonAnimationBuilder.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
+8 -2
View File
@@ -36,10 +36,16 @@ extension CALayer {
return sublayers?.filter { $0.name == CALayer.skeletonSubLayersName } ?? [CALayer]()
}
func addMultilinesLayers(lines: Int, type: SkeletonType) {
func addMultilinesLayers(lines: Int, type: SkeletonType, lastLineFillPercent: Int) {
let numberOfSublayers = calculateNumLines(maxLines: lines)
for index in 0..<numberOfSublayers {
let layer = SkeletonLayerFactory().makeMultilineLayer(withType: type, for: index, width: Int(bounds.width))
var width = bounds.width
if index == numberOfSublayers-1 && numberOfSublayers != 1 {
width = width * CGFloat(lastLineFillPercent)/100;
}
let layer = SkeletonLayerFactory().makeMultilineLayer(withType: type, for: index, width: width)
addSublayer(layer)
}
}
@@ -44,7 +44,7 @@ extension UIView {
set { objc_setAssociatedObject(self, &AssociatedKeys.status, newValue, AssociationPolicy.retain.objc) }
}
private var skeletonable: Bool! {
fileprivate var skeletonable: Bool! {
get { return objc_getAssociatedObject(self, &AssociatedKeys.skeletonable) as? Bool ?? false }
set { objc_setAssociatedObject(self, &AssociatedKeys.skeletonable, newValue, AssociationPolicy.retain.objc) }
}
+35 -1
View File
@@ -8,17 +8,51 @@
import UIKit
private enum AssociatedKeys {
static var lastLineFillingPercent = "lastLineFillingPercent"
}
protocol ContainsMultilineText {
var numLines: Int { get }
var lastLineFillingPercent: Int { get }
}
extension ContainsMultilineText {
var numLines: Int { return 0 }
}
public extension UILabel {
@IBInspectable
var lastLineFillPercent: Int {
get { return lastLineFillingPercent }
set { lastLineFillingPercent = min(newValue, 100) }
}
}
public extension UITextView {
@IBInspectable
var lastLineFillPercent: Int {
get { return lastLineFillingPercent }
set { lastLineFillingPercent = min(newValue, 100) }
}
}
extension UILabel: ContainsMultilineText {
var numLines: Int {
return numberOfLines
}
var lastLineFillingPercent: Int {
get { return objc_getAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent) as? Int ?? SkeletonDefaultConfig.multilineLastLineFillPercent }
set { objc_setAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent, newValue, AssociationPolicy.retain.objc) }
}
}
extension UITextView: ContainsMultilineText {
var lastLineFillingPercent: Int {
get { return objc_getAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent) as? Int ?? SkeletonDefaultConfig.multilineLastLineFillPercent }
set { objc_setAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent, newValue, AssociationPolicy.retain.objc) }
}
}
extension UITextView: ContainsMultilineText {}
@@ -15,12 +15,14 @@ protocol PrepareForSkeleton {
extension UILabel: PrepareForSkeleton {
func prepareViewForSkeleton() {
text = nil
resignFirstResponder()
}
}
extension UITextView: PrepareForSkeleton {
func prepareViewForSkeleton() {
text = nil
resignFirstResponder()
}
}
+85
View File
@@ -0,0 +1,85 @@
//
// SkeletonAnimationBuilder.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 17/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
typealias GradientAnimationPoint = (from: CGPoint, to: CGPoint)
public enum GradientDirection {
case leftRight
case rightLeft
case topBottom
case bottomTop
case topLeftBottomRight
case bottomRightTopLeft
public func slidingAnimation(duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation {
return SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: self, duration: duration)
}
var startPoint: GradientAnimationPoint {
switch self {
case .leftRight:
return (from: CGPoint(x:-1, y:0.5), to: CGPoint(x:1, y:0.5))
case .rightLeft:
return (from: CGPoint(x:1, y:0.5), to: CGPoint(x:-1, y:0.5))
case .topBottom:
return (from: CGPoint(x:0.5, y:-1), to: CGPoint(x:0.5, y:1))
case .bottomTop:
return (from: CGPoint(x:0.5, y:1), to: CGPoint(x:0.5, y:-1))
case .topLeftBottomRight:
return (from: CGPoint(x:-1, y:-1), to: CGPoint(x:1, y:1))
case .bottomRightTopLeft:
return (from: CGPoint(x:1, y:1), to: CGPoint(x:-1, y:-1))
}
}
var endPoint: GradientAnimationPoint {
switch self {
case .leftRight:
return (from: CGPoint(x:0, y:0.5), to: CGPoint(x:2, y:0.5))
case .rightLeft:
return ( from: CGPoint(x:2, y:0.5), to: CGPoint(x:0, y:0.5))
case .topBottom:
return ( from: CGPoint(x:0.5, y:0), to: CGPoint(x:0.5, y:2))
case .bottomTop:
return ( from: CGPoint(x:0.5, y:2), to: CGPoint(x:0.5, y:0))
case .topLeftBottomRight:
return ( from: CGPoint(x:0, y:0), to: CGPoint(x:2, y:2))
case .bottomRightTopLeft:
return ( from: CGPoint(x:2, y:2), to: CGPoint(x:0, y:0))
}
}
}
public class SkeletonAnimationBuilder {
public init() {
}
public func makeSlidingAnimation(withDirection direction: GradientDirection, duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation {
return { layer in
let startPointAnim = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.startPoint))
startPointAnim.fromValue = NSValue(cgPoint: direction.startPoint.from)
startPointAnim.toValue = NSValue(cgPoint: direction.startPoint.to)
let endPointAnim = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.endPoint))
endPointAnim.fromValue = NSValue(cgPoint: direction.endPoint.from)
endPointAnim.toValue = NSValue(cgPoint: direction.endPoint.to)
let animGroup = CAAnimationGroup()
animGroup.animations = [startPointAnim, endPointAnim]
animGroup.duration = duration
animGroup.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
animGroup.repeatCount = .infinity
return animGroup
}
}
}
+4 -2
View File
@@ -14,7 +14,9 @@ public enum SkeletonDefaultConfig {
public static let gradient = SkeletonGradient(baseColor: tintColor)
public static let multilineHeight = 15
public static let multilineHeight: CGFloat = 15
public static let multilineSpacing = 10
public static let multilineSpacing: CGFloat = 10
public static let multilineLastLineFillPercent = 70
}
+4 -4
View File
@@ -14,12 +14,12 @@ class SkeletonLayerFactory {
return SkeletonLayer(withType: type, usingColors: colors, andSkeletonHolder: holder)
}
func makeMultilineLayer(withType type: SkeletonType, for index: Int, width: Int) -> CALayer {
let spaceRequitedForEachLine = SkeletonDefaultConfig.multilineHeight + SkeletonDefaultConfig.multilineSpacing
func makeMultilineLayer(withType type: SkeletonType, for index: Int, width: CGFloat) -> CALayer {
let spaceRequiredForEachLine = SkeletonDefaultConfig.multilineHeight + SkeletonDefaultConfig.multilineSpacing
let layer = type.layer
layer.anchorPoint = .zero
layer.name = CALayer.skeletonSubLayersName
layer.frame = CGRect(x: 0, y: (index * spaceRequitedForEachLine), width: width, height: SkeletonDefaultConfig.multilineHeight)
layer.frame = CGRect(x: 0.0, y: CGFloat(index) * spaceRequiredForEachLine, width: width, height: SkeletonDefaultConfig.multilineHeight)
return layer
}
}
@@ -77,7 +77,7 @@ struct SkeletonLayer {
func addMultilinesIfNeeded() {
guard let multiLineView = holder as? ContainsMultilineText else { return }
maskLayer.addMultilinesLayers(lines: multiLineView.numLines, type: type)
maskLayer.addMultilinesLayers(lines: multiLineView.numLines, type: type, lastLineFillPercent: multiLineView.lastLineFillingPercent)
}
}
+5 -3
View File
@@ -28,6 +28,7 @@ public extension UIView {
func hideSkeleton(reloadDataAfter reload: Bool = true) {
removeDummyDataSourceIfNeeded(reloadAfter: reload)
isUserInteractionEnabled = true
recursiveSearch(inArray: subviewsSkeletonables,
leafBlock: { removeSkeletonLayer() },
recursiveBlock: {
@@ -57,6 +58,7 @@ extension UIView {
recursiveSearch(inArray: subviewsSkeletonables,
leafBlock: {
guard !isSkeletonActive else { return }
isUserInteractionEnabled = false
(self as? PrepareForSkeleton)?.prepareViewForSkeleton()
addSkeletonLayer(withType: type, usingColors: colors, animated: animated, animation: animation)
}) {
@@ -64,14 +66,14 @@ extension UIView {
}
}
private func startSkeletonLayerAnimationBlock(_ anim: SkeletonLayerAnimation? = nil) -> VoidBlock {
fileprivate func startSkeletonLayerAnimationBlock(_ anim: SkeletonLayerAnimation? = nil) -> VoidBlock {
return {
guard let layer = self.skeletonLayer else { return }
layer.start(anim)
}
}
private var stopSkeletonLayerAnimationBlock: VoidBlock {
fileprivate var stopSkeletonLayerAnimationBlock: VoidBlock {
return {
guard let layer = self.skeletonLayer else { return }
layer.stopAnimation()
@@ -105,7 +107,7 @@ extension UIStackView {
extension UIView {
func addSkeletonLayer(withType type: SkeletonType, usingColors colors: [UIColor], animated: Bool, animation: SkeletonLayerAnimation? = nil) {
func addSkeletonLayer(withType type: SkeletonType, usingColors colors: [UIColor], gradientDirection direction: GradientDirection? = nil, animated: Bool, animation: SkeletonLayerAnimation? = nil) {
self.skeletonLayer = SkeletonLayerFactory().makeLayer(withType: type, usingColors: colors, andHolder: self)
layer.insertSublayer(skeletonLayer!.contentLayer, at: UInt32.max)
if animated { skeletonLayer!.start(animation) }