Compare commits

..

15 Commits

Author SHA1 Message Date
Juanpe Catalán 988284f78c feat: Update README file 2018-02-02 18:09:04 +01:00
Juanpe Catalán c41c2f0d41 feat: Update version number 2018-02-02 18:02:42 +01:00
Juanpe Catalán 0fcbdfa575 feat: Change UITableView in the example project
Now, UITableView has cells with automatic dimension
2018-02-02 18:00:48 +01:00
Juanpe Catalán db1e789aa5 feat: Support UITableView with AutomaticDimension 2018-02-02 17:57:50 +01:00
Juanpe Catalán 53adf155b9 feat: Add to README file the next steps 2017-12-28 12:01:43 +01:00
Juanpe Catalán cfd8d15240 feat: included CHANGELOG file 2017-11-24 11:28:39 +01:00
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
26 changed files with 251 additions and 71 deletions
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

Executable
+30
View File
@@ -0,0 +1,30 @@
# Change Log
All notable changes to this project will be documented in this file
## [Filled or not (1.0.4)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.4)
### New
- You can set the filling percent of the last line in multiline elements (thanks @jontelang!)
### Bug fixes
- Solved issue [#14](https://github.com/Juanpe/SkeletonView/issues/14). You could edit text views with skeleton active.
## [In all directions (1.0.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.3)
### New
- Create ```SkeletonAnimationBuilder```, to facilitate the creation of layer animations.
```GradientDirection``` enum.
## [Retro (1.0.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.2)
### New
- Change some private keywords, to be Swift 3 compatible
## [Early bird bug (1.0.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.2)
### Bug fixes
- It was not removing the skeleton layer
## [Starter (1.0)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0)
- First release
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0.3</string>
<string>1.0.5</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.3</string>
<string>1.0.5</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>
+14 -1
View File
@@ -13,7 +13,20 @@ let colors = [(UIColor.turquoise,"turquoise"), (UIColor.emerald,"emerald"), (UIC
class ViewController: UIViewController {
@IBOutlet weak var tableview: UITableView!
@IBOutlet weak var tableview: UITableView! {
didSet {
tableview.rowHeight = UITableViewAutomaticDimension
tableview.estimatedRowHeight = 120.0
}
}
@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
+29 -2
View File
@@ -41,6 +41,7 @@ Enjoy it! 🙂
* [Custom animations](#-custom-animations)
* [Hierarchy](#-hierarchy)
* [Documentation](#-documentation)
* [Next steps](#-next-steps)
* [Contributed](#-contributed)
* [Author](#-author)
* [License](#-license)
@@ -65,7 +66,7 @@ Enjoy it! 🙂
To run the example project, clone the repo and run `SkeletonViewExample` target.
![](Assets/demoApp.png)
![](Assets/demoApp2.png)
## 📲 Installation
@@ -189,14 +190,29 @@ There is only one method you need to implement to let Skeleton know the cell ide
}
```
> **IMPORTANT!**
> If you are using resizable cells (`tableView.rowHeight = UITableViewAutomaticDimension` ), it's mandatory define the `estimatedRowHeight`.
### 📰 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: 70%**
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.
@@ -291,6 +307,17 @@ Because an image is worth a thousand words:
### 📚 Documentation
Coming soon...😅
## 📬 Next steps
* [x] Set the filling percent of the last line in multiline elements
* [x] Add more gradient animations
* [x] Supported resizable cells
* [ ] CollectionView compatible
* [ ] Add recovery state
* [ ] Custom collections compatible
* [ ] Add animations when it shows/hides the skeletons
* [ ] MacOS and WatchOS compatible
## ❤️ Contributed
This is an open source project, so feel free to contribute. How?
- Open an [issue](https://github.com/Juanpe/SkeletonView/issues/new).
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SkeletonView"
s.version = "1.0.3"
s.version = "1.0.5"
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.
+12
View File
@@ -18,6 +18,9 @@
F5307E3B1FB123C100EE67C5 /* ContainsMultilineText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */; };
F5307E411FB3B84500EE67C5 /* SkeletonView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* SkeletonView.framework */; };
F5307E421FB3B84500EE67C5 /* SkeletonView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* SkeletonView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
F54CF5E42024CEB000330B0D /* UIView+CollectionSkeleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F54CF5E12024CEAF00330B0D /* UIView+CollectionSkeleton.swift */; };
F54CF5E52024CEB000330B0D /* UITableView+CollectionSkeleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F54CF5E22024CEAF00330B0D /* UITableView+CollectionSkeleton.swift */; };
F54CF5E62024CEB000330B0D /* UICollectionView+CollectionSkeleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F54CF5E32024CEB000330B0D /* UICollectionView+CollectionSkeleton.swift */; };
F56B94461FAE20AF0095662F /* PrepareForSkeletonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */; };
F5F622411FAC6E31007C062A /* UIColor+Skeleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F622401FAC6E31007C062A /* UIColor+Skeleton.swift */; };
F5F622431FAC81FD007C062A /* CALayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F622421FAC81FD007C062A /* CALayer+Extensions.swift */; };
@@ -70,6 +73,9 @@
F5307E361FB1076E00EE67C5 /* SkeletonUITableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonUITableViewDataSource.swift; sourceTree = "<group>"; };
F5307E381FB1078E00EE67C5 /* SkeletonUICollectionViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonUICollectionViewDataSource.swift; sourceTree = "<group>"; };
F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainsMultilineText.swift; sourceTree = "<group>"; };
F54CF5E12024CEAF00330B0D /* UIView+CollectionSkeleton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+CollectionSkeleton.swift"; sourceTree = "<group>"; };
F54CF5E22024CEAF00330B0D /* UITableView+CollectionSkeleton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITableView+CollectionSkeleton.swift"; sourceTree = "<group>"; };
F54CF5E32024CEB000330B0D /* UICollectionView+CollectionSkeleton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UICollectionView+CollectionSkeleton.swift"; sourceTree = "<group>"; };
F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrepareForSkeletonProtocol.swift; sourceTree = "<group>"; };
F5F622401FAC6E31007C062A /* UIColor+Skeleton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Skeleton.swift"; sourceTree = "<group>"; };
F5F622421FAC81FD007C062A /* CALayer+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+Extensions.swift"; sourceTree = "<group>"; };
@@ -166,6 +172,9 @@
F5F899EA1FAB9DA3002E8FDA /* SkeletonCollectionDataSource.swift */,
F5307E381FB1078E00EE67C5 /* SkeletonUICollectionViewDataSource.swift */,
F5307E361FB1076E00EE67C5 /* SkeletonUITableViewDataSource.swift */,
F54CF5E32024CEB000330B0D /* UICollectionView+CollectionSkeleton.swift */,
F54CF5E22024CEAF00330B0D /* UITableView+CollectionSkeleton.swift */,
F54CF5E12024CEAF00330B0D /* UIView+CollectionSkeleton.swift */,
);
path = Collections;
sourceTree = "<group>";
@@ -337,9 +346,11 @@
buildActionMask = 2147483647;
files = (
F5F899D01FAA6A4D002E8FDA /* UIView+IBInspectable.swift in Sources */,
F54CF5E42024CEB000330B0D /* UIView+CollectionSkeleton.swift in Sources */,
F5307E371FB1076E00EE67C5 /* SkeletonUITableViewDataSource.swift in Sources */,
8933C7851EB5B820000D00A4 /* SkeletonView.swift in Sources */,
F5307E301FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift in Sources */,
F54CF5E52024CEB000330B0D /* UITableView+CollectionSkeleton.swift in Sources */,
F5307E321FB0F42F00EE67C5 /* RecursiveProtocol.swift in Sources */,
F5F622431FAC81FD007C062A /* CALayer+Extensions.swift in Sources */,
F5F899ED1FAB9F04002E8FDA /* CollectionSkeletonProtocol.swift in Sources */,
@@ -347,6 +358,7 @@
F5F899EB1FAB9DA3002E8FDA /* SkeletonCollectionDataSource.swift in Sources */,
F5F622411FAC6E31007C062A /* UIColor+Skeleton.swift in Sources */,
F5F899D21FAB9630002E8FDA /* AssociationPolicy.swift in Sources */,
F54CF5E62024CEB000330B0D /* UICollectionView+CollectionSkeleton.swift in Sources */,
F56B94461FAE20AF0095662F /* PrepareForSkeletonProtocol.swift in Sources */,
F5307E391FB1078E00EE67C5 /* SkeletonUICollectionViewDataSource.swift in Sources */,
F5307E3B1FB123C100EE67C5 /* ContainsMultilineText.swift in Sources */,
@@ -8,72 +8,25 @@
import UIKit
extension UIView {
func addDummyDataSourceIfNeeded() {
guard let collection = self as? CollectionSkeleton else { return }
collection.addDummyDataSource()
collection.disableScrolling()
}
func removeDummyDataSourceIfNeeded(reloadAfter reload: Bool = true) {
guard let collection = self as? CollectionSkeleton else { return }
collection.removeDummyDataSource(reloadAfter: reload)
collection.enableScrolling()
}
enum DataSourceAssociatedKeys {
static var dummyDataSource = "dummyDataSource"
}
protocol CollectionSkeleton {
var skeletonDataSource: SkeletonCollectionDataSource? { get set }
var estimatedNumberOfRows: Int { get }
func addDummyDataSource()
func removeDummyDataSource(reloadAfter: Bool)
func disableScrolling()
func enableScrolling()
}
private enum AssociatedKeys {
static var dummyDataSource = "dummyDataSource"
}
extension CollectionSkeleton where Self: UIScrollView {
var estimatedNumberOfRows: Int { return 0 }
func addDummyDataSource() {}
func removeDummyDataSource(reloadAfter: Bool) {}
func disableScrolling() { isScrollEnabled = false }
func enableScrolling() { isScrollEnabled = true }
}
extension UITableView: CollectionSkeleton {
var skeletonDataSource: SkeletonCollectionDataSource? {
get { return objc_getAssociatedObject(self, &AssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
set {
objc_setAssociatedObject(self, &AssociatedKeys.dummyDataSource, newValue, AssociationPolicy.retain.objc)
self.dataSource = newValue
}
}
func addDummyDataSource() {
guard let originalDataSource = self.dataSource as? SkeletonTableViewDataSource,
!(originalDataSource is SkeletonCollectionDataSource)
else { return }
let dataSource = SkeletonCollectionDataSource(tableViewDataSource: originalDataSource)
self.skeletonDataSource = dataSource
reloadData()
}
func removeDummyDataSource(reloadAfter: Bool) {
guard let dataSource = self.dataSource as? SkeletonCollectionDataSource else { return }
self.skeletonDataSource = nil
self.dataSource = dataSource.originalTableViewDataSource
if reloadAfter { self.reloadData() }
}
}
extension UICollectionView: CollectionSkeleton {
var skeletonDataSource: SkeletonCollectionDataSource? {
get { return objc_getAssociatedObject(self, &AssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
set { objc_setAssociatedObject(self, &AssociatedKeys.dummyDataSource, newValue, AssociationPolicy.retain.objc) }
}
}
@@ -14,11 +14,13 @@ class SkeletonCollectionDataSource: NSObject {
weak var originalTableViewDataSource: SkeletonTableViewDataSource?
weak var originalCollectionViewDataSource: UICollectionViewDataSource?
var rowHeight: CGFloat = 0.0
convenience init(tableViewDataSource: SkeletonTableViewDataSource? = nil, collectionViewDataSource: UICollectionViewDataSource? = nil) {
convenience init(tableViewDataSource: SkeletonTableViewDataSource? = nil, collectionViewDataSource: UICollectionViewDataSource? = nil, rowHeight: CGFloat = 0.0) {
self.init()
self.originalTableViewDataSource = tableViewDataSource
self.originalCollectionViewDataSource = collectionViewDataSource
self.rowHeight = rowHeight
}
}
@@ -40,7 +42,7 @@ extension SkeletonCollectionDataSource: UITableViewDataSource {
}
}
// MARK: - UICollectionViewDataSource
// MARK: - [WIP] UICollectionViewDataSource
extension SkeletonCollectionDataSource: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
@@ -8,4 +8,5 @@
import UIKit
// Work in progress
protocol SkeletonUICollectionViewDataSource: UICollectionViewDataSource {}
@@ -17,7 +17,7 @@ public protocol SkeletonTableViewDataSource: UITableViewDataSource {
public extension SkeletonTableViewDataSource {
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Int(ceil(skeletonView.frame.height/skeletonView.rowHeight))
return skeletonView.estimatedNumberOfRows
}
func numSections(in collectionSkeletonView: UITableView) -> Int { return 1 }
@@ -0,0 +1,17 @@
//
// UICollectionView+CollectionSkeleton.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 02/02/2018.
// Copyright © 2018 SkeletonView. All rights reserved.
//
import UIKit
extension UICollectionView: CollectionSkeleton {
var skeletonDataSource: SkeletonCollectionDataSource? {
get { return objc_getAssociatedObject(self, &DataSourceAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
set { objc_setAssociatedObject(self, &DataSourceAssociatedKeys.dummyDataSource, newValue, AssociationPolicy.retain.objc) }
}
}
@@ -0,0 +1,52 @@
//
// UITableView+CollectionSkeleton.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 02/02/2018.
// Copyright © 2018 SkeletonView. All rights reserved.
//
import UIKit
extension UITableView: CollectionSkeleton {
var estimatedNumberOfRows: Int {
return Int(ceil(frame.height/rowHeight))
}
var skeletonDataSource: SkeletonCollectionDataSource? {
get { return objc_getAssociatedObject(self, &DataSourceAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
set {
objc_setAssociatedObject(self, &DataSourceAssociatedKeys.dummyDataSource, newValue, AssociationPolicy.retain.objc)
self.dataSource = newValue
}
}
func addDummyDataSource() {
guard let originalDataSource = self.dataSource as? SkeletonTableViewDataSource,
!(originalDataSource is SkeletonCollectionDataSource)
else { return }
let dataSource = SkeletonCollectionDataSource(tableViewDataSource: originalDataSource, rowHeight: calculateRowHeight())
self.skeletonDataSource = dataSource
reloadData()
}
func removeDummyDataSource(reloadAfter: Bool) {
guard let dataSource = self.dataSource as? SkeletonCollectionDataSource else { return }
restoreRowHeight()
self.skeletonDataSource = nil
self.dataSource = dataSource.originalTableViewDataSource
if reloadAfter { self.reloadData() }
}
private func restoreRowHeight() {
guard let dataSource = self.dataSource as? SkeletonCollectionDataSource else { return }
rowHeight = dataSource.rowHeight
}
private func calculateRowHeight() -> CGFloat {
guard rowHeight == UITableViewAutomaticDimension else { return rowHeight }
rowHeight = estimatedRowHeight
return estimatedRowHeight
}
}
@@ -0,0 +1,23 @@
//
// UIView+CollectionSkeleton.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 02/02/2018.
// Copyright © 2018 SkeletonView. All rights reserved.
//
import UIKit
extension UIView {
func addDummyDataSourceIfNeeded() {
guard let collection = self as? CollectionSkeleton else { return }
collection.addDummyDataSource()
collection.disableScrolling()
}
func removeDummyDataSourceIfNeeded(reloadAfter reload: Bool = true) {
guard let collection = self as? CollectionSkeleton else { return }
collection.removeDummyDataSource(reloadAfter: reload)
collection.enableScrolling()
}
}
+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)
}
}
+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()
}
}
+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)
}
}
+2
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)
}) {