Compare commits

...

139 Commits

Author SHA1 Message Date
Juanpe Catalán db19eae968 feat: Update swift-version 2019-04-23 20:44:39 +02:00
Juanpe Catalán 1d2256b4e6 feat: Update podspec 2019-04-23 20:43:21 +02:00
Juanpe Catalán 4e5c8627dd feat: Update Gemfile 2019-04-23 20:42:05 +02:00
Juanpe Catalán 09e1fe31e0 feat: Migrate to Swift 5 2019-04-23 20:38:03 +02:00
Juanpe Catalán 303ae5aeb8 feat: Update readme 2019-04-03 16:40:35 +02:00
Juanpe Catalán 4649c7e61f feat: Added tableview illustration 2019-04-03 16:39:00 +02:00
Juanpe Catalán 83e7e48caa feat: Bump cocoapod version 2019-02-26 17:54:53 +01:00
Juanpe Catalán c03af15070 Merge branch 'master' into develop 2019-02-02 10:30:30 +01:00
Juanpe Catalán ed82f74ba8 Merge pull request #131 from grifas/hotfix/reloadDataAfter
🐛 Fix  removeDummyDataSourceIfNeeded passing the reload parameter
2019-02-02 10:27:18 +01:00
Juanpe Catalán 01aba0b3ff Merge pull request #128 from jehlert-trip/feature/podspec_swift_version
Include swift_version in podspec
2019-02-02 10:24:14 +01:00
Aurélien Grifasi 6352030012 🐛 Fix removeDummyDataSourceIfNeeded passing the reload parameter 2019-01-15 11:53:45 +01:00
Justin Ehlert 4b06c30b9d Include swift_version in podspec 2019-01-10 14:33:15 -05:00
Juanpe Catalán 3dcde46fe9 feat: Update gitignore 2018-12-18 11:48:58 +01:00
Juanpe Catalán c9de28eb37 feat: update README file 2018-12-13 10:23:09 +01:00
Juanpe Catalán 915e838994 Merge branch 'develop'
# Conflicts:
#	README.md
2018-12-13 10:17:38 +01:00
Juanpe Catalán 74b44264ec Merge pull request #117 from Juanpe/feature/codebeat_refactor
Feature/codebeat refactor
2018-12-13 10:17:03 +01:00
Juanpe Catalán a9ad72aa0c feat: Update README file 2018-12-13 10:16:28 +01:00
Juanpe Catalán d535529be8 feat: Mute gradient switch 2018-12-13 10:03:13 +01:00
Juanpe Catalán a6bff05ca4 feat: Split line into two 2018-12-13 09:48:35 +01:00
Juanpe Catalán bc98cd600e feat: Refactor associated keys 2018-12-13 09:42:44 +01:00
Juanpe Catalán 8f1cfc0045 feat: Mute associated keys many instance variables 2018-12-13 08:51:35 +01:00
Juanpe Catalán 432ec6caa2 feat: Split associated keys 2018-12-12 18:59:34 +01:00
Juanpe Catalán e18aedbbf8 feat: Move examples files 2018-12-12 18:32:52 +01:00
Juanpe Catalán 4dc67ac3d7 feat: Refactor some classes 2018-12-12 18:28:42 +01:00
Juanpe Catalán aa29cd3d6b feat: Mute too many instance variables 2018-12-12 18:02:54 +01:00
Juanpe Catalán a2def55880 feat: Reorder section in README file 2018-12-12 16:01:44 +01:00
Juanpe Catalán 97f5e3e2e1 feat: Update README file 2018-12-12 15:59:40 +01:00
Juanpe Catalán 420cc7a669 Merge branch 'develop' 2018-12-12 09:11:09 +01:00
Juanpe Catalán b9601f56c3 Merge pull request #115 from Juanpe/feature/configure_fastlane
Feature/configure fastlane
2018-12-12 09:09:59 +01:00
Juanpe Catalán fbca1092c3 feat: Remove Appfile 2018-12-12 09:05:50 +01:00
Juanpe Catalán eb90faf413 fix: URL of bitrise badge 2018-12-11 11:21:57 +01:00
Juanpe Catalán f0cc7125ef feat: update readme 2018-12-11 11:20:44 +01:00
Juanpe Catalán f5b493d6f0 feat: Add bitrise in README file 2018-12-11 11:05:08 +01:00
Juanpe Catalán 48bc217413 feat: Update lane to create new version 2018-12-11 10:56:45 +01:00
Juanpe Catalán 516fcecce2 testing bitrise trigger 2018-12-11 10:41:51 +01:00
Juanpe Catalán 9052d5d806 feat: test bitrise 2018-12-11 09:44:15 +01:00
Juanpe Catalán 12dff52238 feat: remove useless code 2018-12-11 09:43:03 +01:00
Juanpe Catalán f0b4575de6 feat: Create fastfile 2018-12-11 09:42:09 +01:00
Juanpe Catalán 7be8ddd70a Merge pull request #114 from Juanpe/refactor_builder
feat: Create SkeletonLayerBuilder and SkeletonMultilineLayerBuilder
2018-12-10 20:48:49 +01:00
Juanpe Catalán a9628c0de8 feat: Create SkeletonLayerBuilder and SkeletonMultilineLayerBuilder 2018-12-10 20:04:07 +01:00
Juanpe Catalán 339191f016 feat: update changelog file 2018-12-10 19:34:33 +01:00
Juanpe Catalán 7022942d92 feat: Update Changelog file 2018-12-10 19:33:02 +01:00
Juanpe Catalán 03cf1bfea8 Merge pull request #113 from osterbergmarcus/patch-1
Adding swift news to mentioned section
2018-12-10 18:13:46 +01:00
Marcus Osterberg 9f46e7a647 Adding swift news to mentioned section 2018-12-06 19:31:12 +01:00
Juanpe Catalán 674cd1aa9f feat: Update README file to include swift 4.2 2018-10-04 16:15:40 +02:00
Juanpe Catalán 36a863b557 feat: Bump cocoapod version 2018-10-02 11:37:25 +02:00
Juanpe Catalán 3b83b85249 feat: Upgrade swift version 2018-10-02 11:33:55 +02:00
Juanpe Catalán afbb10d785 Update .travis.yml 2018-10-02 11:25:06 +02:00
Juanpe Catalán 99ccd1e640 Update .travis.yml 2018-10-02 11:15:02 +02:00
Juanpe Catalán e6834d1296 Merge pull request #99 from mohpor/master
Updated to Swift 4.2
2018-10-02 11:12:14 +02:00
Mohammad Porooshani 89cb6f4116 Updated to Swift 4.2
Signed-off-by: Mohammad Porooshani <porooshani@gmail.com>
2018-09-15 11:36:30 +04:30
Juanpe Catalán b74592e6be feat: Change guide image 2018-09-04 09:16:10 +02:00
Juanpe Catalán 5f79708f03 feat: Update thumbnail guide 2018-09-04 02:49:10 +02:00
Juanpe Catalán a83e1a00bf feat: Add guides section in README file 2018-09-04 00:52:14 +02:00
Juanpe Catalán 61e98b26ad Merge pull request #95 from arn00s/fix/demoProjectSettings
Fix/demo project settings
2018-08-20 23:48:30 +02:00
Arnaud 0ec754dfc5 use safe area for iPhone X 2018-08-20 17:22:09 +02:00
Arnaud 78b4b7b31c fix info.plist path 2018-08-20 17:17:51 +02:00
Juanpe Catalán e3ddc4fe98 Merge pull request #89 from brunomunizaf/translate-readme-brazil-portuguese
Translate readme file to Brazilian Portuguese
2018-08-13 11:23:34 +02:00
Bruno Muniz 13485e61f7 Translate readme file to Brazilian Portuguese 2018-08-13 11:11:23 +02:00
Juanpe Catalán 8f5d409304 feat: revert last changes in README file 2018-08-10 17:43:15 +02:00
Juanpe Catalán bc3258b8a2 feat: update README 2018-08-10 17:42:24 +02:00
Juanpe Catalán fd4683f555 Merge pull request #90 from Juanpe/debug_system
Debug system
2018-08-10 17:40:11 +02:00
Juanpe Catalán 97ebbcc917 feat: update README 2018-08-10 17:36:49 +02:00
Juanpe Catalán a02922dba6 feat: Update README file 2018-08-10 17:30:43 +02:00
Juanpe Catalán 4ac1abe50e feat: BUMP version 1.4 2018-08-10 17:28:31 +02:00
Juanpe Catalán 25e313850e feat: Update CHANGELOG file 2018-08-10 17:28:12 +02:00
Juanpe Catalán 88914f7816 feat: Update README file explaining debug mode 2018-08-10 17:10:49 +02:00
Juanpe Catalán 14ab00d13a feat: update README file 2018-08-10 14:23:48 +02:00
Juanpe Catalán 90b993a16b feat: Create skeletonHierarchy 2018-08-10 14:17:30 +02:00
Juanpe Catalán 413867718e feat: Improve recursive protocol 2018-08-10 13:23:24 +02:00
Juanpe Catalán e43b3f3750 feat: Add skeleton hierarchy 2018-08-10 08:27:51 +02:00
Juanpe Catalán b9b368b7e8 feat: Create Environment variable 2018-08-09 18:45:03 +02:00
Juanpe Catalán fc22d58f89 Merge pull request #87 from reececomo/alternate-user-interaction
#86 disableUserInteraction() on UIScrollView just to prevent scroll
2018-08-06 10:24:18 +02:00
Reece Como 8988b22784 style consistency 2018-08-06 11:09:20 +08:00
Reece Como 3cb1602e11 Merge https://github.com/Juanpe/SkeletonView into alternate-user-interaction 2018-08-06 11:08:31 +08:00
Reece Como 649672182e allow UIScrollViews to accept button taps 2018-08-06 10:58:10 +08:00
Juanpe Catalán 3c16416dbb feat: update README 2018-08-02 17:55:14 +02:00
Juanpe Catalán b90e97592a feat: BUMP version 1.3 2018-08-02 17:32:31 +02:00
Juanpe Catalán 9fbaaa02f5 feat: Update README and CHANGELOG file 2018-08-02 17:32:23 +02:00
Juanpe Catalán 3f7f2ec95c feat: Remove useless code 2018-08-02 16:33:20 +02:00
Juanpe Catalán 26da7e3fc0 feat: Rename SkeletonAppearance 2018-08-02 16:32:57 +02:00
Juanpe Catalán 804390e98a feat: Create SkeletonViewAppearance 2018-08-02 16:18:41 +02:00
Juanpe Catalán 3bf613da0d Merge pull request #84 from reececomo/83-Customisable-Defaults
#83 Customisable Defaults
2018-08-02 15:41:40 +02:00
Reece Como 0eff052314 Merge pull request #1 from reececomo/83-Customisable-Defaults
#83 Customisable Defaults
2018-08-02 16:32:39 +08:00
Reece Como af91af3716 #83 Customisable Defaults 2018-08-02 16:08:31 +08:00
Juanpe Catalán 2fe2323cc1 Merge pull request #42 from kjoneandrei/feature/fix_skeleton_collectionView
Fix: Skeleton loading for UICollectionView
2018-07-31 09:54:00 +02:00
anho 024171a993 fixed collectionview xib and skeleton 2018-07-27 15:28:59 +02:00
Juanpe Catalán 726817cfe5 feat: Add credits 2018-07-27 13:05:14 +02:00
Juanpe Catalán 9684817956 feat: Change README path 2018-07-27 13:00:36 +02:00
Juanpe Catalán 3a847ece3d fix: Link to Chinese README file 2018-07-27 12:43:11 +02:00
Juanpe Catalán a0fb735c3e feat: Add Chinese README file 2018-07-27 12:38:16 +02:00
Juanpe Catalán c2720898ac feat: BUMP version 1.2.3 2018-07-25 08:52:15 +02:00
Juanpe Catalán 9e395c7747 feat: Update CHANGELOG file 2018-07-25 08:50:08 +02:00
Juanpe Catalán b8aa587af6 Merge pull request #78 from hisaac/master
fix: Typos in README and method signature
2018-07-24 15:13:51 +02:00
Juanpe Catalán 349f7eb48e Merge pull request #77 from giantramen/master
feat: Use Stack View to Calculate Bounds
2018-07-24 11:59:13 +02:00
Juanpe Catalán 4f874a84f0 feat: Bump version 1.2.2 2018-07-24 08:34:26 +02:00
Juanpe Catalán a6de5342a4 Merge branch 'master' of https://github.com/Juanpe/SkeletonView 2018-07-24 08:27:42 +02:00
Juanpe Catalán 404da95cbe feat: New branding 2018-07-24 08:27:39 +02:00
Isaac Halvorson ebe7f3e463 Add default implementation of old method and deprecation message 2018-07-19 10:03:47 -05:00
Isaac Halvorson ad5cf2a21f Fix a couple small typos in README and a method signature 2018-07-19 09:37:15 -05:00
Kamin, Grant 7afe87160a Use Stack View to Calculate Bounds
Labels inside of stack views are not aligned properly
because we were assuming leading alignment.
Calculating the origin based off of the stack view alignment
instead.
2018-07-06 09:04:10 -05:00
Juanpe Catalán 7c7be7c80c Merge pull request #73 from giantramen/master
fix: UIStackView Label Issue #23
2018-06-26 16:25:59 +02:00
giantramen ff42bd02e2 Fix UIStackView Label Issue
Fixes issue where SkeletonView sets the text of each label to nil, UIStackView sets the height of each label to 0 and lays them out on top of each other.
2018-06-26 09:20:49 -05:00
Juanpe Catalán 0136c8576c feat: Update README 2018-06-06 10:36:19 +02:00
Juanpe Catalán 76c526385d Merge branch 'master' of https://github.com/Juanpe/SkeletonView 2018-06-06 10:20:16 +02:00
Juanpe Catalán 2b633e5ab6 BUMP version 1.2.1 2018-06-06 10:20:12 +02:00
Juanpe Catalán 5c19df67df Merge pull request #68 from eduardbosch/patch-1
Revert wrong README change
2018-06-06 10:00:59 +02:00
Juanpe Catalán b1778542d7 Merge pull request #69 from eduardbosch/patch-2
Fix tipo and remove space
2018-06-06 10:00:30 +02:00
Eduard Bosch Bertran ad5c87eea2 Fix tipo and remove space 2018-06-06 09:32:13 +02:00
Eduard Bosch Bertran 09813e530c Revert wrong README change
I've just reverted a missing ` in the README in this commit https://github.com/Juanpe/SkeletonView/commit/c06ae2d72c640a8fddf520991f3a3b274ad91ffd
2018-06-06 09:28:49 +02:00
Juanpe Catalán 23489159dd Update CHANGELOG.md 2018-06-05 17:51:11 +02:00
Juanpe Catalán c06ae2d72c feat: Update REAMDE 2018-06-05 17:16:53 +02:00
Juanpe Catalán 02b56f2707 feat: change some texts in README file 2018-06-05 17:14:48 +02:00
Juanpe Catalán fe40f4b6bf feat: Resize multiline asset 2018-06-05 17:07:54 +02:00
Juanpe Catalán 8b4f0b38b1 feat: update README file 2018-06-05 17:06:10 +02:00
Juanpe Catalán 44ce71e6b2 Merge pull request #67 from Juanpe/viewstate
View State
2018-06-05 13:30:44 +02:00
Juanpe Catalán d4ea1e78c2 feat: Add public headers for tvOS 2018-06-05 13:27:33 +02:00
Juanpe Catalán 00dc7e7b5d Merge branch 'master' into viewstate 2018-06-05 13:07:43 +02:00
Juanpe Catalán 3f947c08ca feat: remove unuseful code 2018-06-05 13:07:33 +02:00
Juanpe Catalán 5d8a30ba82 Merge pull request #15 from B4V4-G/master
enhancement: change corner radius for multilines
2018-06-05 12:54:45 +02:00
Juanpe Catalán aa08656fdd Merge branch 'master' into viewstate 2018-06-05 12:22:54 +02:00
Juanpe Catalán 15b1a33ade Merge pull request #62 from eduardbosch/feature/support_inspectables_with_carthage
feat: Support inspectable properties when using Carthage
2018-06-05 10:27:09 +02:00
Eduard Bosch Bertran d66adce904 chore: Add swift files with inspectables as public headers 2018-06-03 14:21:24 +02:00
Juanpe Catalán 06ddf69bd4 Update README.md 2018-05-16 11:51:00 +02:00
Juanpe Catalán bd0640a4c4 Update README.md 2018-05-16 11:35:31 +02:00
Juanpe Catalán 53a276c66b Update README.md 2018-05-16 11:34:59 +02:00
Juanpe Catalán be7fc21d03 Update README.md 2018-05-14 15:38:30 +02:00
Juanpe Catalán 3ac704eb07 Update README.md 2018-05-14 15:28:32 +02:00
Juanpe Catalán 987d12cbc5 Update issue_template.md 2018-05-14 13:42:05 +02:00
Juanpe Catalán a37948b6c1 Update README.md 2018-05-14 12:54:53 +02:00
Juanpe Catalán 2b15498ef3 Create view state 2018-05-14 00:43:24 +02:00
Juanpe Catalán bcde3e2ed6 fix: Don’t take account of content size layout constraints 2018-05-14 00:34:01 +02:00
Juanpe Catalán 719a5f507b feat: refactor some method signatures 2018-05-05 13:25:39 +02:00
Juanpe Catalán 340f792880 Update CHANGELOG.md 2018-05-01 13:33:26 +02:00
Juanpe Catalán f516eab8e2 feat: Add some link in mentions section 2018-05-01 13:00:34 +02:00
Juanpe Catalán 3506ffbfb5 feat: Add mentions in README file 2018-05-01 12:50:43 +02:00
Juanpe Catalán 2a895dfe35 feat: Add donate button 2018-05-01 12:27:30 +02:00
Juanpe Catalán 261ff5a451 feat: Update shields in README file 2018-04-30 14:19:15 +02:00
3stud1ant3 e0fbf88912 feat: change corner radius for multilines from storyboard 2017-11-24 23:42:01 +05:00
79 changed files with 2935 additions and 401 deletions
+2
View File
@@ -1,5 +1,7 @@
⚠️ Please fill out this template when filing an issue.
#### 🙏🏼 *Please check if it already exists other issue related with yours.*
### What did you do?
*Please replace this with what you did.*
+1 -1
View File
@@ -46,7 +46,7 @@ playground.xcworkspace
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
Pods/
# Carthage
#
+1 -1
View File
@@ -1 +1 @@
4.1
5.0
+4 -4
View File
@@ -1,7 +1,7 @@
language: objective-c
osx_image: xcode9.3
osx_image: xcode10
script:
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-iOS -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=11'
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-tvOS -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV,OS=11'
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonViewExample -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=11'
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-iOS -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=12'
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-tvOS -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV,OS=12'
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonViewExample -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=12'
after_success:
Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

+67
View File
@@ -1,6 +1,73 @@
# Change Log
All notable changes to this project will be documented in this file
## Next version
### New
- Adding swift news to mentioned section (thanks @osterbergmarcus)
## [Debug (1.4)](https://github.com/Juanpe/SkeletonView/releases/tag/1.4)
### New
- Create `skeletonDescription` print a skeleton representation of the view.
- Create `SKELETON_DEBUG` environment variable, in order to print the view hierarchy when the skeleton appears.
### Improvements
- Add two new methods to `SkeletonFlowDelegate` protocol. Now you can know when the skeleton did show and when it did hide.
- `Recursive` protocol
### Bug fixes
- Solved issue [#86](https://github.com/Juanpe/SkeletonView/issues/86) (thanks @reececomo)
## [Custom defaults (1.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.3)
### New
- Default values customizables. Now you can set the default values of Skeleton appearance.(thanks @reececomo)
- issues: [[#50](https://github.com/Juanpe/SkeletonView/issues/50), [#83](https://github.com/Juanpe/SkeletonView/issues/83)]
### Bug fixes
- Solved issue [#41](https://github.com/Juanpe/SkeletonView/issues/41). Now, Skeleton works if UICollectionView cell's Nib is registered in code. (thanks @kjoneandrei)
## [Typo (1.2.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.3)
### Fixes
- Fix typo in `SkeletonTableViewDataSource` protocol
### Improvements
- Now it takes in account the `UIStackView` to calculate the `SkeletonLayer` bounds (thanks @giantramen)
## [New face (1.2.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.2)
### New
- Rebranding
### Bug fixes
- Solved issue [#23](https://github.com/Juanpe/SkeletonView/issues/23). Problem with UIStackView. (thanks @giantramen)
## [State (1.2.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.1)
### New
- You can set the corner radius multiline elements (thanks @B4V4-G)
- Save view state when skeleton appears and recovery when it is hidden (@juanpe)
### Bug fixes
- Solved issue [#51](https://github.com/Juanpe/SkeletonView/issues/51). Support inspectable properties when using Carthage. (thanks @eduardbosch)
## [On TV (1.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2)
### New
- Now ```SkeletonView``` is **tvOS** compatible! 🎉. (thanks @mihai8804858)
### Bug fixes
- Solved issue [#46](https://github.com/Juanpe/SkeletonView/issues/46). It crashes the application when tap on it, didSelect called and crash.
## [Hotfix (1.1.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.1.1)
### Bug fixes
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.2</string>
<string>1.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.2</string>
<string>1.3</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
+40
View File
@@ -0,0 +1,40 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
@@ -0,0 +1,35 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
import SkeletonView
class CollectionViewCell: UICollectionViewCell {
var label: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
isSkeletonable = true
createLabel()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func createLabel() {
label = UILabel()
label.isSkeletonable = true
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: centerXAnchor),
label.centerYAnchor.constraint(equalTo: centerYAnchor),
label.heightAnchor.constraint(equalToConstant: frame.height / 2),
label.widthAnchor.constraint(equalToConstant: frame.width / 2)
])
}
}
+168
View File
@@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="irH-dz-xqL">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="qda-qV-vJk">
<objects>
<viewController id="irH-dz-xqL" customClass="ViewController" customModule="SkeletonViewExampleUICollectionView" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Fso-nq-n6t">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eHI-ka-8vS">
<rect key="frame" x="0.0" y="20" width="375" height="243"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="obr-b6-dib">
<rect key="frame" x="45" y="142" width="287" height="78"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="78" id="jx6-c1-U0j"/>
</constraints>
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </string>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<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="Ql9-Jy-aWM">
<rect key="frame" x="141" y="20" width="93" height="93"/>
<color key="backgroundColor" red="0.56078431370000004" green="0.59607843140000005" blue="0.7843137255" alpha="0.90709546230000004" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="height" constant="93" id="jlG-7K-wMd"/>
<constraint firstAttribute="width" constant="93" id="xHX-Y1-dvi"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</imageView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="243" id="0g6-3g-uII"/>
<constraint firstAttribute="trailing" secondItem="obr-b6-dib" secondAttribute="trailing" constant="43" id="3ms-Wk-qcn"/>
<constraint firstItem="obr-b6-dib" firstAttribute="centerX" secondItem="eHI-ka-8vS" secondAttribute="centerX" constant="1" id="B5s-DM-eR8"/>
<constraint firstAttribute="height" constant="243" id="GX5-3W-tUt"/>
<constraint firstItem="Ql9-Jy-aWM" firstAttribute="centerX" secondItem="eHI-ka-8vS" secondAttribute="centerX" id="HsA-ID-oSK"/>
<constraint firstItem="Ql9-Jy-aWM" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="top" constant="20" id="Hxu-ae-hXQ"/>
<constraint firstItem="obr-b6-dib" firstAttribute="leading" secondItem="eHI-ka-8vS" secondAttribute="leading" constant="45" id="eop-Gq-7mO"/>
<constraint firstItem="obr-b6-dib" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="top" constant="142" id="inJ-75-hGX"/>
</constraints>
<viewLayoutGuide key="safeArea" id="36i-gK-pIa"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</view>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="HKL-L0-T2w">
<rect key="frame" x="0.0" y="263" width="375" height="264"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="mGU-kn-rfE">
<size key="itemSize" width="50" height="50"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<cells/>
<connections>
<outlet property="dataSource" destination="irH-dz-xqL" id="wya-hE-ovQ"/>
</connections>
</collectionView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JjA-MK-YzZ">
<rect key="frame" x="0.0" y="527" width="375" height="140"/>
<subviews>
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="fMR-vj-7de">
<rect key="frame" x="20" y="23" width="140" height="29"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
<segment title="Solid"/>
<segment title="Gradient"/>
</segments>
<connections>
<action selector="changeSkeletonType:" destination="irH-dz-xqL" eventType="valueChanged" id="lfR-JV-DU4"/>
</connections>
</segmentedControl>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KBe-RM-BG8">
<rect key="frame" x="310" y="21" width="49" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<connections>
<action selector="changeAnimated:" destination="irH-dz-xqL" eventType="valueChanged" id="dlH-KK-iee"/>
</connections>
</switch>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Animated" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GSj-Ze-UIK">
<rect key="frame" x="211" y="28" width="91" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Color" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4Hm-fj-45V">
<rect key="frame" x="32" y="89" width="52" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="HBJ-nh-56V">
<rect key="frame" x="92" y="84" width="30" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aUR-Qo-gHK">
<rect key="frame" x="20" y="74" width="140" height="52"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<connections>
<action selector="btnChangeColorTouchUpInside:" destination="irH-dz-xqL" eventType="touchUpInside" id="Xca-QC-htl"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="140" id="QDV-wu-e3I"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="140" id="qR5-cz-YAm"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="eHI-ka-8vS" firstAttribute="trailing" secondItem="2Gq-Y8-1TU" secondAttribute="trailing" id="0dc-Vd-yJY"/>
<constraint firstItem="JjA-MK-YzZ" firstAttribute="leading" secondItem="2Gq-Y8-1TU" secondAttribute="leading" id="57i-UV-Wqd"/>
<constraint firstItem="eHI-ka-8vS" firstAttribute="leading" secondItem="2Gq-Y8-1TU" secondAttribute="leading" id="5tt-Ne-67Y"/>
<constraint firstItem="JjA-MK-YzZ" firstAttribute="bottom" secondItem="2Gq-Y8-1TU" secondAttribute="bottom" id="AAr-ke-R7M"/>
<constraint firstItem="JjA-MK-YzZ" firstAttribute="trailing" secondItem="2Gq-Y8-1TU" secondAttribute="trailing" id="DtS-9c-zBC"/>
<constraint firstItem="HKL-L0-T2w" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="bottom" id="Jgf-jS-PLT"/>
<constraint firstItem="eHI-ka-8vS" firstAttribute="top" secondItem="2Gq-Y8-1TU" secondAttribute="top" id="Ux2-GF-HLK"/>
<constraint firstItem="JjA-MK-YzZ" firstAttribute="top" secondItem="HKL-L0-T2w" secondAttribute="bottom" id="XEd-Gf-KFI"/>
<constraint firstItem="2Gq-Y8-1TU" firstAttribute="trailing" secondItem="HKL-L0-T2w" secondAttribute="trailing" id="bNo-98-pE4"/>
<constraint firstItem="HKL-L0-T2w" firstAttribute="leading" secondItem="2Gq-Y8-1TU" secondAttribute="leading" id="iIq-cx-paX"/>
</constraints>
<viewLayoutGuide key="safeArea" id="2Gq-Y8-1TU"/>
</view>
<connections>
<outlet property="avatarImage" destination="Ql9-Jy-aWM" id="VoL-by-ygR"/>
<outlet property="collectionView" destination="HKL-L0-T2w" id="HSe-j0-S5d"/>
<outlet property="colorSelectedView" destination="HBJ-nh-56V" id="Iiq-iY-Glj"/>
<outlet property="skeletonTypeSelector" destination="fMR-vj-7de" id="CgX-3A-weo"/>
<outlet property="switchAnimated" destination="KBe-RM-BG8" id="emU-g9-NHT"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="PkM-Y0-M5i" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-972" y="-209.14542728635683"/>
</scene>
</scenes>
<resources>
<image name="avatar" width="215" height="211"/>
</resources>
</document>
+173
View File
@@ -0,0 +1,173 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
import SkeletonView
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView! {
didSet {
collectionView.isSkeletonable = true
collectionView.backgroundColor = .clear
collectionView.showsHorizontalScrollIndicator = false
collectionView.showsVerticalScrollIndicator = false
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
}
}
@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
colorSelectedView.layer.masksToBounds = true
colorSelectedView.backgroundColor = SkeletonAppearance.default.tintColor
}
}
@IBOutlet weak var switchAnimated: UISwitch!
@IBOutlet weak var skeletonTypeSelector: UISegmentedControl!
var type: SkeletonType {
return skeletonTypeSelector.selectedSegmentIndex == 0 ? .solid : .gradient
}
override func viewDidLoad() {
super.viewDidLoad()
view.isSkeletonable = true
collectionView.prepareSkeleton(completion: { done in
self.view.showAnimatedSkeleton()
})
}
@IBAction func changeAnimated(_ sender: Any) {
if switchAnimated.isOn {
view.startSkeletonAnimation()
} else {
view.stopSkeletonAnimation()
}
}
@IBAction func changeSkeletonType(_ sender: Any) {
refreshSkeleton()
}
@IBAction func btnChangeColorTouchUpInside(_ sender: Any) {
showAlertPicker()
}
func refreshSkeleton() {
self.view.hideSkeleton()
if type == .gradient { showGradientSkeleton() }
else { showSolidSkeleton() }
}
func showSolidSkeleton() {
if switchAnimated.isOn {
view.showAnimatedSkeleton(usingColor: colorSelectedView.backgroundColor!)
} else {
view.showSkeleton(usingColor: colorSelectedView.backgroundColor!)
}
}
func showGradientSkeleton() {
let gradient = SkeletonGradient(baseColor: colorSelectedView.backgroundColor!)
if switchAnimated.isOn {
view.showAnimatedGradientSkeleton(usingGradient: gradient)
} else {
view.showGradientSkeleton(usingGradient: gradient)
}
}
func showAlertPicker() {
let alertView = UIAlertController(title: "Select color", message: "\n\n\n\n\n\n", preferredStyle: .alert)
let pickerView = UIPickerView(frame: CGRect(x: 0, y: 50, width: 260, height: 115))
pickerView.dataSource = self
pickerView.delegate = self
alertView.view.addSubview(pickerView)
let action = UIAlertAction(title: "OK", style: .default) { [unowned pickerView, unowned self] _ in
let row = pickerView.selectedRow(inComponent: 0)
self.colorSelectedView.backgroundColor = colors[row].0
self.refreshSkeleton()
}
alertView.addAction(action)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertView.addAction(cancelAction)
present(alertView, animated: false, completion: {
pickerView.frame.size.width = alertView.view.frame.size.width
})
}
}
// MARK: - UIPickerViewDelegate, UIPickerViewDataSource
extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return colors.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return colors[row].1
}
}
// MARK: - UICollectionViewDelegateFlowLayout
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width/3 - 10, height: view.frame.width/3 - 10)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
}
// MARK: - SkeletonCollectionViewDataSource
extension ViewController: SkeletonCollectionViewDataSource {
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "CollectionViewCell"
}
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
// MARK: - UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
return cell
}
}
@@ -14,7 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
@@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Before

Width:  |  Height:  |  Size: 933 B

After

Width:  |  Height:  |  Size: 933 B

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -31,7 +31,7 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="120"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="oiE-tt-nc2">
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="avatar" translatesAutoresizingMaskIntoConstraints="NO" id="oiE-tt-nc2">
<rect key="frame" x="15" y="18" width="82" height="82"/>
<color key="backgroundColor" red="0.56078431370000004" green="0.59607843140000005" blue="0.7843137255" alpha="0.90709546230000004" colorSpace="calibratedRGB"/>
<constraints>
@@ -52,6 +52,9 @@
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="linesCornerRadius">
<integer key="value" value="0"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</label>
</subviews>
@@ -76,7 +79,7 @@
</tableViewCell>
</prototypes>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="NO"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="dataSource" destination="BYZ-38-t0r" id="Hxi-nC-gbY"/>
@@ -99,6 +102,9 @@
<userDefinedRuntimeAttribute type="number" keyPath="lastLineFillPercent">
<integer key="value" value="40"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="linesCornerRadius">
<integer key="value" value="6"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="avatar" translatesAutoresizingMaskIntoConstraints="NO" id="nMj-pU-5wJ">
@@ -195,6 +201,9 @@
<constraint firstItem="XgY-1a-UGc" firstAttribute="bottom" secondItem="6Tk-OE-BBY" secondAttribute="bottom" id="vnZ-9k-MfI"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="NO"/>
</userDefinedRuntimeAttributes>
</view>
<navigationItem key="navigationItem" id="BEI-dU-kr2"/>
<connections>
@@ -207,7 +216,7 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-897" y="-376"/>
<point key="canvasLocation" x="-2582" y="-400"/>
</scene>
</scenes>
<resources>
@@ -1,10 +1,4 @@
//
// Constants.swift
// SkeletonView-iOS
//
// Created by Renato Mendes on 28/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
@@ -13,7 +13,7 @@ class ViewController: UIViewController {
@IBOutlet weak var tableview: UITableView! {
didSet {
tableview.rowHeight = UITableViewAutomaticDimension
tableview.rowHeight = UITableView.automaticDimension
tableview.estimatedRowHeight = 120.0
}
}
@@ -29,7 +29,7 @@ class ViewController: UIViewController {
didSet {
colorSelectedView.layer.cornerRadius = 5
colorSelectedView.layer.masksToBounds = true
colorSelectedView.backgroundColor = SkeletonDefaultConfig.tintColor
colorSelectedView.backgroundColor = SkeletonAppearance.default.tintColor
}
}
@@ -136,7 +136,7 @@ extension ViewController: SkeletonTableViewDataSource {
return 9
}
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "CellIdentifier"
}
+4
View File
@@ -0,0 +1,4 @@
source "https://rubygems.org"
gem "fastlane"
gem 'cocoapods'
+212
View File
@@ -0,0 +1,212 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.0)
activesupport (4.2.10)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
atomos (0.1.3)
babosa (1.0.2)
claide (1.0.2)
cocoapods (1.5.3)
activesupport (>= 4.0.2, < 5)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.5.3)
cocoapods-deintegrate (>= 1.0.2, < 2.0)
cocoapods-downloader (>= 1.2.0, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-stats (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.3.0, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
fourflusher (~> 2.0.1)
gh_inspector (~> 1.0)
molinillo (~> 0.6.5)
nap (~> 1.0)
ruby-macho (~> 1.1)
xcodeproj (>= 1.5.7, < 2.0)
cocoapods-core (1.5.3)
activesupport (>= 4.0.2, < 6)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
cocoapods-deintegrate (1.0.2)
cocoapods-downloader (1.2.2)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.0)
cocoapods-stats (1.0.0)
cocoapods-trunk (1.3.1)
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.1.0)
colored (1.2)
colored2 (3.1.2)
commander-fastlane (4.4.6)
highline (~> 1.7.2)
concurrent-ruby (1.0.5)
declarative (0.0.10)
declarative-option (0.1.0)
digest-crc (0.4.1)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.5.0)
emoji_regex (0.1.1)
escape (0.0.4)
excon (0.62.0)
faraday (0.15.4)
multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6)
faraday (>= 0.7.4)
http-cookie (~> 1.0.0)
faraday_middleware (0.12.2)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.5)
fastlane (2.110.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
bundler (>= 1.12.0, < 2.0.0)
colored
commander-fastlane (>= 4.4.6, < 5.0.0)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (~> 0.1)
excon (>= 0.45.0, < 1.0.0)
faraday (~> 0.9)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 0.9)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-api-client (>= 0.21.2, < 0.24.0)
google-cloud-storage (>= 1.15.0, < 2.0.0)
highline (>= 1.7.2, < 2.0.0)
json (< 3.0.0)
mini_magick (~> 4.5.1)
multi_json
multi_xml (~> 0.5)
multipart-post (~> 2.0.0)
plist (>= 3.1.0, < 4.0.0)
public_suffix (~> 2.0.0)
rubyzip (>= 1.2.2, < 2.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
slack-notifier (>= 2.0.0, < 3.0.0)
terminal-notifier (>= 1.6.2, < 2.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.6.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
fourflusher (2.0.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
google-api-client (0.23.9)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.5, < 0.7.0)
httpclient (>= 2.8.1, < 3.0)
mime-types (~> 3.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
signet (~> 0.9)
google-cloud-core (1.2.7)
google-cloud-env (~> 1.0)
google-cloud-env (1.0.5)
faraday (~> 0.11)
google-cloud-storage (1.15.0)
digest-crc (~> 0.4)
google-api-client (~> 0.23)
google-cloud-core (~> 1.2)
googleauth (~> 0.6.2)
googleauth (0.6.7)
faraday (~> 0.12)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.7)
highline (1.7.10)
http-cookie (1.0.3)
domain_name (~> 0.5)
httpclient (2.8.3)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
json (2.1.0)
jwt (2.1.0)
memoist (0.16.0)
mime-types (3.2.2)
mime-types-data (~> 3.2015)
mime-types-data (3.2018.0812)
mini_magick (4.5.1)
minitest (5.11.3)
molinillo (0.6.6)
multi_json (1.13.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
nanaimo (0.2.6)
nap (1.1.0)
naturally (2.2.0)
netrc (0.11.0)
os (1.0.0)
plist (3.4.0)
public_suffix (2.0.5)
representable (3.0.4)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rouge (2.0.7)
ruby-macho (1.3.1)
rubyzip (1.2.2)
security (0.1.3)
signet (0.11.0)
addressable (~> 2.3)
faraday (~> 0.9)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.5)
CFPropertyList
naturally
slack-notifier (2.3.2)
terminal-notifier (1.8.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thread_safe (0.3.6)
tty-cursor (0.6.0)
tty-screen (0.6.5)
tty-spinner (0.9.0)
tty-cursor (~> 0.6.0)
tzinfo (1.2.5)
thread_safe (~> 0.1)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.5)
unicode-display_width (1.4.0)
word_wrap (1.0.0)
xcodeproj (1.7.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.2.6)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.0)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
ruby
DEPENDENCIES
cocoapods
fastlane
BUNDLED WITH
1.16.6
+132 -36
View File
@@ -1,30 +1,36 @@
![](Assets/header.jpg)
![](Assets/header2.jpg)
<p align="center">
<a href="https://travis-ci.org/Juanpe/SkeletonView">
<img src="https://img.shields.io/travis/Juanpe/SkeletonView.svg">
<a href="https://app.bitrise.io/app/6d289a17e22c8323">
<img src="https://app.bitrise.io/app/6d289a17e22c8323/status.svg?token=fI7gKC41XD9-aRXDScCKBw&branch=master">
</a>
<img src="https://img.shields.io/badge/Swift-4.1-orange.svg" />
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
<a href="https://github.com/Juanpe/SkeletonView">
<img src="https://img.shields.io/cocoapods/p/SkeletonView.svg" alt="Platforms">
</a>
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
</a>
<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://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/dt/SkeletonView.svg?style=flat" alt="CocoaPods downloads" />
</a>
<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 href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">
<img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Paypal" />
</a>
<br/>
<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>
🌎 Translations: </br>
[🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) by [@WhatsXie](https://twitter.com/WhatsXie) </br>
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) by [@brunomunizaf](https://twitter.com/brunomuniz_af)
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.
@@ -32,8 +38,7 @@ Today almost all apps have async processes, such as Api requests, long running p
Enjoy it! 🙂
* [Features](#-features)
* [Requirements](#-supported-os--sdk-versions)
* [Example Project](#-example)
* [Guides](#-guides)
* [Installation](#-installation)
* [Cocoapods](#using-cocoapods)
* [Carthage](#using-carthage)
@@ -41,11 +46,15 @@ Enjoy it! 🙂
* [Collections](#-collections)
* [Multiline text](#-multiline-text)
* [Custom colors](#-custom-colors)
* [Appearance](#-appearance)
* [Custom animations](#-custom-animations)
* [Hierarchy](#-hierarchy)
* [Debug](#-debug)
* [Documentation](#-documentation)
* [Supported OS & SDK Versions](#-supported-os--sdk-versions)
* [Next steps](#-next-steps)
* [Contributed](#-contributed)
* [Contributing](#-contributing)
* [Mentions](#-mentions)
* [Author](#-author)
* [License](#-license)
@@ -60,17 +69,9 @@ Enjoy it! 🙂
- [x] Simple Swift syntax
- [x] Lightweight readable codebase
### 📋 Supported OS & SDK Versions
## 🎬 Guides
* iOS 9.0+
* tvOS 9.0+
* Swift 4
### 🔮 Example
To run the example project, clone the repo and run `SkeletonViewExample` target.
![](Assets/demoApp2.png)
[<img src="Assets/thumb_getting_started.png">](https://youtu.be/75kgOhWsPNA)
## 📲 Installation
@@ -158,7 +159,7 @@ avatarImageView.isSkeletonable = true
Now, ```SkeletonView``` is compatible with ```UITableView``` and ```UICollectionView```.
###### UITableView
#### UITableView
If you want to show the skeleton in a ```UITableView```, you need to conform to ```SkeletonTableViewDataSource``` protocol.
@@ -166,7 +167,7 @@ If you want to show the skeleton in a ```UITableView```, you need to conform to
public protocol SkeletonTableViewDataSource: UITableViewDataSource {
func numSections(in collectionSkeletonView: UITableView) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
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.
@@ -186,12 +187,12 @@ func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection s
There is only one method you need to implement to let Skeleton know the cell identifier. This method doesn't have default implementation:
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
```
**Example**
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "CellIdentifier"
}
```
@@ -199,7 +200,15 @@ 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`.
###### UICollectionView
👩🏼‍🏫 **How specify which elements are skeletonables?**
Here is an illustration that shows how you should specify which elements are skeletonables when you are using an `UITableView`:
![](Assets/tableview_scheme.png)
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`
#### UICollectionView
For ```UICollectionView```, you need to conform to ```SkeletonCollectionViewDataSource``` protocol.
@@ -221,17 +230,27 @@ The rest of the process is the same as ```UITableView```
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%**
##### 🎛 Customize
To modify the percent **using code**, set the property:
You can set some properties for multilines elements.
| Property | Values | Default | Preview
| ------- | ------- |------- | -------
| **Filling percent** of the last line. | `0...100` | `70%` | ![](Assets/multiline_lastline.png)
| **Corner radius** of lines. (**NEW**) | `0...10` | `0` | ![](Assets/multiline_corner.png)
To modify the percent or radius **using code**, set the properties:
```swift
descriptionTextView.lastLineFillPercent = 50
descriptionTextView.linesCornerRadius = 5
```
Or, if you prefer use **IB/Storyboard**:
![](Assets/lastline_storyboard.png)
![](Assets/multiline_customize.png)
### 🎨 Custom colors
@@ -256,9 +275,34 @@ Besides, ```SkeletonView``` features 20 flat colors 🤙🏼
![](Assets/flatcolors.png)
###### Image captured from website [https://flatuicolors.com](https://flatuicolors.com)
### 🦋 Appearance
**NEW** 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: .clouds*
- **gradient**: SkeletonGradient
- *default: SkeletonGradient(baseColor: .clouds)*
- **multilineHeight**: CGFloat
- *default: 15*
- **multilineSpacing**: CGFloat
- *default: 10*
- **multilineLastLineFillPercent**: Int
- *default: 70*
- **multilineCornerRadius**: Int
- *default: 0*
To get these default values you can use `SkeletonAppearance.default`. Using this property you can set the values as well:
```Swift
SkeletonAppearance.default.multilineHeight = 20
SkeletonAppearance.default.tintColor = .green
```
### 🤓 Custom animations
Now, ```SkeletonView``` has two built-in animations, *pulse* for solid skeletons and *sliding* for gradients.
```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.
@@ -280,7 +324,7 @@ view.showAnimatedSkeleton { (layer) -> CAAnimation in
}
```
**NEW** It's available ```SkeletonAnimationBuilder```. It's a builder to make ```SkeletonLayerAnimation```.
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).
@@ -298,7 +342,7 @@ view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
|------- | -------
| .leftRight | ![](Assets/sliding_left_to_right.gif)
| .rightLeft | ![](Assets/sliding_right_to_left.gif)
| .topBottom | ![](Assets/sliding_top_to_bottom.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)
@@ -323,10 +367,41 @@ Because an image is worth a thousand words:
|![](Assets/all_skeletonables.png) | ![](Assets/all_skeletonables_result.png)
### 🔬 Debug
**NEW** In order to facilitate the debug tasks when something is not working fine. `SkeletonView` has some new tools.
First, `UIView` has available a new property with his skeleton info:
```swift
var skeletonDescription: String
```
The skeleton representation looks like this:
![](Assets/debug_description.png)
Besides, you can activate the new **debug mode**. You just add the environment variable `SKELETON_DEBUG` and activate it.
![](Assets/debug_mode.png)
Then, when the skeleton appears, you can see the view hierarchy in the Xcode console.
<details>
<summary>Open to see an output example </summary>
<img src="Assets/hierarchy_output.png" />
</details>
### 📚 Documentation
Coming soon...😅
### 📋 Supported OS & SDK Versions
* iOS 9.0+
* tvOS 9.0+
* Swift 4.2
## 📬 Next steps
* [x] Set the filling percent of the last line in multiline elements
@@ -334,12 +409,14 @@ Coming soon...😅
* [x] Supported resizable cells
* [x] CollectionView compatible
* [x] tvOS compatible
* [ ] Add recovery state
* [x] Add recovery state
* [x] Custom default appearance
* [x] Debug mode
* [ ] Custom collections compatible
* [ ] Add animations when it shows/hides the skeletons
* [ ] MacOS and WatchOS compatible
## ❤️ Contributed
## ❤️ Contributing
This is an open source project, so feel free to contribute. How?
- Open an [issue](https://github.com/Juanpe/SkeletonView/issues/new).
- Send feedback via [email](mailto://juanpecatalan.com).
@@ -349,12 +426,31 @@ See [all contributors](https://github.com/Juanpe/SkeletonView/graphs/contributor
###### Project generated with [SwiftPlate](https://github.com/JohnSundell/SwiftPlate)
## 📢 Mentions
- [iOS Dev Weekly #327](https://iosdevweekly.com/issues/327#start)
- [Hacking with Swift Articles](https://www.hackingwithswift.com/articles/40/skeletonview-makes-loading-content-beautiful)
- [Top 10 Swift Articles November](https://medium.mybridge.co/swift-top-10-articles-for-the-past-month-v-nov-2017-dfed7861cd65)
- [30 Amazing iOS Swift Libraries (v2018)](https://medium.mybridge.co/30-amazing-ios-swift-libraries-for-the-past-year-v-2018-7cf15027eee9)
- [AppCoda Weekly #44](http://digest.appcoda.com/issues/appcoda-weekly-issue-44-81899)
- [iOS Cookies Newsletter #103](https://us11.campaign-archive.com/?u=cd1f3ed33c6527331d82107ba&id=48131a516d)
- [Swift Developments Newsletter #113](https://andybargh.com/swiftdevelopments-113/)
- [iOS Goodies #204](http://ios-goodies.com/post/167557280951/week-204)
- [Swift Weekly #96](http://digest.swiftweekly.com/issues/swift-weekly-issue-96-81759)
- [CocoaControls](https://www.cocoacontrols.com/controls/skeletonview)
- [Awesome iOS Newsletter #74](https://ios.libhunt.com/newsletter/74)
- [Swift News #36](https://www.youtube.com/watch?v=mAGpsQiy6so)
## 👨🏻‍💻 Author
[1.1]: http://i.imgur.com/tXSoThF.png
[1]: http://www.twitter.com/JuanpeCatalan
* Juanpe Catalán [![alt text][1.1]][1]
<a class="bmc-button" target="_blank" href="https://www.buymeacoffee.com/CDou4xtIK"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy me a coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;"><span style="margin-left:5px"></span></a>
## 👮🏻 License
```
+442
View File
@@ -0,0 +1,442 @@
![](Assets/header2.jpg)
<p align="center">
<a href="https://app.bitrise.io/app/6d289a17e22c8323">
<img src="https://app.bitrise.io/app/6d289a17e22c8323/status.svg?token=fI7gKC41XD9-aRXDScCKBw&branch=master">
</a>
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
<a href="https://github.com/Juanpe/SkeletonView">
<img src="https://img.shields.io/cocoapods/p/SkeletonView.svg" alt="Platforms">
</a>
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
</a>
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/dt/SkeletonView.svg?style=flat" alt="CocoaPods downloads" />
</a>
<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://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">
<img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Paypal" />
</a>
<br/>
<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>
🌎 Traduções: </br>
[Original](https://github.com/Juanpe/SkeletonView) </br>
[🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) por [@WhatsXie](https://twitter.com/WhatsXie) </br>
Hoje, quase todos os apps têm processos assíncronos, como requisições de API, processos longos, etc. E enquanto os processos estão ocorrendo, normalmente os desenvolvedores usam uma view que mostra os usuarios que algo está ocorrendo.
```SkeletonView``` foi criado para essa necessidade, um jeito elegante de mostrar aos usuários que algo está acontecendo e já prepará-los para qual conteúdo será carregado.
Aproveite! 🙂
* [Features](#-features)
* [Requerimentos](#-supported-os--sdk-versions)
* [Projeto de exemplo](#-example)
* [Instalação](#-installation)
* [Cocoapods](#using-cocoapods)
* [Carthage](#using-carthage)
* [Como usar](#-how-to-use)
* [Coleções](#-collections)
* [Texto em várias linhas](#-multiline-text)
* [Cores customizadas](#-custom-colors)
* [Aparência](#-appearance)
* [Animaçōes customizadas](#-custom-animations)
* [Hierarquia](#-hierarchy)
* [Documentação](#-documentation)
* [Próximos passos](#-next-steps)
* [Contribuindo](#-contributing)
* [Menções](#-mentions)
* [Autor](#-author)
* [Licença](#-license)
## 🌟 Features
- [x] Fácil de usar
- [x] Todas as UIViews são skeletonables
- [x] Completamente customizável
- [x] Universal (iPhone & iPad)
- [x] Interface Builder friendly
- [x] Sintaxe simples em Swift
- [x] Código leve e legível
### 📋 Versões do SDK e OS suportados
* iOS 9.0+
* tvOS 9.0+
* Swift 4.2
### 🔮 Exemplo
Para rodar o projeto de exemplo, clone o repositório e use o target `SkeletonViewExample`.
## 📲 Instalação
#### Usando [CocoaPods](https://cocoapods.org)
Edite seu `Podfile` e especifíque a dependência:
```ruby
pod "SkeletonView"
```
#### Usando [Carthage](https://github.com/carthage)
Edite seu `Cartfile` e especifíque a dependência:
```bash
github "Juanpe/SkeletonView"
```
## 🐒 Como usar
Apenas **3** passos necessários para usar `SkeletonView`:
**1.** Importe SkeletonView no lugar desejado.
```swift
import SkeletonView
```
**2.** Agora, especifíque quais views serão `skeletonables`. Você consegue fazer isso de duas formas:
**Usando código:**
```swift
avatarImageView.isSkeletonable = true
```
**Usando IB/Storyboards:**
![](Assets/storyboard.png)
**3.** Uma vez que você setou as views, você pode mostrar o **skeleton**. Para fazê-lo, você tem **4** escolhas:
```swift
(1) view.showSkeleton() // Solid
(2) view.showGradientSkeleton() // Gradient
(3) view.showAnimatedSkeleton() // Solid animated
(4) view.showAnimatedGradientSkeleton() // Gradient animated
```
**Pré-visualização**
<table>
<tr>
<td width="25%">
<center>Solid</center>
</td>
<td width="25%">
<center>Gradient</center>
</td>
<td width="25%">
<center>Solid Animated</center>
</td>
<td width="25%">
<center>Gradient Animated</center>
</td>
</tr>
<tr>
<td width="25%">
<img src="Assets/solid.png"></img>
</td>
<td width="25%">
<img src="Assets/gradient.png"></img>
</td>
<td width="25%">
<img src="Assets/solid_animated.gif"></img>
</td>
<td width="25%">
<img src="Assets/gradient_animated.gif"></img>
</td>
</tr>
</table>
> **IMPORTANTE!**
>>```SkeletonView``` é recursivo, então se você quer mostrar o esqueleto em todas as views skeletonables, você só precisa chamar o método na main container view. Por exemplo, com UIViewControllers
### 🌿 Coleções
```SkeletonView``` é compatível com ```UITableView``` e ```UICollectionView```.
###### UITableView
Se você quer mostrar o skeleton em uma ```UITableView```, você precisa conformar com o protocolo ```SkeletonTableViewDataSource```.
``` swift
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
}
```
Como você pode ver, esse protocolo herda de ```UITableViewDataSource```, então você pode substituir esse protocolo com o protocolo do skeleton.
Esse protocolo tem uma implementação padrão:
``` swift
func numSections(in collectionSkeletonView: UITableView) -> Int
// Default: 1
```
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
// Default:
// It calculates how many cells need to populate whole tableview
```
Esse é o único método que você precisa implementar para informar o skeleton sobre o cell identifier. Esse método não possui uma implementação padrão:
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
```
**Exemplo**
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "CellIdentifier"
}
```
> **IMPORTANTE!**
> Se você está usando resizable cells (`tableView.rowHeight = UITableViewAutomaticDimension` ), é obrigatório definir a `estimatedRowHeight`.
###### UICollectionView
Para ```UICollectionView```, você precisa conformar com o protocolo ```SkeletonCollectionViewDataSource```.
``` swift
public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
func numSections(in collectionSkeletonView: UICollectionView) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier
}
```
O resto do processo é o mesmo da ```UITableView```
### 📰 Texto de várias linhas
![](Assets/multilines2.png)
Quando você usar elementos com texto, ```SkeletonView``` desenha linhas para simular o texto.
Além disso, você pode decidir quantas linhas você quer. Se ```numberOfLines``` está setado para zero (0), haverá um cálculo para saber quantas linhas são necessárias para preencher o skeleton inteiro e será desenhado. Caso contrário, se você setar para um (1) ou qualquer outro número maior que zero, só serão desenhadas aquele número de linhas.
##### 🎛 Customização
Você pode setar algumas propriedades para elementos de várias linhas.
| Property | Values | Default | Preview
| ------- | ------- |------- | -------
| **Filling percent** of the last line. | `0...100` | `70%` | ![](Assets/multiline_lastline.png)
| **Corner radius** of lines. (**NEW**) | `0...10` | `0` | ![](Assets/multiline_corner.png)
Para modificar a percentagem ou o raio **usando código**, especifique as propriedades:
```swift
descriptionTextView.lastLineFillPercent = 50
descriptionTextView.linesCornerRadius = 5
```
Ou, se você preferir use **IB/Storyboard**:
![](Assets/multiline_customize.png)
### 🎨 Cores customizadas
Você pode decidir que cor o skeleton esta pintado. Você só precisa parametrizar a cor e o gradiente que deseja.
**Usando cores sólidas**
``` swift
view.showSkeleton(usingColor: UIColor.gray) // Solid
// or
view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0))
```
**Usando gradientes**
``` swift
let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
view.showGradientSkeleton(usingGradient: gradient) // Gradient
```
Além do mais, ```SkeletonView``` tem 20 cores flat 🤙🏼
```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...```
![](Assets/flatcolors.png)
###### Imagem capturada do site [https://flatuicolors.com](https://flatuicolors.com)
### 🦋 Aparência
**NOVIDADE** Os skeletons tem uma aparência padrão. Então, quando você não especifíca a cor, gradiente ou propriedades de várias linhas, `SkeletonView` usa os valores padrões.
Valores padrões:
- **tintColor**: UIColor
- *default: .clouds*
- **gradient**: SkeletonGradient
- *default: SkeletonGradient(baseColor: .clouds)*
- **multilineHeight**: CGFloat
- *default: 15*
- **multilineSpacing**: CGFloat
- *default: 10*
- **multilineLastLineFillPercent**: Int
- *default: 70*
- **multilineCornerRadius**: Int
- *default: 0*
Para obter esses valores padrões você pode usar `SkeletonAppearance.default`. Usando essa propriedade você pode declarar os valores também:
```Swift
SkeletonAppearance.default.multilineHeight = 20
SkeletonAppearance.default.tintColor = .green
```
### 🤓 Animações customizadas
```SkeletonView``` tem duas animações pré-definidas, *pulse* para skeletons solidos e *sliding* para gradientes.
Além disso, se você quiser fazer suas próprias animações no skeleton, é muito fácil.
Skeleton disponibiliza a função `showAnimatedSkeleton` que tem o closure ```SkeletonLayerAnimation``` onde você pode definir sua animação customizada.
```swift
public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation
```
Você pode chamar esta função assim:
```swift
view.showAnimatedSkeleton { (layer) -> CAAnimation in
let animation = CAAnimation()
// Customize here your animation
return animation
}
```
Está disponível ```SkeletonAnimationBuilder```. É um construtor para ```SkeletonLayerAnimation```.
Hoje, você pode criar **sliding animations** para gradientes, decidindo a **direction** e setando a **duration** da animaçāo (padrão = 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``` é um enum, com os seguintes 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)
> **😉 TRUQUE!**
Existe outra forma de criar sliding animations, apenas usando este atalho:
>>```let animation = GradientDirection.leftToRight.slidingAnimation()```
### 👨‍👧‍👦 Hierarquia
Já que ```SkeletonView``` é recursiva, e queremos que o skeleton seja muito eficiente, queremos parar a recursão assim que possível. Por este motivo, você deve setar a container view como `Skeletonable`, porque o Skeleton vai parar de procurar por subviews `skeletonable` assim que a view não for mais skeletonable, quebrando a recursão.
Porque uma imagem vale mais que mil palavras:
> ```ìsSkeletonable```= ☠️
| Configuration | Result
|------- | -------
|![](Assets/no_skeletonable.png) | ![](Assets/no_skeletonables_result.png)
|![](Assets/container_no_skeletonable.png) | ![](Assets/no_skeletonables_result.png)
|![](Assets/container_skeletonable.png) | ![](Assets/container_skeletonable_result.png)
|![](Assets/all_skeletonables.png) | ![](Assets/all_skeletonables_result.png)
### 📚 Documentação
Em breve...😅
## 📬 Próximos passos
* [x] Setar o percentual de preenchimento da última linha em elementos de várias linhas
* [x] Adicionar mais animações de gradiente
* [x] Suporte para resizable cells
* [x] Compatível com CollectionView
* [x] Compatível com tvOS
* [x] Adicionar recovery state
* [x] Aparência padrão customizável
* [ ] Compatível com coleções customizáveis
* [ ] Adicionar animações quando mostra/esconde os skeletons
* [ ] Compatível com MacOS e WatchOS
## ❤️ Contribuindo
Este é um projeto de código aberto, então sinta-se a vontade para contribuir. Como?
- Abra uma [issue](https://github.com/Juanpe/SkeletonView/issues/new).
- Envie feedback por [email](mailto://juanpecatalan.com).
- Proponha seus próprios fixes, sugestões e abra um pull request com as alterações.
Ver [todos os contribuidores](https://github.com/Juanpe/SkeletonView/graphs/contributors)
###### Projeto gerado com [SwiftPlate](https://github.com/JohnSundell/SwiftPlate)
## 📢 Menções
- [iOS Dev Weekly #327](https://iosdevweekly.com/issues/327#start)
- [Hacking with Swift Articles](https://www.hackingwithswift.com/articles/40/skeletonview-makes-loading-content-beautiful)
- [Top 10 Swift Articles November](https://medium.mybridge.co/swift-top-10-articles-for-the-past-month-v-nov-2017-dfed7861cd65)
- [30 Amazing iOS Swift Libraries (v2018)](https://medium.mybridge.co/30-amazing-ios-swift-libraries-for-the-past-year-v-2018-7cf15027eee9)
- [AppCoda Weekly #44](http://digest.appcoda.com/issues/appcoda-weekly-issue-44-81899)
- [iOS Cookies Newsletter #103](https://us11.campaign-archive.com/?u=cd1f3ed33c6527331d82107ba&id=48131a516d)
- [Swift Developments Newsletter #113](https://andybargh.com/swiftdevelopments-113/)
- [iOS Goodies #204](http://ios-goodies.com/post/167557280951/week-204)
- [Swift Weekly #96](http://digest.swiftweekly.com/issues/swift-weekly-issue-96-81759)
- [CocoaControls](https://www.cocoacontrols.com/controls/skeletonview)
- [Awesome iOS Newsletter #74](https://ios.libhunt.com/newsletter/74)
## 👨🏻‍💻 Autor
[1.1]: http://i.imgur.com/tXSoThF.png
[1]: http://www.twitter.com/JuanpeCatalan
* Juanpe Catalán [![alt text][1.1]][1]
<a class="bmc-button" target="_blank" href="https://www.buymeacoffee.com/CDou4xtIK"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy me a coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;"><span style="margin-left:5px"></span></a>
## 👮🏻 Licença
```
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.
```
Executable
+418
View File
@@ -0,0 +1,418 @@
![](Assets/header2.jpg)
<p align="center">
<a href="https://app.bitrise.io/app/6d289a17e22c8323">
<img src="https://app.bitrise.io/app/6d289a17e22c8323/status.svg?token=fI7gKC41XD9-aRXDScCKBw&branch=master">
</a>
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
<a href="https://github.com/Juanpe/SkeletonView">
<img src="https://img.shields.io/cocoapods/p/SkeletonView.svg" alt="Platforms">
</a>
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
</a>
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/dt/SkeletonView.svg?style=flat" alt="CocoaPods downloads" />
</a>
<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://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">
<img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Paypal" />
</a>
<br/>
<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>
🌎 翻译: [ [原版的](https://github.com/Juanpe/SkeletonView) ] </br>
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) [@brunomunizaf](https://twitter.com/brunomuniz_af)
今天,几乎所有的应用程序都有异步流程,例如:Api请求、长时间运行的流程等。虽然流程正在运行,但通常开发人员会设置一个加载视图来向用户显示正在发生的事情。
```SkeletonView``` 已经构想出来满足这种需求,这是一种优雅的方式,向用户展示正在发生的事情,并为他们等待的内容做好准备。
好好享受! 🙂
* [特征](#-特征)
* [版本要求](#-版本要求)
* [示例项目](#-示例)
* [安装](#-安装)
* [Cocoapods](#使用-cocoapods)
* [Carthage](#使用-carthage)
* [如何使用](#-如何使用)
* [集合](#-集合)
* [多行文字](#-多行文字)
* [自定义颜色](#-自定义颜色)
* [自定义动画](#-自定义动画)
* [等级制度](#-等级制度)
* [文档](#-文档)
* [下一步](#-下一步)
* [特约](#-特约)
* [提及](#-提及)
* [作者](#-作者)
* [许可证](#-许可证)
## 🌟 特征
- [x] 使用方便
- [x] 支持所有 UIView
- [x] 完全可定制
- [x] 通用(iPhone和iPad
- [x] Interface Builder 友好
- [x] 简单的 Swift 语法
- [x] 轻量级可读代码库
### 📋 版本要求
* iOS 9.0+
* tvOS 9.0+
* Swift 4.2
### 🔮 示例
要运行示例项目,请克隆并运行 `SkeletonViewExample` 项目。
## 📲 安装
#### 使用 [CocoaPods](https://cocoapods.org)
使用 CocoaPods 编辑您的 Podfile 并指定依赖项:
```ruby
pod "SkeletonView"
```
#### 使用 [Carthage](https://github.com/carthage)
编辑您的 Cartfile 并指定依赖项:
```bash
github "Juanpe/SkeletonView"
```
## 🐒 如何使用
只需 **3** 个步骤即可使用 `SkeletonView`:
**1.** 在适当的位置导入SkeletonView
```swift
import SkeletonView
```
**2.** 现在,您可以通过两种设置方式实现 `SkeletonView` 效果
**使用纯代码:**
```swift
avatarImageView.isSkeletonable = true
```
**使用 IB/Storyboards**
![](Assets/storyboard.png)
**3.** 设置视图后,可以显示 **skeleton**. 并且您有 **4** 种效果可供选择:
```swift
(1) view.showSkeleton() // 固体
(2) view.showGradientSkeleton() // 渐变
(3) view.showAnimatedSkeleton() // 纯色动画
(4) view.showAnimatedGradientSkeleton() // 渐变动画
```
**Preview**
<table>
<tr>
<td width="25%">
<center>固体</center>
</td>
<td width="25%">
<center>渐变</center>
</td>
<td width="25%">
<center>纯色动画</center>
</td>
<td width="25%">
<center>渐变动画</center>
</td>
</tr>
<tr>
<td width="25%">
<img src="Assets/solid.png"></img>
</td>
<td width="25%">
<img src="Assets/gradient.png"></img>
</td>
<td width="25%">
<img src="Assets/solid_animated.gif"></img>
</td>
<td width="25%">
<img src="Assets/gradient_animated.gif"></img>
</td>
</tr>
</table>
> **重要!**
>>```SkeletonView``` 是递归的,所以如果你想在所有可骨架化的视图中显示骨架,你只需要在主容器视图中调用show方法。例如,使用UIViewControllers
### 🌿 集合
现在,```SkeletonView``` 兼容 ```UITableView``` 和 ```UICollectionView```。
###### UITableView
如果你要显示 skeleton 在一个 ```UITableView```上,你需要符合 ```SkeletonTableViewDataSource``` 协议。
``` swift
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
}
```
如您所见,此协议继承自 UITableViewDataSource,因此您可以使用骨架协议替换此协议。
该协议具有默认实现:
``` swift
func numSections(in collectionSkeletonView: UITableView) -> Int
// 默认值:1
```
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
// 默认值:
// 它计算填充整个tableview需要多少个单元格
```
为了让Skeleton知道单元标识符,您只需要实现一种方法。此方法没有默认实现:
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
```
**示例**
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "CellIdentifier"
}
```
> **重要!**
> 如果您使用可调整大小的单元格 (`tableView.rowHeight = UITableViewAutomaticDimension` ),则必须定义 `estimatedRowHeight`。
###### UICollectionView
要为 ```UICollectionView``` 设置效果, 您需要符合 ```SkeletonCollectionViewDataSource``` 协议。
``` swift
public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
func numSections(in collectionSkeletonView: UICollectionView) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier
}
```
其余操作与 ```UITableView``` 相同。
### 📰 多行文字
![](Assets/multilines2.png)
使用带有文本的元素时, ```SkeletonView``` 绘制线条以模拟文本。此外,您可以决定您想要多少行。如果 ```numberOfLines``` 设置为零,它将计算填充整个骨架所需的行数,并将绘制它。相反,如果将其设置为一,二或任何大于零的数字,它将只绘制此行数。
##### 🎛 定制
您可以为多行元素设置一些属性。
| 属性 | 值范围 | 默认 | 延时
| ------- | ------- |------- | -------
| **Filling percent** 最后一行的长度百分比 | `0...100` | `70%` | ![](Assets/multiline_lastline.png)
| **Corner radius** 条目圆角半径. (**新**) | `0...10` | `0` | ![](Assets/multiline_corner.png)
**纯代码**修改百分比或半径:
```swift
descriptionTextView.lastLineFillPercent = 50
descriptionTextView.linesCornerRadius = 5
```
或者,如果您更喜欢使用 **IB/Storyboard**:
![](Assets/multiline_customize.png)
### 🎨 自定义颜色
您可以决定 ```SkeletonView``` 的显示颜色。您只需要传递颜色或渐变的参数。
**使用纯色**
``` swift
view.showSkeleton(usingColor: UIColor.gray) // 固体效果
// 或者
view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0))
```
**使用渐变色**
``` swift
let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
view.showGradientSkeleton(usingGradient: gradient) // 梯度效果
```
此外, ```SkeletonView``` 附带的 20 种颜色 🤙🏼
```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...```
![](Assets/flatcolors.png)
###### 从网站 [https://flatuicolors.com](https://flatuicolors.com)捕获的图像
### 🤓 自定义动画
现在,```SkeletonView``` 有两个内置动画,*pulse* 脉冲效果和 *sliding* 渐变滑动效果。
此外,如果你想做自己的 skeleton 动画,那真的很容易。
Skeleton 提供了 `showAnimatedSkeleton` 一个具有 ```SkeletonLayerAnimation``` 闭包的功能,您可以在其中定义自定义动画。
```swift
public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation
```
您可以像这样调用函数:
```swift
view.showAnimatedSkeleton { (layer) -> CAAnimation in
let animation = CAAnimation()
// 在这里自定义你的动画
return animation
}
```
**新** 它可用 ```SkeletonAnimationBuilder```。这是一个 ```SkeletonLayerAnimation```的衍生。
今天,您可以为渐变创建 **滑动动画**,确定 **方向** 并设置动画的 **持续时间** (默认值 = 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``` 是一个枚举,在这种情况下:
| 方向 | 效果
|------- | -------
| .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)
> **😉 技巧!**
存在另一种创建滑动动画的方法,只需使用此快捷方式:
>>```let animation = GradientDirection.leftToRight.slidingAnimation()```
### 👨‍👧‍👦 等级制度
由于 ```SkeletonView``` 是递归的,我们希望 skeleton 效率高效, 我们希望尽快停止递归。因此,您必须将容器视图设置为 `Skeletonable` ,因为`skeletonable` 一旦视图不是 Skeletonable, Skeleton 将停止查找子视图,然后断开递归。
一图胜千言:
> 设置 ```ìsSkeletonable```= ☠️
| 分组 | 结果
|------- | -------
|![](Assets/no_skeletonable.png) | ![](Assets/no_skeletonables_result.png)
|![](Assets/container_no_skeletonable.png) | ![](Assets/no_skeletonables_result.png)
|![](Assets/container_skeletonable.png) | ![](Assets/container_skeletonable_result.png)
|![](Assets/all_skeletonables.png) | ![](Assets/all_skeletonables_result.png)
### 📚 文档
快出来...😅
## 📬 下一步
* [x] 设置多行元素中最后一行的填充百分比
* [x] 添加更多渐变动画
* [x] 支持可调整大小的单元
* [x] CollectionView 兼容
* [x] tvOS 兼容
* [x] 添加恢复状态
* [ ] 自定义集合兼容
* [ ] 在显示/隐藏骨架时添加动画
* [ ] MacOS 和 WatchOS兼容
## ❤️ 特约
这是一个开源项目,所以请随时贡献。怎么样?
- 打开一个 [issue](https://github.com/Juanpe/SkeletonView/issues/new)
- 反馈通过发送 [email](mailto://juanpecatalan.com)
- 提出您自己的修复和建议,并带有拉取的请求。
查看 [所有贡献者](https://github.com/Juanpe/SkeletonView/graphs/contributors)
###### 使用 [SwiftPlate](https://github.com/JohnSundell/SwiftPlate) 生成的项目
## 📢 提及
- [iOS Dev Weekly #327](https://iosdevweekly.com/issues/327#start)
- [Hacking with Swift Articles](https://www.hackingwithswift.com/articles/40/skeletonview-makes-loading-content-beautiful)
- [Top 10 Swift Articles November](https://medium.mybridge.co/swift-top-10-articles-for-the-past-month-v-nov-2017-dfed7861cd65)
- [30 Amazing iOS Swift Libraries (v2018)](https://medium.mybridge.co/30-amazing-ios-swift-libraries-for-the-past-year-v-2018-7cf15027eee9)
- [AppCoda Weekly #44](http://digest.appcoda.com/issues/appcoda-weekly-issue-44-81899)
- [iOS Cookies Newsletter #103](https://us11.campaign-archive.com/?u=cd1f3ed33c6527331d82107ba&id=48131a516d)
- [Swift Developments Newsletter #113](https://andybargh.com/swiftdevelopments-113/)
- [iOS Goodies #204](http://ios-goodies.com/post/167557280951/week-204)
- [Swift Weekly #96](http://digest.swiftweekly.com/issues/swift-weekly-issue-96-81759)
- [CocoaControls](https://www.cocoacontrols.com/controls/skeletonview)
- [Awesome iOS Newsletter #74](https://ios.libhunt.com/newsletter/74)
## 👨🏻‍💻 作者
[1.1]: http://i.imgur.com/tXSoThF.png
[1]: http://www.twitter.com/JuanpeCatalan
* Juanpe Catalán [![alt text][1.1]][1]
<a class="bmc-button" target="_blank" href="https://www.buymeacoffee.com/CDou4xtIK"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy me a coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;"><span style="margin-left:5px"></span></a>
## 👮🏻 许可证
```
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.
```
+2 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SkeletonView"
s.version = "1.2"
s.version = "1.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,6 +12,7 @@ Pod::Spec.new do |s|
s.social_media_url = "https://twitter.com/JuanpeCatalan"
s.ios.deployment_target = "9.0"
s.tvos.deployment_target = "9.0"
s.swift_version = "5.0"
s.source = { :git => "https://github.com/Juanpe/SkeletonView.git", :tag => s.version.to_s }
s.source_files = "Sources/**/*"
end
+320 -32
View File
@@ -20,7 +20,7 @@
17DD0E12207FB28C00C56334 /* UIView+IBInspectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */; };
17DD0E13207FB28C00C56334 /* UIView+UIApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */; };
17DD0E14207FB28F00C56334 /* SkeletonAnimationBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */; };
17DD0E15207FB28F00C56334 /* SkeletonDefaultConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2F1FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift */; };
17DD0E15207FB28F00C56334 /* SkeletonAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2F1FB0EC9D00EE67C5 /* SkeletonAppearance.swift */; };
17DD0E16207FB28F00C56334 /* SkeletonGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2B1FAF6BC900EE67C5 /* SkeletonGradient.swift */; };
17DD0E17207FB28F00C56334 /* SkeletonLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F899E81FAB9D2B002E8FDA /* SkeletonLayer.swift */; };
17DD0E18207FB28F00C56334 /* SkeletonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* SkeletonView.swift */; };
@@ -29,6 +29,30 @@
17DD0E1D207FB32100C56334 /* ContainsMultilineText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */; };
17DD0E1E207FB32100C56334 /* PrepareForSkeletonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */; };
17DD0E1F207FB32100C56334 /* RecursiveProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */; };
3B83EE4820C41488005178A4 /* UIView+IBInspectable.swift in Headers */ = {isa = PBXBuildFile; fileRef = 3B83EE4620C41488005178A4 /* UIView+IBInspectable.swift */; settings = {ATTRIBUTES = (Public, ); }; };
42ABD063210B548200BEEFF4 /* SkeletonView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* SkeletonView.framework */; };
42ABD069210B548200BEEFF4 /* SkeletonView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* SkeletonView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
42ABD078210B54E200BEEFF4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD070210B54E100BEEFF4 /* AppDelegate.swift */; };
42ABD079210B54E200BEEFF4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD071210B54E100BEEFF4 /* Main.storyboard */; };
42ABD07A210B54E200BEEFF4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD072210B54E100BEEFF4 /* Assets.xcassets */; };
42ABD07B210B54E200BEEFF4 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD073210B54E100BEEFF4 /* ViewController.swift */; };
42ABD07C210B54E200BEEFF4 /* Base.lproj in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD074210B54E100BEEFF4 /* Base.lproj */; };
42ABD07F210B54E200BEEFF4 /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */; };
872D5A5621C177E20037D763 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5521C177E20037D763 /* UIView+Extension.swift */; };
872D5A5721C177E20037D763 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5521C177E20037D763 /* UIView+Extension.swift */; };
872D5A5C21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */; };
872D5A5D21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */; };
872D5A5F21C24F8E0037D763 /* UITextView+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */; };
872D5A6021C24F8E0037D763 /* UITextView+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */; };
8748240D20C6A88A00E92179 /* UIView+IBInspectable.swift in Headers */ = {isa = PBXBuildFile; fileRef = F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */; settings = {ATTRIBUTES = (Public, ); }; };
8748240E20C6A88E00E92179 /* ContainsMultilineText.swift in Headers */ = {isa = PBXBuildFile; fileRef = F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */; settings = {ATTRIBUTES = (Public, ); }; };
877EFA4521BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */; };
877EFA4621BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */; };
877EFA4821BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */; };
877EFA4921BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */; };
8785E3A1211C9C9800CC9DFD /* SkeletonDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */; };
8785E3A2211C9CA500CC9DFD /* SkeletonDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */; };
8785E3A3211C9CE800CC9DFD /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88DEA97D1FCDBD1F006C80EF /* Constants.swift */; };
88DEA97F1FCDBD78006C80EF /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88DEA97D1FCDBD1F006C80EF /* Constants.swift */; };
8933C7851EB5B820000D00A4 /* SkeletonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* SkeletonView.swift */; };
F51DE1091FBF70A70037919A /* SkeletonAnimationBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */; };
@@ -40,7 +64,7 @@
F51ED28520973CC9008B2434 /* SkeletonReusableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F51DF870206E91B300D23301 /* SkeletonReusableCell.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 */; };
F5307E301FB0EC9D00EE67C5 /* SkeletonAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2F1FB0EC9D00EE67C5 /* SkeletonAppearance.swift */; };
F5307E321FB0F42F00EE67C5 /* RecursiveProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */; };
F5307E371FB1076E00EE67C5 /* SkeletonTableViewProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E361FB1076E00EE67C5 /* SkeletonTableViewProtocols.swift */; };
F5307E391FB1078E00EE67C5 /* SkeletonCollectionViewProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E381FB1078E00EE67C5 /* SkeletonCollectionViewProtocols.swift */; };
@@ -53,6 +77,14 @@
F56B94461FAE20AF0095662F /* PrepareForSkeletonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */; };
F587FB84202CBFC8002DB5FE /* SkeletonFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F587FB83202CBFC8002DB5FE /* SkeletonFlow.swift */; };
F587FB86202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */; };
F58A6E6B20A8C54100612494 /* RecoverableViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F58A6E6A20A8C54100612494 /* RecoverableViewState.swift */; };
F58A6E6C20A8C54100612494 /* RecoverableViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F58A6E6A20A8C54100612494 /* RecoverableViewState.swift */; };
F58A6E6E20A8C66300612494 /* Recoverable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F58A6E6D20A8C66300612494 /* Recoverable.swift */; };
F58A6E6F20A8C66300612494 /* Recoverable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F58A6E6D20A8C66300612494 /* Recoverable.swift */; };
F5A5E8B4211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5A5E8B3211DCE7000FD20D0 /* Int+Whitespaces.swift */; };
F5A5E8B5211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5A5E8B3211DCE7000FD20D0 /* Int+Whitespaces.swift */; };
F5D3FB0B209DCAA300003FCF /* SubviewsSkeletonables.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5D3FB0A209DCAA300003FCF /* SubviewsSkeletonables.swift */; };
F5D3FB0C209DCAA300003FCF /* SubviewsSkeletonables.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5D3FB0A209DCAA300003FCF /* SubviewsSkeletonables.swift */; };
F5F622411FAC6E31007C062A /* UIColor+Skeleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F622401FAC6E31007C062A /* UIColor+Skeleton.swift */; };
F5F622431FAC81FD007C062A /* CALayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F622421FAC81FD007C062A /* CALayer+Extensions.swift */; };
F5F622451FACA338007C062A /* Cell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F622441FACA338007C062A /* Cell.swift */; };
@@ -69,6 +101,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
42ABD05C210B548200BEEFF4 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 52D6D9731BEFF229002C0205 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 52D6D97B1BEFF229002C0205;
remoteInfo = "SkeletonView-iOS";
};
F5307E431FB3B84500EE67C5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 52D6D9731BEFF229002C0205 /* Project object */;
@@ -79,6 +118,17 @@
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
42ABD068210B548200BEEFF4 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
42ABD069210B548200BEEFF4 /* SkeletonView.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
F5307E451FB3B84600EE67C5 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@@ -96,7 +146,22 @@
17DD0E00207FB27400C56334 /* SkeletonView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SkeletonView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
17DD0E1A207FB2C200C56334 /* SkeletonView-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SkeletonView-tvOS.plist"; sourceTree = "<group>"; };
17DD0E1B207FB2C200C56334 /* SkeletonView-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SkeletonView-iOS.plist"; sourceTree = "<group>"; };
3B83EE4620C41488005178A4 /* UIView+IBInspectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "UIView+IBInspectable.swift"; path = "Sources/Extensions/UIView+IBInspectable.swift"; sourceTree = "<group>"; };
42ABD06D210B548200BEEFF4 /* SkeletonViewExampleUICollectionView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SkeletonViewExampleUICollectionView.app; sourceTree = BUILT_PRODUCTS_DIR; };
42ABD070210B54E100BEEFF4 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
42ABD071210B54E100BEEFF4 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
42ABD072210B54E100BEEFF4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
42ABD073210B54E100BEEFF4 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
42ABD074210B54E100BEEFF4 /* Base.lproj */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Base.lproj; sourceTree = "<group>"; };
42ABD076210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SkeletonViewExampleCollectionview-Info.plist"; sourceTree = "<group>"; };
42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCell.swift; sourceTree = "<group>"; };
52D6D97C1BEFF229002C0205 /* SkeletonView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SkeletonView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
872D5A5521C177E20037D763 /* UIView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = "<group>"; };
872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Multiline.swift"; sourceTree = "<group>"; };
872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+Multiline.swift"; sourceTree = "<group>"; };
877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonLayerBuilder.swift; sourceTree = "<group>"; };
877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonMultilineLayerBuilder.swift; sourceTree = "<group>"; };
8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonDebug.swift; sourceTree = "<group>"; };
88DEA97D1FCDBD1F006C80EF /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
8933C7841EB5B820000D00A4 /* SkeletonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkeletonView.swift; sourceTree = "<group>"; };
F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonAnimationBuilder.swift; sourceTree = "<group>"; };
@@ -105,7 +170,7 @@
F51DF878206E9F5500D23301 /* SkeletonCollectionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonCollectionDelegate.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>"; };
F5307E2F1FB0EC9D00EE67C5 /* SkeletonAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonAppearance.swift; sourceTree = "<group>"; };
F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecursiveProtocol.swift; sourceTree = "<group>"; };
F5307E361FB1076E00EE67C5 /* SkeletonTableViewProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonTableViewProtocols.swift; sourceTree = "<group>"; };
F5307E381FB1078E00EE67C5 /* SkeletonCollectionViewProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonCollectionViewProtocols.swift; sourceTree = "<group>"; };
@@ -116,6 +181,10 @@
F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrepareForSkeletonProtocol.swift; sourceTree = "<group>"; };
F587FB83202CBFC8002DB5FE /* SkeletonFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonFlow.swift; sourceTree = "<group>"; };
F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+UIApplicationDelegate.swift"; sourceTree = "<group>"; };
F58A6E6A20A8C54100612494 /* RecoverableViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoverableViewState.swift; sourceTree = "<group>"; };
F58A6E6D20A8C66300612494 /* Recoverable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Recoverable.swift; sourceTree = "<group>"; };
F5A5E8B3211DCE7000FD20D0 /* Int+Whitespaces.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Whitespaces.swift"; sourceTree = "<group>"; };
F5D3FB0A209DCAA300003FCF /* SubviewsSkeletonables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubviewsSkeletonables.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>"; };
F5F622441FACA338007C062A /* Cell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cell.swift; sourceTree = "<group>"; };
@@ -141,6 +210,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
42ABD062210B548200BEEFF4 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
42ABD063210B548200BEEFF4 /* SkeletonView.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
52D6D9781BEFF229002C0205 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -162,6 +239,7 @@
52D6D9721BEFF229002C0205 = {
isa = PBXGroup;
children = (
3B83EE4620C41488005178A4 /* UIView+IBInspectable.swift */,
F5F899F31FABA607002E8FDA /* Example */,
8933C7811EB5B7E0000D00A4 /* Sources */,
52D6D99C1BEFF38C002C0205 /* Configs */,
@@ -176,6 +254,7 @@
52D6D97C1BEFF229002C0205 /* SkeletonView.framework */,
F5F899F21FABA607002E8FDA /* SkeletonViewExample.app */,
17DD0E00207FB27400C56334 /* SkeletonView.framework */,
42ABD06D210B548200BEEFF4 /* SkeletonViewExampleUICollectionView.app */,
);
name = Products;
sourceTree = "<group>";
@@ -189,18 +268,85 @@
path = Configs;
sourceTree = "<group>";
};
872D5A5821C17D850037D763 /* CollectionView */ = {
isa = PBXGroup;
children = (
42ABD070210B54E100BEEFF4 /* AppDelegate.swift */,
42ABD072210B54E100BEEFF4 /* Assets.xcassets */,
42ABD074210B54E100BEEFF4 /* Base.lproj */,
42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */,
42ABD071210B54E100BEEFF4 /* Main.storyboard */,
42ABD073210B54E100BEEFF4 /* ViewController.swift */,
);
path = CollectionView;
sourceTree = "<group>";
};
872D5A5921C17D980037D763 /* TableView */ = {
isa = PBXGroup;
children = (
F5F899F41FABA607002E8FDA /* AppDelegate.swift */,
88DEA97D1FCDBD1F006C80EF /* Constants.swift */,
F5F899F61FABA607002E8FDA /* ViewController.swift */,
F5F622441FACA338007C062A /* Cell.swift */,
F5F899F81FABA607002E8FDA /* Main.storyboard */,
F5F899FB1FABA607002E8FDA /* Assets.xcassets */,
F5F899FD1FABA607002E8FDA /* LaunchScreen.storyboard */,
);
path = TableView;
sourceTree = "<group>";
};
872D5A5A21C24E7C0037D763 /* Multilines */ = {
isa = PBXGroup;
children = (
F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */,
872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */,
872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */,
);
path = Multilines;
sourceTree = "<group>";
};
877EFA4321BEE9C40031FC00 /* Builders */ = {
isa = PBXGroup;
children = (
877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */,
877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */,
);
path = Builders;
sourceTree = "<group>";
};
8785E39E211C9C6D00CC9DFD /* Appearance */ = {
isa = PBXGroup;
children = (
F5307E2F1FB0EC9D00EE67C5 /* SkeletonAppearance.swift */,
);
path = Appearance;
sourceTree = "<group>";
};
8785E39F211C9C7C00CC9DFD /* Debug */ = {
isa = PBXGroup;
children = (
8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */,
);
path = Debug;
sourceTree = "<group>";
};
8933C7811EB5B7E0000D00A4 /* Sources */ = {
isa = PBXGroup;
children = (
872D5A5A21C24E7C0037D763 /* Multilines */,
877EFA4321BEE9C40031FC00 /* Builders */,
8785E39F211C9C7C00CC9DFD /* Debug */,
8785E39E211C9C6D00CC9DFD /* Appearance */,
F58A6E7020A8C87100612494 /* Recoverable */,
F5307E331FB1068500EE67C5 /* Collections */,
F5307E341FB106A500EE67C5 /* Extensions */,
F5307E351FB106BF00EE67C5 /* Helpers */,
F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */,
F5307E2F1FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift */,
F587FB83202CBFC8002DB5FE /* SkeletonFlow.swift */,
F5307E2B1FAF6BC900EE67C5 /* SkeletonGradient.swift */,
F5F899E81FAB9D2B002E8FDA /* SkeletonLayer.swift */,
8933C7841EB5B820000D00A4 /* SkeletonView.swift */,
F587FB83202CBFC8002DB5FE /* SkeletonFlow.swift */,
F5D3FB0A209DCAA300003FCF /* SubviewsSkeletonables.swift */,
);
path = Sources;
sourceTree = "<group>";
@@ -262,7 +408,9 @@
F5F622401FAC6E31007C062A /* UIColor+Skeleton.swift */,
F5307E2D1FB0E5E400EE67C5 /* UIView+Frame.swift */,
F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */,
872D5A5521C177E20037D763 /* UIView+Extension.swift */,
F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */,
F5A5E8B3211DCE7000FD20D0 /* Int+Whitespaces.swift */,
);
path = Extensions;
sourceTree = "<group>";
@@ -271,23 +419,26 @@
isa = PBXGroup;
children = (
F5F899D11FAB9630002E8FDA /* AssociationPolicy.swift */,
F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */,
F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */,
F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */,
);
path = Helpers;
sourceTree = "<group>";
};
F58A6E7020A8C87100612494 /* Recoverable */ = {
isa = PBXGroup;
children = (
F58A6E6A20A8C54100612494 /* RecoverableViewState.swift */,
F58A6E6D20A8C66300612494 /* Recoverable.swift */,
);
path = Recoverable;
sourceTree = "<group>";
};
F5F899F31FABA607002E8FDA /* Example */ = {
isa = PBXGroup;
children = (
F5F899F41FABA607002E8FDA /* AppDelegate.swift */,
88DEA97D1FCDBD1F006C80EF /* Constants.swift */,
F5F899F61FABA607002E8FDA /* ViewController.swift */,
F5F622441FACA338007C062A /* Cell.swift */,
F5F899F81FABA607002E8FDA /* Main.storyboard */,
F5F899FB1FABA607002E8FDA /* Assets.xcassets */,
F5F899FD1FABA607002E8FDA /* LaunchScreen.storyboard */,
872D5A5921C17D980037D763 /* TableView */,
872D5A5821C17D850037D763 /* CollectionView */,
);
path = Example;
sourceTree = "<group>";
@@ -302,6 +453,7 @@
F5F89A061FABA725002E8FDA /* Example */ = {
isa = PBXGroup;
children = (
42ABD076210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist */,
F5F89A001FABA607002E8FDA /* SkeletonViewExampleInfo.plist */,
);
name = Example;
@@ -314,6 +466,8 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
8748240D20C6A88A00E92179 /* UIView+IBInspectable.swift in Headers */,
8748240E20C6A88E00E92179 /* ContainsMultilineText.swift in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -321,6 +475,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
3B83EE4820C41488005178A4 /* UIView+IBInspectable.swift in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -345,6 +500,25 @@
productReference = 17DD0E00207FB27400C56334 /* SkeletonView.framework */;
productType = "com.apple.product-type.framework";
};
42ABD05A210B548200BEEFF4 /* SkeletonViewExampleUICollectionView */ = {
isa = PBXNativeTarget;
buildConfigurationList = 42ABD06A210B548200BEEFF4 /* Build configuration list for PBXNativeTarget "SkeletonViewExampleUICollectionView" */;
buildPhases = (
42ABD05D210B548200BEEFF4 /* Sources */,
42ABD062210B548200BEEFF4 /* Frameworks */,
42ABD064210B548200BEEFF4 /* Resources */,
42ABD068210B548200BEEFF4 /* Embed Frameworks */,
);
buildRules = (
);
dependencies = (
42ABD05B210B548200BEEFF4 /* PBXTargetDependency */,
);
name = SkeletonViewExampleUICollectionView;
productName = SkeletonViewExample;
productReference = 42ABD06D210B548200BEEFF4 /* SkeletonViewExampleUICollectionView.app */;
productType = "com.apple.product-type.application";
};
52D6D97B1BEFF229002C0205 /* SkeletonView-iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "SkeletonView-iOS" */;
@@ -389,28 +563,31 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0910;
LastUpgradeCheck = 0930;
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = SkeletonView;
TargetAttributes = {
17DD0DFF207FB27400C56334 = {
CreatedOnToolsVersion = 9.3;
ProvisioningStyle = Automatic;
};
42ABD05A210B548200BEEFF4 = {
LastSwiftMigration = 0940;
ProvisioningStyle = Automatic;
};
52D6D97B1BEFF229002C0205 = {
CreatedOnToolsVersion = 7.1;
DevelopmentTeam = KWEMDK92F4;
LastSwiftMigration = 0800;
LastSwiftMigration = 1000;
};
F5F899F11FABA607002E8FDA = {
CreatedOnToolsVersion = 9.1;
DevelopmentTeam = KWEMDK92F4;
LastSwiftMigration = 1000;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "SkeletonView" */;
compatibilityVersion = "Xcode 6.3";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
@@ -424,6 +601,7 @@
52D6D97B1BEFF229002C0205 /* SkeletonView-iOS */,
17DD0DFF207FB27400C56334 /* SkeletonView-tvOS */,
F5F899F11FABA607002E8FDA /* SkeletonViewExample */,
42ABD05A210B548200BEEFF4 /* SkeletonViewExampleUICollectionView */,
);
};
/* End PBXProject section */
@@ -436,6 +614,16 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
42ABD064210B548200BEEFF4 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
42ABD07A210B54E200BEEFF4 /* Assets.xcassets in Resources */,
42ABD07C210B54E200BEEFF4 /* Base.lproj in Resources */,
42ABD079210B54E200BEEFF4 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
52D6D97A1BEFF229002C0205 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -460,60 +648,91 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
872D5A5721C177E20037D763 /* UIView+Extension.swift in Sources */,
17DD0E1F207FB32100C56334 /* RecursiveProtocol.swift in Sources */,
872D5A5D21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */,
17DD0E1E207FB32100C56334 /* PrepareForSkeletonProtocol.swift in Sources */,
F51ED28520973CC9008B2434 /* SkeletonReusableCell.swift in Sources */,
17DD0E17207FB28F00C56334 /* SkeletonLayer.swift in Sources */,
8785E3A2211C9CA500CC9DFD /* SkeletonDebug.swift in Sources */,
17DD0E08207FB28900C56334 /* CollectionSkeletonProtocol.swift in Sources */,
F58A6E6C20A8C54100612494 /* RecoverableViewState.swift in Sources */,
17DD0E0B207FB28900C56334 /* SkeletonTableViewProtocols.swift in Sources */,
17DD0E0D207FB28900C56334 /* UITableView+CollectionSkeleton.swift in Sources */,
17DD0E0E207FB28900C56334 /* UIView+CollectionSkeleton.swift in Sources */,
877EFA4921BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */,
F51ED28320973CBB008B2434 /* SkeletonCollectionDelegate.swift in Sources */,
F58A6E6F20A8C66300612494 /* Recoverable.swift in Sources */,
17DD0E18207FB28F00C56334 /* SkeletonView.swift in Sources */,
17DD0E19207FB28F00C56334 /* SkeletonFlow.swift in Sources */,
17DD0E09207FB28900C56334 /* SkeletonCollectionDataSource.swift in Sources */,
17DD0E0C207FB28900C56334 /* UICollectionView+CollectionSkeleton.swift in Sources */,
17DD0E13207FB28C00C56334 /* UIView+UIApplicationDelegate.swift in Sources */,
F5D3FB0C209DCAA300003FCF /* SubviewsSkeletonables.swift in Sources */,
17DD0E12207FB28C00C56334 /* UIView+IBInspectable.swift in Sources */,
17DD0E14207FB28F00C56334 /* SkeletonAnimationBuilder.swift in Sources */,
17DD0E11207FB28C00C56334 /* UIView+Frame.swift in Sources */,
17DD0E15207FB28F00C56334 /* SkeletonDefaultConfig.swift in Sources */,
17DD0E15207FB28F00C56334 /* SkeletonAppearance.swift in Sources */,
872D5A6021C24F8E0037D763 /* UITextView+Multiline.swift in Sources */,
17DD0E10207FB28C00C56334 /* UIColor+Skeleton.swift in Sources */,
F51ED28420973CC6008B2434 /* GenericCollectionView.swift in Sources */,
17DD0E0A207FB28900C56334 /* SkeletonCollectionViewProtocols.swift in Sources */,
17DD0E1C207FB32100C56334 /* AssociationPolicy.swift in Sources */,
877EFA4621BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */,
F5A5E8B5211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */,
17DD0E1D207FB32100C56334 /* ContainsMultilineText.swift in Sources */,
17DD0E0F207FB28C00C56334 /* CALayer+Extensions.swift in Sources */,
17DD0E16207FB28F00C56334 /* SkeletonGradient.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
42ABD05D210B548200BEEFF4 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
42ABD07B210B54E200BEEFF4 /* ViewController.swift in Sources */,
42ABD078210B54E200BEEFF4 /* AppDelegate.swift in Sources */,
42ABD07F210B54E200BEEFF4 /* CollectionViewCell.swift in Sources */,
8785E3A3211C9CE800CC9DFD /* Constants.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
52D6D9771BEFF229002C0205 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
872D5A5621C177E20037D763 /* UIView+Extension.swift in Sources */,
F5F899D01FAA6A4D002E8FDA /* UIView+IBInspectable.swift in Sources */,
872D5A5C21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */,
F54CF5E42024CEB000330B0D /* UIView+CollectionSkeleton.swift in Sources */,
F5307E371FB1076E00EE67C5 /* SkeletonTableViewProtocols.swift in Sources */,
8933C7851EB5B820000D00A4 /* SkeletonView.swift in Sources */,
F5307E301FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift in Sources */,
8785E3A1211C9C9800CC9DFD /* SkeletonDebug.swift in Sources */,
F5307E301FB0EC9D00EE67C5 /* SkeletonAppearance.swift in Sources */,
F58A6E6B20A8C54100612494 /* RecoverableViewState.swift in Sources */,
F54CF5E52024CEB000330B0D /* UITableView+CollectionSkeleton.swift in Sources */,
F5307E321FB0F42F00EE67C5 /* RecursiveProtocol.swift in Sources */,
F5F622431FAC81FD007C062A /* CALayer+Extensions.swift in Sources */,
877EFA4821BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */,
F5F899ED1FAB9F04002E8FDA /* CollectionSkeletonProtocol.swift in Sources */,
F58A6E6E20A8C66300612494 /* Recoverable.swift in Sources */,
F5307E2C1FAF6BC900EE67C5 /* SkeletonGradient.swift in Sources */,
F587FB86202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift in Sources */,
F5F899EB1FAB9DA3002E8FDA /* SkeletonCollectionDataSource.swift in Sources */,
F5F622411FAC6E31007C062A /* UIColor+Skeleton.swift in Sources */,
F587FB84202CBFC8002DB5FE /* SkeletonFlow.swift in Sources */,
F5D3FB0B209DCAA300003FCF /* SubviewsSkeletonables.swift in Sources */,
F5F899D21FAB9630002E8FDA /* AssociationPolicy.swift in Sources */,
F54CF5E62024CEB000330B0D /* UICollectionView+CollectionSkeleton.swift in Sources */,
F56B94461FAE20AF0095662F /* PrepareForSkeletonProtocol.swift in Sources */,
F5307E391FB1078E00EE67C5 /* SkeletonCollectionViewProtocols.swift in Sources */,
872D5A5F21C24F8E0037D763 /* UITextView+Multiline.swift in Sources */,
F51DF873206E91FB00D23301 /* GenericCollectionView.swift in Sources */,
F5307E3B1FB123C100EE67C5 /* ContainsMultilineText.swift in Sources */,
F5F899E91FAB9D2B002E8FDA /* SkeletonLayer.swift in Sources */,
F5307E2E1FB0E5E400EE67C5 /* UIView+Frame.swift in Sources */,
877EFA4521BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */,
F5A5E8B4211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */,
F51DF871206E91B300D23301 /* SkeletonReusableCell.swift in Sources */,
F51DF879206E9F5500D23301 /* SkeletonCollectionDelegate.swift in Sources */,
F51DE1091FBF70A70037919A /* SkeletonAnimationBuilder.swift in Sources */,
@@ -534,6 +753,11 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
42ABD05B210B548200BEEFF4 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 52D6D97B1BEFF229002C0205 /* SkeletonView-iOS */;
targetProxy = 42ABD05C210B548200BEEFF4 /* PBXContainerItemProxy */;
};
F5307E441FB3B84500EE67C5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 52D6D97B1BEFF229002C0205 /* SkeletonView-iOS */;
@@ -587,7 +811,7 @@
SDKROOT = appletvos;
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.0;
};
@@ -619,16 +843,70 @@
SDKROOT = appletvos;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.0;
};
name = Release;
};
42ABD06B210B548200BEEFF4 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "Configs/SkeletonViewExampleCollectionview-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
42ABD06C210B548200BEEFF4 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "Configs/SkeletonViewExampleCollectionview-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
52D6D98E1BEFF229002C0205 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@@ -677,7 +955,7 @@
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
@@ -688,6 +966,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@@ -728,7 +1007,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
@@ -744,7 +1023,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = KWEMDK92F4;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
@@ -757,7 +1036,7 @@
PRODUCT_NAME = SkeletonView;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
@@ -769,7 +1048,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = KWEMDK92F4;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
@@ -781,7 +1060,7 @@
PRODUCT_NAME = SkeletonView;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
};
name = Release;
};
@@ -797,7 +1076,7 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = KWEMDK92F4;
DEVELOPMENT_TEAM = "";
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "$(SRCROOT)/Configs/SkeletonViewExampleInfo.plist";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
@@ -805,7 +1084,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@@ -822,7 +1101,7 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = KWEMDK92F4;
DEVELOPMENT_TEAM = "";
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "$(SRCROOT)/Configs/SkeletonViewExampleInfo.plist";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
@@ -830,7 +1109,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
@@ -847,6 +1126,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
42ABD06A210B548200BEEFF4 /* Build configuration list for PBXNativeTarget "SkeletonViewExampleUICollectionView" */ = {
isa = XCConfigurationList;
buildConfigurations = (
42ABD06B210B548200BEEFF4 /* Debug */,
42ABD06C210B548200BEEFF4 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "SkeletonView" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>FILEHEADER</key>
<string> ___COPYRIGHT___</string>
</dict>
</plist>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0930"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0930"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -0,0 +1,35 @@
// Copyright © 2017 SkeletonView. All rights reserved.
import UIKit
public protocol Appearance {
var tintColor: UIColor { get set }
var gradient: SkeletonGradient { get set }
var multilineHeight: CGFloat { get set }
var multilineSpacing: CGFloat { get set }
var multilineLastLineFillPercent: Int { get set }
var multilineCornerRadius: Int { get set }
}
public enum SkeletonAppearance {
public static var `default`: Appearance = SkeletonViewAppearance.shared
}
// codebeat:disable[TOO_MANY_IVARS]
class SkeletonViewAppearance: Appearance {
static var shared = SkeletonViewAppearance()
var tintColor: UIColor = .clouds
var gradient: SkeletonGradient = SkeletonGradient(baseColor: .clouds)
var multilineHeight: CGFloat = 15
var multilineSpacing: CGFloat = 10
var multilineLastLineFillPercent: Int = 70
var multilineCornerRadius: Int = 0
}
// codebeat:enable[TOO_MANY_IVARS]
@@ -0,0 +1,41 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
/// Object that facilitates the creation of skeleton layers,
/// based on the builder pattern
class SkeletonLayerBuilder {
var skeletonType: SkeletonType?
var colors: [UIColor] = []
var holder: UIView?
func setSkeletonType(_ type: SkeletonType) -> SkeletonLayerBuilder {
self.skeletonType = type
return self
}
func addColor(_ color: UIColor) -> SkeletonLayerBuilder {
return addColors([color])
}
func addColors(_ colors: [UIColor]) -> SkeletonLayerBuilder {
self.colors.append(contentsOf: colors)
return self
}
func setHolder(_ holder: UIView) -> SkeletonLayerBuilder {
self.holder = holder
return self
}
func build() -> SkeletonLayer? {
guard let type = skeletonType,
let holder = holder
else { return nil }
return SkeletonLayer(withType: type,
usingColors: colors,
andSkeletonHolder: holder)
}
}
@@ -0,0 +1,52 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
/// Object that facilitates the creation of skeleton layers for multiline
/// elements, based on the builder pattern
class SkeletonMultilineLayerBuilder {
var skeletonType: SkeletonType?
var index: Int?
var width: CGFloat?
var cornerRadius: Int?
func setSkeletonType(_ type: SkeletonType) -> SkeletonMultilineLayerBuilder {
self.skeletonType = type
return self
}
func setIndex(_ index: Int) -> SkeletonMultilineLayerBuilder {
self.index = index
return self
}
func setWidth(_ width: CGFloat) -> SkeletonMultilineLayerBuilder {
self.width = width
return self
}
func setCornerRadius(_ radius: Int) -> SkeletonMultilineLayerBuilder {
self.cornerRadius = radius
return self
}
func build() -> CALayer? {
guard let type = skeletonType,
let index = index,
let width = width,
let radius = cornerRadius
else { return nil }
let spaceRequiredForEachLine = SkeletonAppearance.default.multilineHeight + SkeletonAppearance.default.multilineSpacing
let layer = type.layer
layer.anchorPoint = .zero
layer.name = CALayer.skeletonSubLayersName
layer.frame = CGRect(x: 0.0, y: CGFloat(index) * spaceRequiredForEachLine, width: width, height: SkeletonAppearance.default.multilineHeight)
layer.cornerRadius = CGFloat(radius)
layer.masksToBounds = true
return layer
}
}
@@ -28,6 +28,6 @@ extension CollectionSkeleton where Self: UIScrollView {
var estimatedNumberOfRows: Int { return 0 }
func addDummyDataSource() {}
func removeDummyDataSource(reloadAfter: Bool) {}
func disableUserInteraction() { isUserInteractionEnabled = false }
func enableUserInteraction() { isUserInteractionEnabled = true }
func disableUserInteraction() { isScrollEnabled = false }
func enableUserInteraction() { isScrollEnabled = true }
}
@@ -16,17 +16,17 @@ extension UICollectionView: CollectionSkeleton {
}
var skeletonDataSource: SkeletonCollectionDataSource? {
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
set {
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource, newValue, AssociationPolicy.retain.objc)
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDataSource)
self.dataSource = newValue
}
}
var skeletonDelegate: SkeletonCollectionDelegate? {
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
set {
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate, newValue, AssociationPolicy.retain.objc)
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDelegate)
self.delegate = newValue
}
}
@@ -36,7 +36,7 @@ extension UICollectionView: CollectionSkeleton {
!(originalDataSource is SkeletonCollectionDataSource)
else { return }
let dataSource = SkeletonCollectionDataSource(collectionViewDataSource: originalDataSource, rowHeight: 0.0)
let dataSource = SkeletonCollectionDataSource(collectionViewDataSource: originalDataSource)
self.skeletonDataSource = dataSource
reloadData()
}
@@ -52,3 +52,20 @@ extension UICollectionView: CollectionSkeleton {
extension UICollectionView: GenericCollectionView {
var scrollView: UIScrollView { return self }
}
public extension UICollectionView {
func prepareSkeleton(completion: @escaping (Bool) -> Void) {
guard let originalDataSource = self.dataSource as? SkeletonCollectionViewDataSource,
!(originalDataSource is SkeletonCollectionDataSource)
else { return }
let dataSource = SkeletonCollectionDataSource(collectionViewDataSource: originalDataSource, rowHeight: 0.0)
self.skeletonDataSource = dataSource
performBatchUpdates({
self.reloadData()
}) { (done) in
completion(done)
}
}
}
@@ -36,7 +36,7 @@ extension SkeletonCollectionDataSource: UITableViewDataSource {
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = originalTableViewDataSource?.collectionSkeletonView(tableView, cellIdenfierForRowAt: indexPath) ?? ""
let cellIdentifier = originalTableViewDataSource?.collectionSkeletonView(tableView, cellIdentifierForRowAt: indexPath) ?? ""
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
return cell
}
@@ -11,7 +11,7 @@ import UIKit
public protocol SkeletonTableViewDataSource: UITableViewDataSource {
func numSections(in collectionSkeletonView: UITableView) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
}
public extension SkeletonTableViewDataSource {
@@ -21,6 +21,13 @@ public extension SkeletonTableViewDataSource {
}
func numSections(in collectionSkeletonView: UITableView) -> Int { return 1 }
/// Keeping the misspelled version around until it can be deprecated
/// Right now, it just calls the new correctly spelled method and returns its result
@available(*, deprecated, renamed: "collectionSkeletonView(_:cellIdentifierForRowAt:)")
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return collectionSkeletonView(skeletonView, cellIdentifierForRowAt: indexPath)
}
}
public protocol SkeletonTableViewDelegate: UITableViewDelegate {
@@ -15,17 +15,17 @@ extension UITableView: CollectionSkeleton {
}
var skeletonDataSource: SkeletonCollectionDataSource? {
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
set {
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource, newValue, AssociationPolicy.retain.objc)
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDataSource)
self.dataSource = newValue
}
}
var skeletonDelegate: SkeletonCollectionDelegate? {
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
set {
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate, newValue, AssociationPolicy.retain.objc)
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDelegate)
self.delegate = newValue
}
}
@@ -34,7 +34,9 @@ extension UITableView: CollectionSkeleton {
guard let originalDataSource = self.dataSource as? SkeletonTableViewDataSource,
!(originalDataSource is SkeletonCollectionDataSource)
else { return }
let dataSource = SkeletonCollectionDataSource(tableViewDataSource: originalDataSource, rowHeight: calculateRowHeight())
let rowHeight = calculateRowHeight()
let dataSource = SkeletonCollectionDataSource(tableViewDataSource: originalDataSource,
rowHeight: rowHeight)
self.skeletonDataSource = dataSource
reloadData()
}
@@ -53,11 +55,8 @@ extension UITableView: CollectionSkeleton {
}
private func calculateRowHeight() -> CGFloat {
guard rowHeight == UITableViewAutomaticDimension else { return rowHeight }
guard rowHeight == UITableView.automaticDimension else { return rowHeight }
rowHeight = estimatedRowHeight
return estimatedRowHeight
}
}
+50
View File
@@ -0,0 +1,50 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import Foundation
import UIKit
enum SkeletonEnvironmentKey: String {
case debugMode = "SKELETON_DEBUG"
}
extension Dictionary {
subscript (_ key: SkeletonEnvironmentKey) -> Value? {
return self[key.rawValue as! Key]
}
}
func printSkeletonHierarchy(in view: UIView) {
skeletonLog(view.skeletonHierarchy())
}
func skeletonLog(_ message: String) {
if let _ = ProcessInfo.processInfo.environment[.debugMode] {
print(message)
}
}
extension UIView {
public var skeletonDescription: String {
var description = "<\(type(of: self)): \(Unmanaged.passUnretained(self).toOpaque())"
let subSkeletons = subviewsSkeletonables
if subSkeletons.count != 0 {
description += " | (\(subSkeletons.count)) subSkeletons"
}
if isSkeletonable {
description += " | ☠️ "
}
return description + ">"
}
public func skeletonHierarchy(index: Int = 0) -> String {
var description = index == 0 ? "\n ⬇⬇ ☠️ Root view hierarchy with Skeletons ⬇⬇ \n" : ""
description += "\(index == 0 ? "\n" : 3.whitespaces) \(skeletonDescription) \n"
subviewsToSkeleton.forEach {
description += (index + 2).whitespaces
description += $0.skeletonHierarchy(index: index + 1)
}
return description
}
}
+35 -23
View File
@@ -10,18 +10,20 @@ import UIKit
extension CALayer {
@objc func tint(withColors colors: [UIColor]) {
recursiveSearch(inArray: skeletonSublayers,
leafBlock: { backgroundColor = colors.first?.cgColor }) {
$0.tint(withColors: colors)
skeletonSublayers.recursiveSearch(leafBlock: {
backgroundColor = colors.first?.cgColor
}) {
$0.tint(withColors: colors)
}
}
}
extension CAGradientLayer {
override func tint(withColors colors: [UIColor]) {
recursiveSearch(inArray: skeletonSublayers,
leafBlock: { self.colors = colors.map { $0.cgColor } }) {
$0.tint(withColors: colors)
skeletonSublayers.recursiveSearch(leafBlock: {
self.colors = colors.map { $0.cgColor }
}) {
$0.tint(withColors: colors)
}
}
}
@@ -36,22 +38,30 @@ extension CALayer {
return sublayers?.filter { $0.name == CALayer.skeletonSubLayersName } ?? [CALayer]()
}
func addMultilinesLayers(lines: Int, type: SkeletonType, lastLineFillPercent: Int) {
func addMultilinesLayers(lines: Int, type: SkeletonType, lastLineFillPercent: Int, multilineCornerRadius: Int) {
let numberOfSublayers = calculateNumLines(maxLines: lines)
for index in 0..<numberOfSublayers {
let layerBuilder = SkeletonMultilineLayerBuilder()
.setSkeletonType(type)
.setCornerRadius(multilineCornerRadius)
(0..<numberOfSublayers).forEach { index in
var width = bounds.width
if index == numberOfSublayers-1 && numberOfSublayers != 1 {
width = width * CGFloat(lastLineFillPercent)/100;
if index == numberOfSublayers - 1 && numberOfSublayers != 1 {
width = width * CGFloat(lastLineFillPercent) / 100;
}
if let layer = layerBuilder
.setIndex(index)
.setWidth(width)
.build() {
addSublayer(layer)
}
let layer = SkeletonLayerFactory().makeMultilineLayer(withType: type, for: index, width: width)
addSublayer(layer)
}
}
private func calculateNumLines(maxLines: Int) -> Int {
let spaceRequitedForEachLine = SkeletonDefaultConfig.multilineHeight + SkeletonDefaultConfig.multilineSpacing
let spaceRequitedForEachLine = SkeletonAppearance.default.multilineHeight + SkeletonAppearance.default.multilineSpacing
var numberOfSublayers = Int(round(CGFloat(bounds.height)/CGFloat(spaceRequitedForEachLine)))
if maxLines != 0, maxLines <= numberOfSublayers { numberOfSublayers = maxLines }
return numberOfSublayers
@@ -66,7 +76,7 @@ public extension CALayer {
pulseAnimation.fromValue = backgroundColor
pulseAnimation.toValue = UIColor(cgColor: backgroundColor!).complementaryColor.cgColor
pulseAnimation.duration = 1
pulseAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
pulseAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
pulseAnimation.autoreverses = true
pulseAnimation.repeatCount = .infinity
return pulseAnimation
@@ -84,23 +94,25 @@ public extension CALayer {
let animGroup = CAAnimationGroup()
animGroup.animations = [startPointAnim, endPointAnim]
animGroup.duration = 1.5
animGroup.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
animGroup.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
animGroup.repeatCount = .infinity
return animGroup
}
func playAnimation(_ anim: SkeletonLayerAnimation, key: String) {
recursiveSearch(inArray: skeletonSublayers,
leafBlock: { add(anim(self), forKey: key) }) {
$0.playAnimation(anim, key: key)
skeletonSublayers.recursiveSearch(leafBlock: {
add(anim(self), forKey: key)
}) {
$0.playAnimation(anim, key: key)
}
}
func stopAnimation(forKey key: String) {
recursiveSearch(inArray: skeletonSublayers,
leafBlock: { removeAnimation(forKey: key) }) {
$0.stopAnimation(forKey: key)
skeletonSublayers.recursiveSearch(leafBlock: {
removeAnimation(forKey: key)
}) {
$0.stopAnimation(forKey: key)
}
}
}
+14
View File
@@ -0,0 +1,14 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import Foundation
extension Int {
var whitespace: String {
return whitespaces
}
var whitespaces: String {
return String(repeating: " ", count: self)
}
}
+3 -9
View File
@@ -1,13 +1,8 @@
//
// UIColor+Skeleton.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 03/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
// codebeat:disable[TOO_MANY_IVARS]
extension UIColor {
convenience init(_ hex: UInt) {
@@ -20,9 +15,8 @@ extension UIColor {
}
func isLight() -> Bool {
// algorithm from: http://www.w3.org/WAI/ER/WD-AERT/#color-contrast
guard let components = cgColor.components,
components.count >= 3 else { return false }
components.count >= 3 else { return false }
let brightness = ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000
return !(brightness < 0.5)
}
@@ -72,4 +66,4 @@ public extension UIColor {
static var silver = UIColor(0xbdc3c7)
static var asbestos = UIColor(0x7f8c8d)
}
// codebeat:enable[TOO_MANY_IVARS]
+42
View File
@@ -0,0 +1,42 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
// codebeat:disable[TOO_MANY_IVARS]
enum ViewAssociatedKeys {
static var skeletonable = "skeletonable"
static var status = "status"
static var skeletonLayer = "layer"
static var flowDelegate = "flowDelegate"
static var isSkeletonAnimated = "isSkeletonAnimated"
static var viewState = "viewState"
}
// codebeat:enable[TOO_MANY_IVARS]
extension UIView {
enum Status {
case on
case off
}
var flowDelegate: SkeletonFlowDelegate? {
get { return ao_get(pkey: &ViewAssociatedKeys.flowDelegate) as? SkeletonFlowDelegate }
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.flowDelegate) }
}
var skeletonLayer: SkeletonLayer? {
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonLayer) as? SkeletonLayer }
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.skeletonLayer) }
}
var status: Status! {
get { return ao_get(pkey: &ViewAssociatedKeys.status) as? Status ?? .off }
set { ao_set(newValue ?? .off, pkey: &ViewAssociatedKeys.status) }
}
var skeletonIsAnimated: Bool! {
get { return ao_get(pkey: &ViewAssociatedKeys.isSkeletonAnimated) as? Bool ?? false }
set { ao_set(newValue ?? false, pkey: &ViewAssociatedKeys.isSkeletonAnimated) }
}
}
+18 -2
View File
@@ -12,6 +12,18 @@ import UIKit
extension UIView {
var maxBoundsEstimated: CGRect {
if let parentStackView = (superview as? UIStackView) {
var origin: CGPoint = .zero
switch parentStackView.alignment {
case .center:
origin.x = maxWidthEstimated / 2
case .trailing:
origin.x = maxWidthEstimated
default:
break
}
return CGRect(origin: origin, size: maxSizeEstimated)
}
return CGRect(origin: .zero, size: maxSizeEstimated)
}
@@ -20,12 +32,12 @@ extension UIView {
}
var maxWidthEstimated: CGFloat {
let constraintsWidth = constraints.filter({ $0.firstAttribute == NSLayoutAttribute.width })
let constraintsWidth = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutConstraint.Attribute.width })
return max(between: frame.size.width, andContantsOf: constraintsWidth)
}
var maxHeightEstimated: CGFloat {
let constraintsHeight = constraints.filter({ $0.firstAttribute == NSLayoutAttribute.height })
let constraintsHeight = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutConstraint.Attribute.height })
return max(between: frame.size.height, andContantsOf: constraintsHeight)
}
@@ -37,4 +49,8 @@ extension UIView {
})
return max
}
var nonContentSizeLayoutConstraints: [NSLayoutConstraint] {
return constraints.filter({ "\(type(of: $0))" != "NSContentSizeLayoutConstraint" })
}
}
+2 -44
View File
@@ -1,21 +1,7 @@
//
// UIView+IBInspectable.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 01/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
private enum AssociatedKeys {
static var skeletonable = "skeletonable"
static var status = "status"
static var skeletonLayer = "layer"
static var flowDelegate = "flowDelegate"
static var isSkeletonAnimated = "isSkeletonAnimated"
}
public extension UIView {
@IBInspectable
@@ -27,38 +13,10 @@ public extension UIView {
var isSkeletonActive: Bool {
return status == .on || (subviewsSkeletonables.first(where: { $0.isSkeletonActive }) != nil)
}
}
extension UIView {
enum Status {
case on
case off
}
var flowDelegate: SkeletonFlowDelegate? {
get { return objc_getAssociatedObject(self, &AssociatedKeys.flowDelegate) as? SkeletonFlowDelegate }
set { objc_setAssociatedObject(self, &AssociatedKeys.flowDelegate, newValue, AssociationPolicy.retain.objc) }
}
var skeletonLayer: SkeletonLayer? {
get { return objc_getAssociatedObject(self, &AssociatedKeys.skeletonLayer) as? SkeletonLayer }
set { objc_setAssociatedObject(self, &AssociatedKeys.skeletonLayer, newValue, AssociationPolicy.retain.objc) }
}
var status: Status! {
get { return objc_getAssociatedObject(self, &AssociatedKeys.status) as? Status ?? .off }
set { objc_setAssociatedObject(self, &AssociatedKeys.status, newValue, AssociationPolicy.retain.objc) }
}
var skeletonIsAnimated: Bool! {
get { return objc_getAssociatedObject(self, &AssociatedKeys.isSkeletonAnimated) as? Bool ?? false }
set { objc_setAssociatedObject(self, &AssociatedKeys.isSkeletonAnimated, newValue, AssociationPolicy.retain.objc) }
}
fileprivate var skeletonable: Bool! {
get { return objc_getAssociatedObject(self, &AssociatedKeys.skeletonable) as? Bool ?? false }
set { objc_setAssociatedObject(self, &AssociatedKeys.skeletonable, newValue, AssociationPolicy.retain.objc) }
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonable) as? Bool ?? false }
set { ao_set(newValue ?? false, pkey: &ViewAssociatedKeys.skeletonable) }
}
}
@@ -11,8 +11,8 @@ import UIKit
extension UIView {
enum Constants {
static let becomeActiveNotification = NSNotification.Name.UIApplicationDidBecomeActive
static let enterForegroundNotification = NSNotification.Name.UIApplicationDidEnterBackground
static let becomeActiveNotification = UIApplication.didBecomeActiveNotification
static let enterForegroundNotification = UIApplication.didEnterBackgroundNotification
static let needAnimatedSkeletonKey = "needAnimateSkeleton"
}
+29 -6
View File
@@ -1,10 +1,4 @@
//
// AssociationPolicy.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 02/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import Foundation
@@ -21,3 +15,32 @@ enum AssociationPolicy: UInt {
return objc_AssociationPolicy(rawValue: rawValue)!
}
}
protocol AssociatedObjects: class { }
// transparent wrappers
extension AssociatedObjects {
/// wrapper around `objc_getAssociatedObject`
func ao_get(pkey: UnsafeRawPointer) -> Any? {
return objc_getAssociatedObject(self, pkey)
}
/// wrapper around `objc_setAssociatedObject`
func ao_setOptional(_ value: Any?, pkey: UnsafeRawPointer, policy: AssociationPolicy = .retainNonatomic) {
guard let value = value else { return }
objc_setAssociatedObject(self, pkey, value, policy.objc)
}
/// wrapper around `objc_setAssociatedObject`
func ao_set(_ value: Any, pkey: UnsafeRawPointer, policy: AssociationPolicy = .retainNonatomic) {
objc_setAssociatedObject(self, pkey, value, policy.objc)
}
/// wrapper around 'objc_removeAssociatedObjects'
func ao_removeAll() {
objc_removeAssociatedObjects(self)
}
}
extension NSObject: AssociatedObjects { }
@@ -1,58 +0,0 @@
//
// ContainsMultilineText.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 07/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
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) }
}
}
@@ -14,14 +14,14 @@ protocol PrepareForSkeleton {
extension UILabel: PrepareForSkeleton {
func prepareViewForSkeleton() {
text = nil
text = " "
resignFirstResponder()
}
}
extension UITextView: PrepareForSkeleton {
func prepareViewForSkeleton() {
text = nil
text = " "
resignFirstResponder()
}
}
+10 -19
View File
@@ -1,37 +1,28 @@
//
// HelperProtocols.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 06/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
typealias VoidBlock = () -> Void
typealias RecursiveBlock<T> = (T) -> Void
protocol IterableElement {}
extension UIView: IterableElement {}
extension CALayer: IterableElement {}
//MARK: Recursive
protocol Recursive {
associatedtype Element
func recursiveSearch(inArray array:[Element], leafBlock: VoidBlock, recursiveBlock: RecursiveBlock<Element>)
associatedtype Element: IterableElement
func recursiveSearch(leafBlock: VoidBlock, recursiveBlock: RecursiveBlock<Element>)
}
extension Recursive {
func recursiveSearch(inArray array:[Element], leafBlock: VoidBlock, recursiveBlock: RecursiveBlock<Element>) {
guard array.count > 0 else {
extension Array: Recursive where Element: IterableElement {
func recursiveSearch(leafBlock: VoidBlock, recursiveBlock: RecursiveBlock<Element>) {
guard count > 0 else {
leafBlock()
return
}
array.forEach { recursiveBlock($0) }
forEach { recursiveBlock($0) }
}
}
extension UIView: Recursive {
typealias Element = UIView
}
extension CALayer: Recursive {
typealias Element = CALayer
}
@@ -0,0 +1,18 @@
// Copyright © 2017 SkeletonView. All rights reserved.
import UIKit
enum MultilineAssociatedKeys {
static var lastLineFillingPercent = "lastLineFillingPercent"
static var multilineCornerRadius = "multilineCornerRadius"
}
protocol ContainsMultilineText {
var numLines: Int { get }
var lastLineFillingPercent: Int { get }
var multilineCornerRadius: Int { get }
}
extension ContainsMultilineText {
var numLines: Int { return 0 }
}
@@ -0,0 +1,32 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
public extension UILabel {
@IBInspectable
var lastLineFillPercent: Int {
get { return lastLineFillingPercent }
set { lastLineFillingPercent = min(newValue, 100) }
}
@IBInspectable
var linesCornerRadius: Int {
get { return multilineCornerRadius }
set { multilineCornerRadius = min(newValue, 10) }
}
}
extension UILabel: ContainsMultilineText {
var numLines: Int {
return numberOfLines
}
var lastLineFillingPercent: Int {
get { return ao_get(pkey: &MultilineAssociatedKeys.lastLineFillingPercent) as? Int ?? SkeletonAppearance.default.multilineLastLineFillPercent }
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.lastLineFillingPercent) }
}
var multilineCornerRadius: Int {
get { return ao_get(pkey: &MultilineAssociatedKeys.multilineCornerRadius) as? Int ?? SkeletonAppearance.default.multilineCornerRadius }
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.multilineCornerRadius) }
}
}
@@ -0,0 +1,36 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
public extension UITextView {
@IBInspectable
var lastLineFillPercent: Int {
get { return lastLineFillingPercent }
set { lastLineFillingPercent = min(newValue, 100) }
}
@IBInspectable
var linesCornerRadius: Int {
get { return multilineCornerRadius }
set { multilineCornerRadius = min(newValue, 10) }
}
}
extension UITextView: ContainsMultilineText {
var lastLineFillingPercent: Int {
get {
let defaultValue = SkeletonAppearance.default.multilineLastLineFillPercent
return ao_get(pkey: &MultilineAssociatedKeys.lastLineFillingPercent) as? Int ?? defaultValue
}
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.lastLineFillingPercent) }
}
var multilineCornerRadius: Int {
get {
let defaultValue = SkeletonAppearance.default.multilineCornerRadius
return ao_get(pkey: &MultilineAssociatedKeys.multilineCornerRadius) as? Int ?? defaultValue
}
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.multilineCornerRadius) }
}
}
+74
View File
@@ -0,0 +1,74 @@
//
// Recoverable.swift
// SkeletonView
//
// Created by Juanpe Catalán on 13/05/2018.
// Copyright © 2018 SkeletonView. All rights reserved.
//
import UIKit
protocol Recoverable {
var viewState: RecoverableViewState? { get set }
func saveViewState()
func recoverViewState(forced: Bool)
}
extension UIView: Recoverable {
var viewState: RecoverableViewState? {
get { return ao_get(pkey: &ViewAssociatedKeys.viewState) as? RecoverableViewState }
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.viewState) }
}
@objc func saveViewState() {
viewState = RecoverableViewState(view: self)
}
@objc func recoverViewState(forced: Bool) {
guard let safeViewState = viewState else { return }
layer.cornerRadius = safeViewState.cornerRadius
layer.masksToBounds = safeViewState.clipToBounds
if safeViewState.backgroundColor != backgroundColor || forced {
backgroundColor = safeViewState.backgroundColor
}
}
}
extension UILabel {
override func saveViewState() {
super.saveViewState()
viewState?.text = text
}
override func recoverViewState(forced: Bool) {
super.recoverViewState(forced: forced)
text = text == " " || forced ? viewState?.text : text
}
}
extension UITextView {
override func saveViewState() {
super.saveViewState()
viewState?.text = text
}
override func recoverViewState(forced: Bool) {
super.recoverViewState(forced: forced)
text = text == " " || forced ? viewState?.text : text
}
}
extension UIImageView {
override func saveViewState() {
super.saveViewState()
viewState?.image = image
}
override func recoverViewState(forced: Bool) {
super.recoverViewState(forced: forced)
image = image == nil || forced ? viewState?.image : image
}
}
@@ -0,0 +1,29 @@
//
// RecoverableViewState.swift
// SkeletonView
//
// Created by Juanpe Catalán on 13/05/2018.
// Copyright © 2018 SkeletonView. All rights reserved.
//
import UIKit
struct RecoverableViewState {
var backgroundColor: UIColor?
var cornerRadius: CGFloat
var clipToBounds: Bool
// UI text
var text: String?
// UI image
var image: UIImage?
}
extension RecoverableViewState {
init(view: UIView) {
self.backgroundColor = view.backgroundColor
self.clipToBounds = view.layer.masksToBounds
self.cornerRadius = view.layer.cornerRadius
}
}
+4 -2
View File
@@ -21,7 +21,8 @@ public enum GradientDirection {
public func slidingAnimation(duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation {
return SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: self, duration: duration)
}
// codebeat:disable[ABC]
var startPoint: GradientAnimationPoint {
switch self {
case .leftRight:
@@ -55,6 +56,7 @@ public enum GradientDirection {
return ( from: CGPoint(x:2, y:2), to: CGPoint(x:0, y:0))
}
}
// codebeat:enable[ABC]
}
public class SkeletonAnimationBuilder {
@@ -76,7 +78,7 @@ public class SkeletonAnimationBuilder {
let animGroup = CAAnimationGroup()
animGroup.animations = [startPointAnim, endPointAnim]
animGroup.duration = duration
animGroup.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
animGroup.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
animGroup.repeatCount = .infinity
return animGroup
-22
View File
@@ -1,22 +0,0 @@
//
// SkeletonDefaultConfig.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 06/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
public enum SkeletonDefaultConfig {
public static let tintColor = UIColor.clouds
public static let gradient = SkeletonGradient(baseColor: tintColor)
public static let multilineHeight: CGFloat = 15
public static let multilineSpacing: CGFloat = 10
public static let multilineLastLineFillPercent = 70
}
+10 -7
View File
@@ -1,26 +1,29 @@
//
// SkeletonFlow.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 08/02/2018.
// Copyright © 2018 SkeletonView. All rights reserved.
//
import UIKit
protocol SkeletonFlowDelegate {
func willBeginShowingSkeletons(withRootView rootView: UIView)
func didShowSkeletons(withRootView rootView: UIView)
func willBeginHidingSkeletons(withRootView rootView: UIView)
func didHideSkeletons(withRootView rootView: UIView)
}
class SkeletonFlowHandler: SkeletonFlowDelegate {
func willBeginShowingSkeletons(withRootView rootView: UIView) {
rootView.addAppNotificationsObservers()
}
func didShowSkeletons(withRootView rootView: UIView) {
printSkeletonHierarchy(in: rootView)
}
func willBeginHidingSkeletons(withRootView rootView: UIView) {
rootView.removeAppNoticationsObserver()
}
func didHideSkeletons(withRootView rootView: UIView) {
rootView.flowDelegate = nil
}
}
+1 -17
View File
@@ -8,22 +8,6 @@
import UIKit
class SkeletonLayerFactory {
func makeSkeletonLayer(withType type: SkeletonType, usingColors colors: [UIColor], andHolder holder: UIView) -> SkeletonLayer {
return SkeletonLayer(withType: type, usingColors: colors, andSkeletonHolder: holder)
}
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.0, y: CGFloat(index) * spaceRequiredForEachLine, width: width, height: SkeletonDefaultConfig.multilineHeight)
return layer
}
}
public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation
public enum SkeletonType {
@@ -77,7 +61,7 @@ struct SkeletonLayer {
func addMultilinesIfNeeded() {
guard let multiLineView = holder as? ContainsMultilineText else { return }
maskLayer.addMultilinesLayers(lines: multiLineView.numLines, type: type, lastLineFillPercent: multiLineView.lastLineFillingPercent)
maskLayer.addMultilinesLayers(lines: multiLineView.numLines, type: type, lastLineFillPercent: multiLineView.lastLineFillingPercent, multilineCornerRadius: multiLineView.multilineCornerRadius)
}
}
+46 -75
View File
@@ -1,49 +1,41 @@
//
// SkeletonView.swift
// SkeletonView
//
// Created by Juanpe Catalán on 01/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
public extension UIView {
func showSkeleton(usingColor color: UIColor = SkeletonDefaultConfig.tintColor) {
func showSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor) {
showSkeleton(withType: .solid, usingColors: [color])
}
func showGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonDefaultConfig.gradient) {
func showGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient) {
showSkeleton(withType: .gradient, usingColors: gradient.colors)
}
func showAnimatedSkeleton(usingColor color: UIColor = SkeletonDefaultConfig.tintColor, animation: SkeletonLayerAnimation? = nil) {
func showAnimatedSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor, animation: SkeletonLayerAnimation? = nil) {
showSkeleton(withType: .solid, usingColors: [color], animated: true, animation: animation)
}
func showAnimatedGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonDefaultConfig.gradient, animation: SkeletonLayerAnimation? = nil) {
func showAnimatedGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient, animation: SkeletonLayerAnimation? = nil) {
showSkeleton(withType: .gradient, usingColors: gradient.colors, animated: true, animation: animation)
}
func hideSkeleton(reloadDataAfter reload: Bool = true) {
flowDelegate?.willBeginHidingSkeletons(withRootView: self)
recursiveHideSkeleton(reloadDataAfter: reload)
recursiveHideSkeleton(reloadDataAfter: reload, root: self)
}
func startSkeletonAnimation(_ anim: SkeletonLayerAnimation? = nil) {
skeletonIsAnimated = true
recursiveSearch(inArray: subviewsSkeletonables,
leafBlock: startSkeletonLayerAnimationBlock(anim)) {
$0.startSkeletonAnimation(anim)
}
subviewsSkeletonables.recursiveSearch(leafBlock: startSkeletonLayerAnimationBlock(anim)) { subview in
subview.startSkeletonAnimation(anim)
}
}
func stopSkeletonAnimation() {
skeletonIsAnimated = false
recursiveSearch(inArray: subviewsSkeletonables,
leafBlock: stopSkeletonLayerAnimationBlock) {
$0.stopSkeletonAnimation()
subviewsSkeletonables.recursiveSearch(leafBlock: stopSkeletonLayerAnimationBlock) { subview in
subview.stopSkeletonAnimation()
}
}
}
@@ -54,31 +46,39 @@ extension UIView {
skeletonIsAnimated = animated
flowDelegate = SkeletonFlowHandler()
flowDelegate?.willBeginShowingSkeletons(withRootView: self)
recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation)
recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation, root: self)
}
fileprivate func recursiveShowSkeleton(withType type: SkeletonType = .solid, usingColors colors: [UIColor], animated: Bool = false, animation: SkeletonLayerAnimation? = nil) {
fileprivate func recursiveShowSkeleton(withType type: SkeletonType, usingColors colors: [UIColor], animated: Bool, animation: SkeletonLayerAnimation?, root: UIView? = nil) {
addDummyDataSourceIfNeeded()
recursiveSearch(inArray: subviewsSkeletonables,
leafBlock: {
guard !isSkeletonActive else { return }
isUserInteractionEnabled = false
(self as? PrepareForSkeleton)?.prepareViewForSkeleton()
addSkeletonLayer(withType: type, usingColors: colors, animated: animated, animation: animation)
}) {
$0.recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation)
subviewsSkeletonables.recursiveSearch(leafBlock: {
guard !isSkeletonActive else { return }
isUserInteractionEnabled = false
saveViewState()
(self as? PrepareForSkeleton)?.prepareViewForSkeleton()
addSkeletonLayer(withType: type, usingColors: colors, animated: animated, animation: animation)
}) { subview in
subview.recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation)
}
if let root = root {
flowDelegate?.didShowSkeletons(withRootView: root)
}
}
fileprivate func recursiveHideSkeleton(reloadDataAfter reload: Bool = true) {
removeDummyDataSourceIfNeeded()
fileprivate func recursiveHideSkeleton(reloadDataAfter reload: Bool, root: UIView? = nil) {
removeDummyDataSourceIfNeeded(reloadAfter: reload)
isUserInteractionEnabled = true
recursiveSearch(inArray: subviewsSkeletonables,
leafBlock: {
removeSkeletonLayer()
}, recursiveBlock: {
$0.recursiveHideSkeleton(reloadDataAfter: reload)
})
subviewsSkeletonables.recursiveSearch(leafBlock: {
recoverViewState(forced: false)
removeSkeletonLayer()
}) { subview in
subview.recursiveHideSkeleton(reloadDataAfter: reload)
}
if let root = root {
flowDelegate?.didHideSkeletons(withRootView: root)
}
}
fileprivate func startSkeletonLayerAnimationBlock(_ anim: SkeletonLayerAnimation? = nil) -> VoidBlock {
@@ -96,48 +96,19 @@ extension UIView {
}
}
extension UIView {
@objc var subviewsSkeletonables: [UIView] {
return subviews.filter { $0.isSkeletonable }
}
}
extension UITableView {
override var subviewsSkeletonables: [UIView] {
return visibleCells.filter { $0.isSkeletonable }
}
}
extension UITableViewCell {
override var subviewsSkeletonables: [UIView] {
return contentView.subviews.filter { $0.isSkeletonable }
}
}
extension UICollectionView {
override var subviewsSkeletonables: [UIView] {
return subviews.filter { $0.isSkeletonable }
}
}
extension UICollectionViewCell {
override var subviewsSkeletonables: [UIView] {
return contentView.subviews.filter { $0.isSkeletonable }
}
}
extension UIStackView {
override var subviewsSkeletonables: [UIView] {
return arrangedSubviews.filter { $0.isSkeletonable }
}
}
extension UIView {
func addSkeletonLayer(withType type: SkeletonType, usingColors colors: [UIColor], gradientDirection direction: GradientDirection? = nil, animated: Bool, animation: SkeletonLayerAnimation? = nil) {
self.skeletonLayer = SkeletonLayerFactory().makeSkeletonLayer(withType: type, usingColors: colors, andHolder: self)
layer.insertSublayer(skeletonLayer!.contentLayer, at: UInt32.max)
if animated { skeletonLayer!.start(animation) }
guard let skeletonLayer = SkeletonLayerBuilder()
.setSkeletonType(type)
.addColors(colors)
.setHolder(self)
.build()
else { return }
self.skeletonLayer = skeletonLayer
layer.insertSublayer(skeletonLayer.contentLayer, at: UInt32.max)
if animated { skeletonLayer.start(animation) }
status = .on
}
+48
View File
@@ -0,0 +1,48 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
extension UIView {
@objc var subviewsSkeletonables: [UIView] {
return subviewsToSkeleton.filter { $0.isSkeletonable }
}
@objc var subviewsToSkeleton: [UIView] {
return subviews
}
}
extension UITableView {
override var subviewsToSkeleton: [UIView] {
return visibleCells
}
}
extension UITableViewCell {
override var subviewsToSkeleton: [UIView] {
return contentView.subviews
}
}
extension UICollectionView {
override var subviewsToSkeleton: [UIView] {
return subviews
}
}
extension UICollectionViewCell {
override var subviewsToSkeleton: [UIView] {
return contentView.subviews
}
}
extension UIStackView {
override var subviewsToSkeleton: [UIView] {
return arrangedSubviews
}
}
+13
View File
@@ -0,0 +1,13 @@
default_platform(:ios)
podspec_name = "SkeletonView.podspec"
lane :release_current do
version = version_get_podspec(path: @podspec_name)
if git_tag_exists(tag: version)
UI.user_error!("The tag #{version} already exists on the repo. To release a new version of the library bump the version on #{@podspec_name}")
end
pod_lib_lint
add_git_tag(tag: "#{version}")
push_git_tags
pod_push
end
+28
View File
@@ -0,0 +1,28 @@
fastlane documentation
================
# Installation
Make sure you have the latest version of the Xcode command line tools installed:
```
xcode-select --install
```
Install _fastlane_ using
```
[sudo] gem install fastlane -NV
```
or alternatively using `brew cask install fastlane`
# Available Actions
### release_current
```
fastlane release_current
```
----
This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).