Compare commits

...

201 Commits

Author SHA1 Message Date
Richard L Zarth III 6f78f5c378 Replace SkeletonCollectionDataSource.automaticNumberOfRows with UITableView.automaticNumberOfSkeletonRows and UICollectionView.automaticNumberOfSkeletonItems (#409) 2021-06-11 22:21:27 +02:00
Juanpe Catalán c8fdd6998d fix bug remove constraints wrongly (#406)
* check if the constraints were modified and then restore the original

* identify constraints added by SkeletonView

* move removal of skeleton constraint to recoverable protocol implementation
2021-06-11 11:33:23 +02:00
Juanpe Catalán 134463e529 Update README.md 2021-06-10 19:40:41 +02:00
Juanpe ee59239c59 Bump version 1.17.1 2021-06-10 17:36:12 +00:00
Juanpe Catalán f1e61aa9c0 fix typo isUserInteractionDisabledWhenSkeletonIsActive (#404)
* fix typo

* update README
2021-06-10 19:34:18 +02:00
Juanpe 135778aa1a Bump version 1.17.0 2021-06-10 17:24:00 +00:00
Juanpe Catalán e8d5eb61d8 update README file 2021-06-10 19:19:29 +02:00
Juanpe Catalán c2a029ed51 create disableWhenSkeletonIsActive property 2021-06-10 19:14:05 +02:00
Sam Harrison a1c8276980 Make CI action build targets, not just clean (#403) 2021-06-10 19:01:01 +02:00
Sam Harrison e9ac3a5ab3 Fix TableViewCell skeleton not being removed & automaticNumberOfRows reference (#402)
* Change TableView subviewsToSkeleton to all subviews

* Fix reference to automaticNumberOfRows from pr #401
2021-06-10 18:59:21 +02:00
Richard L Zarth III fb83a62f7b Add SkeletonCollectionDataSource.automaticNumberOfRows Constant (#401)
* Add automaticNumberOfRows constant to SkeletonCollectionDataSource.

* Update tableView(_:numberOfRowsInSection:) and collectionView(_:numberOfItemsInSection:) to use the new automaticNumberOfRows constant.

* Update README.

* Update README.md

Add an **IMPORTANT!** header to the automaticNumberOfRows mention in the README.

Co-authored-by: Juanpe Catalán <juanpecm@gmail.com>

Co-authored-by: Juanpe Catalán <juanpecm@gmail.com>
2021-06-09 18:45:44 +02:00
Juanpe Catalán 12521c1d87 update Info.plist 2021-05-31 17:20:11 +02:00
Juanpe c266035888 Bump version 1.16.0 2021-05-31 15:09:22 +00:00
Juanpe Catalán 74b5172ea5 use the right delegate method for footers (#394) 2021-05-11 19:15:41 +02:00
Michael Henry 318e629d04 update the UILabel placeholder to use an empty space #388 (#389) 2021-05-11 19:02:41 +02:00
StasMalinovsky 62193db76f Added estimated number of rows for UICollectionViewFlowLayout with vertical scroll direction. (#385) 2021-04-30 13:26:25 +02:00
Juanpe 19e7866d3d Bump version 1.15.0 2021-04-13 06:50:02 +00:00
Corey Davis 36668f450b [BUG] Fix crashing on NaN value (#382) 2021-04-12 18:25:43 +02:00
Michael Henry 1bdb3b9c72 fix #383 UITableViewAlertForLayoutOutsideViewHierarchy warning (#384) 2021-04-12 11:09:48 +02:00
Juanpe 6c4e9091a7 Bump version 1.14.0 2021-04-08 10:03:16 +00:00
Alex Lykesas 61a6efbc7b UnSwizzle methods when SkeletonView is hidden (#381)
* Add InputAccessoryView to textField

* Add method to removeOnce Add unSwizzleMethods that are called when recursiveHideSkeleton

Co-authored-by: Alexandros Lykesas <alexandros.lykesas@untis.at>
2021-04-08 11:59:32 +02:00
Juanpe Catalán 4143a6065c feat: add workaround to simulate the content of labels contained in a stack view (#380) 2021-03-29 18:00:34 +02:00
Juanpe Catalán f3baeedc14 Update pod_lib_lint.yml 2021-02-15 15:44:10 +01:00
Juanpe Catalán 29f7d8fd6a create pod lib lint workflow 2021-02-15 15:39:32 +01:00
Juanpe Catalán 83e1600a73 remove useless code from release workflow 2021-02-15 15:34:26 +01:00
Juanpe Catalán 450cecbd9c update release workflow 2021-02-15 15:33:44 +01:00
Juanpe 1676f8720a Bump version 1.13.0 2021-02-15 14:23:42 +00:00
Juanpe Catalán 32ab6d3852 unify jobs 2021-02-15 15:19:09 +01:00
Josh Brunner 7ce5e0623a Fixed a deprecation warning where the AssociatedObjects protocol was inheriting from class instead of AnyObject per Xcode's suggestion (#376) 2021-02-08 23:06:32 +01:00
Juanpe Catalán f0c7d2d6a0 modify release workflow steps 2021-02-08 10:11:52 +01:00
Juanpe Catalán 26476fd3ed bump version 1.12.1 2021-02-08 10:07:11 +01:00
Juanpe Catalán f0c2cf4ab4 Update stale.yml 2021-02-08 09:53:37 +01:00
Juanpe Catalán cadf8bb136 Update stale.yml 2021-02-08 09:45:48 +01:00
Juanpe Catalán 91c8f953ca Merge branch 'main' of https://github.com/Juanpe/SkeletonView into main 2021-02-08 09:41:38 +01:00
Juanpe Catalán 0a91aebf2a fix: recover isUserInteractionEnabled value when the skeleton disappears (#375) 2021-02-08 09:41:33 +01:00
Juanpe Catalán 7d8a057b64 fix: recover isUserInteractionEnabled value when the skeleton disappears (#375) 2021-02-08 09:38:34 +01:00
Juanpe Catalán 06f7d240ff update all “develop” references (#374) 2021-02-05 21:51:24 +01:00
Juanpe Catalán c4c725eea3 revert change in "change-template" 2021-02-03 14:21:27 +01:00
Juanpe Catalán cc9dbdb499 update "change-template" in release-drafter.yml 2021-02-03 14:20:16 +01:00
Keshavamurthy GN c6dfcf9c1a Fixed: UITextField placeholder text is visible while skeleton is showing (#356)
* Fixed: UITextField placeholder text is visible while skeleton is showing #345
Setting placeholder to nil on prepareViewForSkeleton() method
Created RecoverableTextFieldState, which has textColor and placeholder properties, Added UITextField extension in Recoverable.swift, setting the values on recoverViewStage 'forced' is true

* Update PrepareForSkeletonProtocol.swift
2021-02-03 14:18:30 +01:00
Juanpe Catalán dd7e5243bd fix syntax in release.yml 2021-02-03 14:04:44 +01:00
Juanpe Catalán 7e002ddce0 update release-drafter config considering a feature as minor 2021-02-03 14:00:43 +01:00
Juanpe Catalán 6dcfb325dc remove SwiftLint's warnings (#372) 2021-02-03 09:21:14 +01:00
Juanpe Catalán 7b5739295a Remove CHANGELOG condition in Dangerfile 2021-02-03 09:04:59 +01:00
Juanpe Catalán 2e1808ea50 fix: remove temp height constraints after hiding the skeleton (#371) 2021-02-03 09:03:40 +01:00
Juanpe Catalán cec3aeb781 refactor release.yml file 2021-02-02 08:59:26 +01:00
Juanpe Catalán b16400294d update commit job 2021-02-01 19:44:05 +01:00
Juanpe Catalán abb8ea55f6 remove scripts folder 2021-02-01 19:39:00 +01:00
Juanpe Catalán c833da20f8 Merge branch 'develop' of https://github.com/Juanpe/SkeletonView into develop 2021-02-01 19:36:17 +01:00
Juanpe Catalán 292fadf5b0 bump version 1.12.0 2021-02-01 19:36:06 +01:00
Juanpe Catalán 2ce041901c remove pod lib lint 2021-02-01 19:35:10 +01:00
Juanpe Catalán 961cc77661 update order release workflow 2021-02-01 19:33:23 +01:00
Juanpe Catalán 11b623fb13 Update release.yml 2021-02-01 19:23:48 +01:00
Juanpe Catalán 4899802b01 Update release.yml 2021-02-01 19:14:29 +01:00
Juanpe Catalán 0dfe1ca43b Update release.yml 2021-02-01 19:11:00 +01:00
Juanpe Catalán 499d01c232 Update release.yml 2021-02-01 19:07:22 +01:00
Juanpe Catalán f8f368ab8a Update release.yml 2021-02-01 19:02:14 +01:00
Juanpe Catalán 023a57a763 Merge branch 'develop' of https://github.com/Juanpe/SkeletonView into develop 2021-02-01 19:00:58 +01:00
Juanpe Catalán d426a0916e create new lane to bump podspec version 2021-02-01 19:00:51 +01:00
Juanpe Catalán 611c248c51 Update release.yml 2021-02-01 18:52:49 +01:00
Juanpe Catalán 6f20c34d56 Update release.yml 2021-02-01 18:40:01 +01:00
Juanpe Catalán be25b572f8 Update release.yml 2021-02-01 18:27:51 +01:00
Juanpe Catalán 3c67812453 Update main.yml 2021-02-01 18:23:39 +01:00
Juanpe Catalán 0431eb2a7d Update release.yml 2021-02-01 18:20:55 +01:00
Juanpe Catalán fa0b95d923 Update release-drafter.yml 2021-02-01 18:18:15 +01:00
Juanpe Catalán 523cd1c657 Create release_notes.yml 2021-02-01 18:14:27 +01:00
Juanpe Catalán 29e65c70d3 Create release-drafter.yml 2021-02-01 18:13:12 +01:00
Juanpe Catalán 4b7360a011 Update release.yml 2021-02-01 18:12:11 +01:00
Juanpe Catalán 2753c54216 feat: add script to update version 2021-02-01 18:10:22 +01:00
Juanpe Catalán c7b3bc67be Update release.yml 2021-02-01 18:08:03 +01:00
Juanpe Catalán 0c8bbf1fed Update release.yml 2021-02-01 17:58:55 +01:00
Juanpe Catalán 2e99bc98bf Update release.yml 2021-02-01 17:57:43 +01:00
Juanpe Catalán d5d52d1416 Update release.yml 2021-02-01 17:41:18 +01:00
Juanpe Catalán 91c7735d0c Create release.yml 2021-02-01 15:27:24 +01:00
Juanpe Catalán 18c5c1deb8 feat: remove useless corner radius constraint (#369)
* remove useless corner radius constraint

* update CHANGELOG
2021-02-01 12:13:48 +01:00
Juanpe Catalán 1ecfa31743 feat: add more templates to create issues (#368) 2021-01-31 17:11:46 +01:00
Juanpe Catalán 82b70ea1ee Update validations.yml 2021-01-31 14:20:25 +01:00
Juanpe Catalán d8ee06f1c5 Update validations.yml 2021-01-31 13:44:08 +01:00
Juanpe Catalán 13ce7426dd Update validations.yml 2021-01-31 13:36:38 +01:00
Juanpe Catalán 7d5039536c Update validations.yml 2021-01-31 13:28:12 +01:00
Juanpe Catalán b7a7731784 Update validations.yml 2021-01-31 13:22:01 +01:00
Juanpe Catalán 07f92d098a Update validations.yml 2021-01-31 13:06:33 +01:00
Juanpe Catalán f45405c9d5 Update validations.yml 2021-01-31 12:58:44 +01:00
Juanpe Catalán 768354ca35 Update validations.yml 2021-01-31 12:27:12 +01:00
Juanpe Catalán d5819763a1 Update validations.yml 2021-01-31 12:26:05 +01:00
Juanpe Catalán a73fc993aa Update validations.yml 2021-01-31 12:07:18 +01:00
Juanpe Catalán 0d027b5927 Update validations.yml 2021-01-31 11:50:44 +01:00
Juanpe Catalán bc482f58b3 Update and rename Validations.yml to validations.yml 2021-01-31 11:11:17 +01:00
Juanpe Catalán 78e941e01c Update and rename PR.yml to Validations.yml 2021-01-30 21:25:25 +01:00
Juanpe Catalán 7282008606 Update PR.yml 2021-01-28 10:40:24 +01:00
Juanpe Catalán 52d657faa0 Update PR.yml 2021-01-28 09:50:52 +01:00
Juanpe Catalán cba096becf Update PR.yml 2021-01-28 09:46:17 +01:00
Juanpe Catalán c095f49028 Update PR.yml 2021-01-28 09:34:44 +01:00
Juanpe Catalán 89e292c11b fix: broken workflow link 2021-01-27 17:29:40 +01:00
Juanpe Catalán 5089295771 feat: update CI badge in README files 2021-01-27 17:27:10 +01:00
Juanpe Catalán 39971af22d feat: update Github actions 2021-01-27 17:25:23 +01:00
Juanpe Catalán 81d182259d feat: refactor CI workflows 2021-01-27 17:24:16 +01:00
Juanpe Catalán 1d82545a3d Merge pull request #360 from Juanpe/test_action 2021-01-26 18:06:59 +01:00
Juanpe Catalán 9086d7e874 use GITHUB TOKEN 2021-01-26 18:02:43 +01:00
Juanpe Catalán 666ebdf472 fix syntax error 2021-01-26 17:56:53 +01:00
Juanpe Catalán 5a5d7b66e7 update action trigger 2021-01-26 17:54:50 +01:00
Juanpe Catalán 78f2bbe9f1 udpate danger action 2021-01-26 16:59:22 +01:00
Juanpe Catalán c7133a6711 Merge pull request #357 from keshavamurthy1/fix/refactored_removed_unused_code
Removed duplicate code in SkeletonCollectionDelegate
2021-01-22 14:26:54 +01:00
Juanpe Catalán aee5cdfd8f Merge pull request #359 from keshavamurthy1/fix/skeletonview_respecting_font_height_than_view_height
Fixed: SkeletonView respecting Font's height, rather than the `UIView…
2021-01-22 14:23:12 +01:00
Juanpe Catalán 638c535767 Update checks.yml 2021-01-22 13:36:26 +01:00
Juanpe Catalán 782dd45b33 Update checks.yml 2021-01-22 13:33:38 +01:00
Keshavamurthy GN 073baa6483 Update UIView+Frame.swift 2021-01-22 17:45:36 +05:30
Keshavamurthy GN a26dbf7c89 Reverted the iOS 10 check as per the review comment 2021-01-22 17:28:32 +05:30
Juanpe Catalán 7ebde2b030 Update checks.yml 2021-01-22 12:51:51 +01:00
Keshavamurthy GN 4009ed2b1e Fixed: SkeletonView respecting Font's height, rather than the UIView actual height #353
Using Label height from the HeightConstraint if there is any instead of the Label Font line height
if no HeightConstraint set, will be using SkeletonAppearance.default.multilineHeight as it was before
2021-01-21 12:52:17 +05:30
Keshavamurthy GN a246c16cfe Removed duplicate code in SkeletonCollectionDelegate
Removed check for older versions in UIView+Frame
Removed return on single lines (Implicit Returns)
2021-01-18 18:41:23 +05:30
Juanpe Catalán ef8e4578d7 Update CHANGELOG.md 2020-10-29 14:27:42 +01:00
Juanpe Catalán 46e2180c4b docs: bump version 1.11.0 and update CHANGELOG file 2020-10-29 14:20:20 +01:00
Juanpe Catalán f32c420165 Merge pull request #344 from Juanpe/feature/resize_labels_based_on_number_of_lines
Feature/resize labels based on number of lines
2020-10-29 14:17:57 +01:00
Juanpe Catalán 0ca38d8ea1 doc: update CHANGELOG file 2020-10-29 14:13:16 +01:00
Juanpe Catalán 9ed337ba53 Merge branch 'develop' of https://github.com/Juanpe/SkeletonView into feature/resize_labels_based_on_number_of_lines 2020-10-29 14:12:09 +01:00
Juanpe Catalán 932a1f6e6e Merge pull request #340 from yzhao198/fixMultiLineAndPadding
Fix incorrect padding and multiline layer frame calculation
2020-10-26 08:11:37 +01:00
Yang Zhao 5cd08fb77c Updated CHANGELOG.md. 2020-10-25 18:40:02 -06:00
Yang Zhao f7cb021a93 Fixed incorrect padding, and incorrect multiline layer frame calculation. 2020-10-25 18:32:31 -06:00
Juanpe Catalán 4fbfbeca55 feat: update layout to desired height 2020-10-22 00:48:23 +02:00
Juanpe Catalán c92461ae5c fix: shows at least one line 2020-10-21 14:35:48 +02:00
Juanpe Catalán efed2e7f32 Merge pull request #341 from Juanpe/feature/animation_autoreverses
Support autoreverses in gradient animations
2020-10-19 11:52:16 +02:00
Juanpe Catalán 52cbd3ae2c doc: Include PR #341 in the CHANGELOG file 2020-10-19 11:49:08 +02:00
Juanpe Catalán a349f7174c feat: support autoreverses animations for gradients 2020-10-19 11:41:57 +02:00
Juanpe Catalán a58c97eb1a Update CHANGELOG.md 2020-10-19 10:36:48 +02:00
Juanpe Catalán 8e6fea2de3 Update CHANGELOG.md 2020-10-19 10:36:33 +02:00
Juanpe Catalán bf417d6bf8 Update CHANGELOG.md 2020-10-19 10:36:16 +02:00
Juanpe Catalán c3295531be Merge pull request #339 from mohn93/develop
Add  hiddenWhenSkeletonIsActive property
2020-10-19 10:34:43 +02:00
Mohanned Binmiskeen a87f7302c0 Documentation for isHiddenWhenSkeletonIsActive 2020-10-16 22:46:20 +02:00
Juanpe Catalán 5e22f38454 Upgrade Danger version 3.5.0 2020-10-07 13:07:29 +02:00
Mohanned Binmiskeen 0dbdb9064d Add hiddenWhenSkeletonIsActive property which will hide the view when the skeleton animation starts 2020-10-06 16:03:55 +02:00
Juanpe Catalán c8efc4ccb1 doc: Update CHANGELOG 2020-10-02 17:33:48 +02:00
Juanpe Catalán 01733b358c bump version 1.10.0 2020-10-02 17:15:47 +02:00
Juanpe Catalán 027143499c Merge pull request #337 from Juanpe/feature/support_RTL
RTL support
2020-10-02 17:14:24 +02:00
Juanpe Catalán ae1b5ee790 fix: add condition for tvOS 10.0 2020-10-02 17:08:25 +02:00
Juanpe Catalán 0c0a0ea451 doc: Update CHANGELOG file 2020-10-02 17:01:40 +02:00
Juanpe Catalán 8d090904b4 feat: support RTL 2020-10-02 16:47:00 +02:00
Juanpe Catalán 8d00d5fa16 Merge pull request #336 from Juanpe/fix/replacing_text_wrongly_after_hide
Not replace text when the skeleton disappears
2020-10-02 14:00:28 +02:00
Juanpe Catalán f2bc1cab27 doc: update CHANGELOG file 2020-10-02 13:39:20 +02:00
Juanpe Catalán 83b99f16b1 fix: modified how the skeleton save and restore the original view state of the elements 2020-10-02 13:23:26 +02:00
Juanpe Catalán 7a2cc5d51d Update .swiftlint.yml 2020-09-15 12:28:53 +02:00
Juanpe Catalán c50547ef5f Update README.md 2020-09-15 10:19:36 +02:00
Juanpe Catalán 0a7f80e6d0 Update README.md 2020-09-15 10:17:02 +02:00
Juanpe Catalán 29f0726397 Update README_es.md 2020-09-13 21:02:25 +02:00
Juanpe Catalán 6db57ee5fb Update README_es.md 2020-09-13 21:02:05 +02:00
Juanpe Catalán 709dec5fa0 Update README_es.md 2020-09-13 21:01:22 +02:00
Juanpe Catalán 3796d96039 Update README.md 2020-09-13 20:59:30 +02:00
Juanpe Catalán fab5e2e36b Update README.md 2020-09-13 20:45:08 +02:00
Juanpe Catalán a6a6dc2440 Merge pull request #329 from Juanpe/feature/create_spanish_README
Add Spanish README 🇪🇸
2020-09-13 20:39:10 +02:00
Juanpe Catalán 58e9ace96d doc: Add PR in CHANGELOG file 2020-09-13 20:36:48 +02:00
Juanpe Catalán 030da2437c doc: Add spanish README 2020-09-13 20:19:31 +02:00
Juanpe Catalán 95b1b7d512 Merge branch 'develop' into feature/create_spanish_README 2020-09-13 17:47:04 +02:00
Juanpe Catalán 2060d1a4a5 Update README.md 2020-09-13 16:22:40 +02:00
Juanpe Catalán 4874e7c95c Update README.md 2020-09-13 16:15:30 +02:00
Juanpe Catalán f42ce31a7d Update README.md 2020-09-11 13:16:44 +02:00
Juanpe Catalán 805d3bc12a Update README.md 2020-09-11 13:13:43 +02:00
Juanpe Catalán a8c61c8ed2 Update README.md 2020-09-11 13:10:27 +02:00
Juanpe Catalán 90bb2cb22a Update README.md 2020-09-11 13:04:42 +02:00
Juanpe Catalán 8fddc20ed3 Update README.md 2020-09-11 13:02:27 +02:00
Juanpe Catalán a7137ac29d Update README.md 2020-09-11 12:39:56 +02:00
Juanpe Catalán a72acbae5b Update README.md 2020-09-11 12:37:53 +02:00
Juanpe Catalán bb5bdf2d0d Update README.md 2020-09-11 12:33:08 +02:00
Juanpe Catalán beeb303db8 doc: refactor README file 2020-09-11 12:27:01 +02:00
Juanpe Catalán 4ea81526b6 Update swiftlint.yml 2020-09-11 02:10:17 +02:00
Juanpe Catalán 3b8c0114de Update swiftlint.yml 2020-09-11 02:05:50 +02:00
Juanpe Catalán e1a74ad0b8 Update swiftlint.yml 2020-09-11 02:01:54 +02:00
Juanpe Catalán 39d7af9980 Update swiftlint.yml 2020-09-11 01:54:29 +02:00
Juanpe Catalán 796702671b Update swiftlint.yml 2020-09-11 01:47:31 +02:00
Juanpe Catalán df5fbb4faa Update swiftlint.yml 2020-09-11 01:45:12 +02:00
Juanpe Catalán 072f085da4 Rename .github/swiftlint.yml to .github/workflows/swiftlint.yml 2020-09-11 01:36:25 +02:00
Juanpe Catalán 88521db1a1 Create swiftlint.yml 2020-09-11 01:31:01 +02:00
Juanpe Catalán ffac0dd0e4 Merge pull request #327 from Juanpe/feature/swiftlint
Add SwiftLint
2020-09-11 01:16:47 +02:00
Juanpe Catalán d8ccc8ec7a doc: Add PR to CHANGELOG file 2020-09-11 01:16:10 +02:00
Juanpe Catalán 3dcfebda82 refactor: swiftlint changes 2020-09-11 01:10:04 +02:00
Juanpe Catalán e2afb6aba7 feat: add swiftlint.yml file 2020-09-11 01:09:43 +02:00
Juanpe Catalán b267a2c675 Merge pull request #326 from Juanpe/master
Merge branch 'develop'
2020-09-10 20:49:02 +02:00
Juanpe Catalán ced07a1ca1 Update CONTRIBUTING.md 2020-09-10 20:48:17 +02:00
Juanpe Catalán 8d09e6646a Merge branch 'develop' 2020-09-10 20:47:01 +02:00
Juanpe Catalán 7d710bbd74 doc: Update README file including contributing guidelines link 2020-09-10 20:46:50 +02:00
Juanpe Catalán 4970073190 doc: Add CONTRIBUTING file 2020-09-10 20:46:29 +02:00
Juanpe Catalán 4ecfa226b6 doc: add Pull Request template 2020-09-10 20:46:19 +02:00
Juanpe Catalán a256001f61 doc: Update Issue template 2020-09-10 20:46:11 +02:00
Juanpe Catalán 8b3b8c1d71 doc: Add CODE_OF_CONDUCT file 2020-09-10 20:15:48 +02:00
Juanpe Catalán 4e605d2fd4 Merge pull request #324 from Juanpe/master
Merge branch 'develop'
2020-09-10 11:12:05 +02:00
Juanpe Catalán 6feffdb41b Merge branch 'develop' 2020-09-10 11:08:18 +02:00
Juanpe Catalán 0adc9683ee docs: bump and update CHANGELOG file 2020-09-10 10:59:04 +02:00
Juanpe Catalán 0521be6d6e Merge pull request #323 from Juanpe/fix/save_and_restore_button_view_state
Save and restore view state for UIButton
2020-09-09 17:35:16 +02:00
Juanpe Catalán 4167579d8a add PR in the CHANGELOG file 2020-09-09 17:31:34 +02:00
Juanpe Catalán 1bd4d3d960 feat: save and restore button view state 2020-09-09 16:49:40 +02:00
Juanpe Catalán b79219ac88 Update stale.yml 2020-09-08 14:03:07 +02:00
Juanpe Catalán 39ad7aa264 Update CHANGELOG.md 2020-09-07 13:50:13 +02:00
Juanpe Catalán 997c62b7d1 Merge pull request #319 from xpereta/fix-bottom-edge-inset
Fix to consider the top and bottom edge insets when updating the skeleton layer height
2020-09-07 13:49:40 +02:00
Xavier Pereta d4aa1743e6 Update CHANGELOG.md 2020-09-03 23:07:48 +02:00
Xavier Pereta 770be4fc06 fix: Consider the bottom and top edge insets when updating the height of the skeleton layer. 2020-09-03 23:00:42 +02:00
Juanpe Catalán cdd30bd623 Update CHANGELOG.md 2020-09-01 20:30:54 +02:00
Juanpe Catalán 0c500a943c Merge pull request #320 from Juanpe/fix_labels_single_line_appearance
Fix Single line customisation
2020-09-01 20:29:40 +02:00
Juanpe Catalán 8258b73b35 Include the PR in the CHANGELOG file 2020-09-01 20:24:42 +02:00
Juanpe Catalán 3d9e9063c5 Update Dangerfile.swift 2020-09-01 20:23:21 +02:00
Juanpe Catalán b443dcc17c use multiline height when text is a single line 2020-09-01 20:18:24 +02:00
Juanpe Catalán 13b07d9edd Update checks.yml 2020-09-01 18:50:25 +02:00
Juanpe Catalán 5a27b37e81 Merge branch 'master' into develop 2020-08-27 11:43:20 +02:00
67 changed files with 2051 additions and 579 deletions
+56
View File
@@ -0,0 +1,56 @@
---
name: "\U0001F41B Bug report"
about: Report a bug or unexpected behavior while using SkeletonView
title: ''
labels: bug
assignees: ''
---
### Description
Describe your issue here.
### What type of issue is this? (place an `x` in one of the `[ ]`)
- [ ] bug
- [ ] enhancement (feature request)
- [ ] question
- [ ] documentation related
- [ ] discussion
### Requirements (place an `x` in each of the `[ ]`)
* [ ] I've read and understood the [Contributing guidelines](https://github.com/Juanpe/SkeletonView/blob/main/CONTRIBUTING.md) and have done my best effort to follow them.
* [ ] I've read and agree to the [Code of Conduct](https://github.com/Juanpe/SkeletonView/blob/main/CODE_OF_CONDUCT.md).
* [ ] I've searched for any related issues and avoided creating a duplicate issue.
---
### Bug Report
Filling out the following details about bugs will help us solve your issue sooner.
### SkeletonView Environment:
**SkeletonView version:**
**Xcode version:**
**Swift version:**
#### Steps to reproduce:
*Please replace this with the steps to reproduce the behavior.*
1.
2.
3.
#### Expected result:
*Please replace this with what you expected to happen.*
#### Actual result:
*Please replace this with of what happened instead.*
#### Attachments:
Logs, screenshots, sample project, funny gif, etc.
+28
View File
@@ -0,0 +1,28 @@
---
name: "\U0001F4E3 Feedback"
about: Give us general feedback about the SkeletonView
title: ''
labels: feedback
assignees: ''
---
# SkeletonView Feedback
You can use this template to give us structured feedback or just wipe it and leave us a note. Thank you!
## What have you loved?
_eg "the nice colors"_
## What was confusing or gave you pause?
_eg "it did something unexpected"_
## Are there features you'd like to see added?
_eg "SkeletonView should be compatible with SwiftUI"_
## Anything else?
_eg "have a nice day"_
@@ -0,0 +1,20 @@
---
name: "⭐ Submit a request"
about: Surface a feature or problem that you think should be solved
title: ''
labels: enhancement
assignees: ''
---
### Describe the feature or problem youd like to solve
A clear and concise description of what the feature or problem is.
### Proposed solution
How will it benefit SkeletonView and its users?
### Additional context
Add any other context like screenshots or mockups are helpful, if applicable.
-25
View File
@@ -1,25 +0,0 @@
⚠️ 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.*
### What did you expect to happen?
*Please replace this with what you expected to happen.*
### What happened instead?
*Please replace this with of what happened instead.*
### Steps to reproduce the behavior
*Please replace this with the steps to reproduce the behavior.*
### SkeletonView Environment
**SkeletonView version:**
**Xcode version:**
**Swift version:**
+7
View File
@@ -0,0 +1,7 @@
### Summary
Describe the goal of this PR. Mention any related Issue numbers.
### Requirements (place an `x` in each of the `[ ]`)
* [ ] I've read and understood the [Contributing guidelines](https://github.com/Juanpe/SkeletonView/blob/main/CONTRIBUTING.md) and have done my best effort to follow them.
* [ ] I've read and agree to the [Code of Conduct](https://github.com/Juanpe/SkeletonView/blob/main/CODE_OF_CONDUCT.md).
+39
View File
@@ -0,0 +1,39 @@
name-template: '📦 $RESOLVED_VERSION'
tag-template: '$RESOLVED_VERSION'
category-template: '#### $TITLE'
change-template: '- **#$NUMBER**: $TITLE - @$AUTHOR'
template: |
$CHANGES
categories:
- title: '🚨 Breaking'
label: 'breaking'
- title: '🔬Improvements'
label: '💡 enhancement'
- title: '🙌 New'
label: 'feature'
- title: '🩹 Bug fixes'
label: '🐞 bug'
- title: '⚙️ Maintenance'
label: '⚙️ maintenance'
- title: '📚 Documentation'
label: '📚 docs'
- title: '💾 Dependency Updates'
label: 'dependencies'
version-resolver:
major:
labels:
- 'breaking'
minor:
labels:
- '💡 enhancement'
- 'feature'
patch:
labels:
- '🐞 bug'
- '⚙️ maintenance'
- '📚 docs'
- 'dependencies'
exclude-labels:
- 'skip-changelog'
+3 -3
View File
@@ -1,10 +1,10 @@
daysUntilStale: 20
daysUntilStale: 7
daysUntilClose: 7
onlyLabels:
- awaiting user input
staleLabel: given up
markComment: >
This issue has been automatically marked as stale because it has not had
🤖 This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
for your contributions 🙂
closeComment: false
-20
View File
@@ -1,20 +0,0 @@
name: build
on:
push:
branches:
- develop
pull_request:
branches:
- develop
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: xcodebuild
run: |
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'
-13
View File
@@ -1,13 +0,0 @@
name: CI
on: [pull_request]
jobs:
danger:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Execute danger
uses: danger/swift@2.0.3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+19
View File
@@ -0,0 +1,19 @@
name: CI
on:
pull_request:
branches: [main]
jobs:
build:
runs-on: macos-latest
strategy:
matrix:
build-config:
- { target: 'SkeletonView-iOS', destination: 'platform=iOS Simulator,name=iPhone 8', sdk: 'iphonesimulator' }
- { target: 'SkeletonView-tvOS', destination: 'platform=tvOS Simulator,name=Apple TV', sdk: 'appletvsimulator' }
- { target: 'SkeletonViewExample', destination: 'platform=iOS Simulator,name=iPhone 8', sdk: 'iphonesimulator' }
steps:
- uses: actions/checkout@v2
- name: Build
run: xcodebuild clean build -target '${{ matrix.build-config['target'] }}' -sdk '${{ matrix.build-config['sdk'] }}' -destination '${{ matrix.build-config['destination'] }}'
+14
View File
@@ -0,0 +1,14 @@
name: Pod lint
on: [workflow_dispatch]
jobs:
pod_lib_lint:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v2
- env:
COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
run: |
set -eo pipefail
pod lib lint --allow-warnings
+38
View File
@@ -0,0 +1,38 @@
name: Release
on: [workflow_dispatch]
jobs:
release_version:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v2
- name: Setup fastlane
run: brew install fastlane
- name: Publish release
id: publish_release
uses: release-drafter/release-drafter@v5
with:
publish: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Update podspec
run: fastlane bump_version next_version:${{ steps.publish_release.outputs.tag_name }}
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4
with:
branch: 'main'
commit_message: 'Bump version ${{ steps.publish_release.outputs.tag_name }}'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Deploy to Cocoapods
env:
COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
run: |
set -eo pipefail
pod lib lint --allow-warnings
pod trunk push --allow-warnings
+14
View File
@@ -0,0 +1,14 @@
name: Release Notes
on:
push:
branches:
- main
jobs:
update_release_notes:
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+34
View File
@@ -0,0 +1,34 @@
name: Validations
on:
pull_request_target:
branches: [main]
types: [opened, reoneped, edited, synchronized]
# workflow_dispatch:
# inputs:
# commit hash:
# description: "Commit hash"
# required: true
# default: ""
jobs:
lint:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: Run SwiftLint
run: swiftlint lint --reporter github-actions-logging
danger:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Danger
uses: docker://frmeloni/danger-swift-with-swiftlint:1.3.1
with:
args: --failOnErrors --verbose
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Executable
+69
View File
@@ -0,0 +1,69 @@
included:
- Sources
disabled_rules:
- trailing_whitespace
- line_length
- type_body_length
- identifier_name
- multiple_closures_with_trailing_closure
- class_delegate_protocol
- force_unwrapping
- force_try
- force_cast
- function_parameter_count
- discouraged_optional_collection
- shorthand_operator
- reduce_boolean
- weak_delegate
- nesting
- closure_end_indentation
- function_default_parameter_at_end
- unowned_variable_capture
- legacy_constructor
opt_in_rules:
- multiline_arguments
- multiline_parameters
- closure_spacing
- closure_body_length
- collection_alignment
- contains_over_filter_is_empty
- contains_over_filter_count
- contains_over_first_not_nil
- contains_over_range_nil_comparison
- convenience_type
- discouraged_object_literal
- discouraged_optional_boolean
- empty_count
- empty_string
- fallthrough
- file_name_no_space
- first_where
- flatmap_over_map_reduce
- implicitly_unwrapped_optional
- joined_default_parameter
- last_where
- literal_expression_end_indentation
- multiline_function_chains
- operator_usage_whitespace
- private_action
- private_outlet
- redundant_optional_initialization
- redundant_set_access_control
- redundant_type_annotation
- sorted_first_last
- switch_case_on_newline
- unneeded_parentheses_in_closure_argument
- unused_declaration
- unused_import
- vertical_whitespace_opening_braces
- discouraged_optional_collection
- enum_case_associated_values_counts
- legacy_multiple
- legacy_random
indentation: 2
file_length:
- 2500
- 3000
large_tuple:
- 5
- 6
+61 -22
View File
@@ -3,7 +3,46 @@ All notable changes to this project will be documented in this file
### Next version
### 📦 [1.8.8](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.8)
#### 🙌 New
#### 🔬 Improvements
* [**369**](https://github.com/Juanpe/SkeletonView/pull/369): remove useless corner radius constraint - [@Juanpe](https://github.com/Juanpe)
* [**357**](https://github.com/Juanpe/SkeletonView/pull/357): Removed duplicate code in SkeletonCollectionDelegate. - [@keshavamurthy1](https://github.com/keshavamurthy1)
#### 🩹 Bug fixes
* [**359**](https://github.com/Juanpe/SkeletonView/pull/359): SkeletonView respecting Font's height, rather than the `UIView` actual height. - [@keshavamurthy1](https://github.com/keshavamurthy1)
## 📦 [1.11.0](https://github.com/Juanpe/SkeletonView/releases/tag/1.11.0)
#### 🙌 New
* [**339**](https://github.com/Juanpe/SkeletonView/pull/339): Add `hiddenWhenSkeletonIsActive` property - [@mohn93](https://github.com/mohn93)
* [**341**](https://github.com/Juanpe/SkeletonView/pull/341): Support autoreverses in gradient animations - [@Juanpe](https://github.com/Juanpe)
#### 🔬Improvements
* [**344**](https://github.com/Juanpe/SkeletonView/pull/344): Resize labels based on number of lines - [@Juanpe](https://github.com/Juanpe)
#### 🩹 Bug fixes
* [**340**](https://github.com/Juanpe/SkeletonView/pull/340): Fixed incorrect padding, and incorrect multiline layer frame calculation - [@yzhao198](https://github.com/yzhao198)
## 📦 [1.10.0](https://github.com/Juanpe/SkeletonView/releases/tag/1.10.0)
#### 🙌 New
* [**327**](https://github.com/Juanpe/SkeletonView/pull/327): Add SwiftLint - [@Juanpe](https://github.com/Juanpe)
* [**329**](https://github.com/Juanpe/SkeletonView/pull/329): Spanish README 🇪🇸 - [@Juanpe](https://github.com/Juanpe)
#### 🩹 Bug fixes
* [**336**](https://github.com/Juanpe/SkeletonView/pull/336): Not replace text when the skeleton disappears. Solved issues: [#296](https://github.com/Juanpe/SkeletonView/issues/296), [#330](https://github.com/Juanpe/SkeletonView/issues/330) - [@Juanpe](https://github.com/Juanpe)
* [**337**](https://github.com/Juanpe/SkeletonView/pull/337): RTL support. Solved issues: [#143](https://github.com/Juanpe/SkeletonView/issues/143) - [@Juanpe](https://github.com/Juanpe)
## 📦 [1.9](https://github.com/Juanpe/SkeletonView/releases/tag/1.9)
#### 🩹 Bug fixes
* [**319**](https://github.com/Juanpe/SkeletonView/pull/319): Fix to consider the top and bottom edge insets when updating the skeleton layer height - [@xpereta](https://github.com/xpereta)
* [**320**](https://github.com/Juanpe/SkeletonView/pull/320): Fix Single line customisation - [@Juanpe](https://github.com/juanpe)
* [**323**](https://github.com/Juanpe/SkeletonView/pull/323): Save and restore view state for UIButton - [@Juanpe](https://github.com/juanpe)
## 📦 [1.8.8](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.8)
#### 🙌 New
* [**304**](https://github.com/Juanpe/SkeletonView/pull/304): French README 🇫🇷 - [@OmarJalil](https://github.com/OmarJalil)
@@ -17,7 +56,7 @@ All notable changes to this project will be documented in this file
* [**308**](https://github.com/Juanpe/SkeletonView/pull/308): Fix example backgroundColor in DarkMode - [@toshi0383](https://github.com/toshi0383)
* [**307**](https://github.com/Juanpe/SkeletonView/pull/307): Prevent incorrect skeletonLayer to be added when updating skeleton - [@wsalim1610](https://github.com/wsalim1610)
### 📦 [1.8.7](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.7)
## 📦 [1.8.7](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.7)
#### 🔬Improvements
* [**271**](https://github.com/Juanpe/SkeletonView/pull/271): Add corner radius for skeletonView as IBInspectable (CGFloat) default is 0.0 - [@paulanatoleclaudot-betclic](https://github.com/paulanatoleclaudot-betclic)
@@ -27,7 +66,7 @@ All notable changes to this project will be documented in this file
* [**274**](https://github.com/Juanpe/SkeletonView/pull/274): Fix: hiding skeleton when header and footer views of section would not hide it - [@darkside999](https://github.com/darkside999)
* [**273**](https://github.com/Juanpe/SkeletonView/pull/273): Fix: in vertical stack view with center alignment show incorrect position - [@koooootake](https://github.com/koooootake)
### 📦 [1.8.6](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.6)
## 📦 [1.8.6](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.6)
#### 🔬Improvements
* [**242**](https://github.com/Juanpe/SkeletonView/pull/242): Offscreen table view layout issue fixed - [@Cacodemon](https://github.com/Cacodemon)
@@ -41,16 +80,16 @@ All notable changes to this project will be documented in this file
* [**257**](https://github.com/Juanpe/SkeletonView/issues/257): Unit test problem when using SkeletonView
### 📦 [1.8.3](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.3)
## 📦 [1.8.3](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.3)
- Support for iOS 13 dark mode. (thanks @Wilsonator5000)
### 📦 [1.8.2](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.2)
## 📦 [1.8.2](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.2)
#### 🙌 New
- Add ability to customize line spacing per label. (thanks @gshahbazian)
### 📦 [LayoutSkeleton (1.8.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.1)
## 📦 [LayoutSkeleton (1.8.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.1)
#### 🔬Improvements
- Fix completion call in .none transition style while hide skeletons. (thanks @aadudyrev)
@@ -66,7 +105,7 @@ All notable changes to this project will be documented in this file
- Update layout subviews when the original method is called.
- Issues: [#88, #149]
### 📦 [Transitions (1.8)](https://github.com/Juanpe/SkeletonView/releases/tag/1.8)
## 📦 [Transitions (1.8)](https://github.com/Juanpe/SkeletonView/releases/tag/1.8)
#### 🙌 New
@@ -80,7 +119,7 @@ All notable changes to this project will be documented in this file
- Solved issues.
[#175](https://github.com/Juanpe/SkeletonView/issues/175) Swift Package Manager version format
### 📦 [Layout update (1.7)](https://github.com/Juanpe/SkeletonView/releases/tag/1.7)
## 📦 [Layout update (1.7)](https://github.com/Juanpe/SkeletonView/releases/tag/1.7)
#### 🙌 New
@@ -90,7 +129,7 @@ All notable changes to this project will be documented in this file
- Allow updating skeleton layers without recreating them: `updateSkeleton`, `updateGradientSkeleton`, `updateAnimatedSkeleton`, `updateAnimatedGradientSkeleton`. (thanks @eduardbosch)
### 📦 [Debug (1.4)](https://github.com/Juanpe/SkeletonView/releases/tag/1.4)
## 📦 [Debug (1.4)](https://github.com/Juanpe/SkeletonView/releases/tag/1.4)
#### 🙌 New
@@ -104,7 +143,7 @@ All notable changes to this project will be documented in this file
#### 🩹 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)
## 📦 [Custom defaults (1.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.3)
#### 🙌 New
@@ -114,7 +153,7 @@ All notable changes to this project will be documented in this file
#### 🩹 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)
## 📦 [Typo (1.2.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.3)
#### Fixes
@@ -124,7 +163,7 @@ All notable changes to this project will be documented in this file
- 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 face (1.2.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.2)
#### 🙌 New
@@ -133,7 +172,7 @@ All notable changes to this project will be documented in this file
#### 🩹 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)
## 📦 [State (1.2.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.1)
#### 🙌 New
@@ -143,7 +182,7 @@ All notable changes to this project will be documented in this file
#### 🩹 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)
## 📦 [On TV (1.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2)
#### 🙌 New
- Now ```SkeletonView``` is **tvOS** compatible! 🎉. (thanks @mihai8804858)
@@ -152,12 +191,12 @@ All notable changes to this project will be documented in this file
- 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)
## 📦 [Hotfix (1.1.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.1.1)
#### 🩹 Bug fixes
- Now yes, solved issue [#39](https://github.com/Juanpe/SkeletonView/issues/39)
### 📦 [Needed (1.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.1)
## 📦 [Needed (1.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.1)
#### 🙌 New
- Now ```SkeletonView```supports **UICollectionViews**! 🎉. (thanks @Renatdz)
@@ -166,7 +205,7 @@ All notable changes to this project will be documented in this file
- Solved issue [#39](https://github.com/Juanpe/SkeletonView/issues/39). Gradient animation did not work when app becomes active.
### 📦 [Resizable (1.0.5)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.5)
## 📦 [Resizable (1.0.5)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.5)
#### 🙌 New
- Now you can use table views with resizable cells.
@@ -177,7 +216,7 @@ All notable changes to this project will be documented in this file
[#30](https://github.com/Juanpe/SkeletonView/issues/30),
[#34](https://github.com/Juanpe/SkeletonView/issues/34).
### 📦 [Filled or not (1.0.4)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.4)
## 📦 [Filled or not (1.0.4)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.4)
#### 🙌 New
- You can set the filling percent of the last line in multiline elements (thanks @jontelang!)
@@ -185,22 +224,22 @@ All notable changes to this project will be documented in this file
#### 🩹 Bug fixes
- Solved issue [#14](https://github.com/Juanpe/SkeletonView/issues/14). You could edit text views with skeleton active.
### 📦 [In all directions (1.0.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.3)
## 📦 [In all directions (1.0.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.3)
#### 🙌 New
- Create ```SkeletonAnimationBuilder```, to facilitate the creation of layer animations.
```GradientDirection``` enum.
### 📦 [Retro (1.0.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.2)
## 📦 [Retro (1.0.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.2)
#### 🙌 New
- Change some private keywords, to be Swift 3 compatible
### 📦 [Early bird bug (1.0.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.2)
## 📦 [Early bird bug (1.0.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.2)
#### 🩹 Bug fixes
- It was not removing the skeleton layer
### 📦 [Starter (1.0)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0)
## 📦 [Starter (1.0)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0)
- First release
+52
View File
@@ -0,0 +1,52 @@
# Code of Conduct
The Code of Conduct governs how we behave in public or in private
whenever the project will be judged by our actions.
We expect it to be honored by everyone who represents the project
officially or informally,
claims affiliation with the project,
or participates directly.
We strive to:
* **Be open**: We invite anybody to participate in any aspect of our projects.
Our community is open, and any responsibility can be carried
by any contributor who demonstrates the required capacity and competence.
* **Be empathetic**: We work together to resolve conflict,
assume good intentions,
and do our best to act in an empathic fashion.
By understanding that humanity drops a few packets in online interactions,
and adjusting accordingly,
we can create a comfortable environment for everyone to share their ideas.
* **Be collaborative**: We prefer to work transparently
and to involve interested parties early on in the process.
Wherever possible, we work closely with others in the open source community
to coordinate our efforts.
* **Be decisive**: We expect participants in the project to resolve disagreements constructively.
When they cannot, we escalate the matter to structures
with designated leaders to arbitrate and provide clarity and direction.
* **Be responsible**: We hold ourselves accountable for our actions.
When we make mistakes, we take responsibility for them.
When we need help, we reach out to others.
When it comes time to move on from a project,
we take the proper steps to ensure that others can pick up where we left off.
This code is not exhaustive or complete.
It serves to distill our common understanding of a
collaborative, shared environment and goals.
We expect it to be followed in spirit as much as in the letter.
---
The **SkeletonView** Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
+77
View File
@@ -0,0 +1,77 @@
# Contributors Guide
Interested in contributing? Awesome! Before you do though, please read our
[Code of Conduct](https://github.com/Juanpe/SkeletonView/blob/main/CODE_OF_CONDUCT.md). We take it very seriously, and expect that you will as
well.
There are many ways you can contribute! :heart:
### Bug Reports and Fixes :bug:
- If you find a bug, please search for it in the [Issues](https://github.com/Juanpe/SkeletonView/issues), and if it isn't already tracked,
[create a new issue](https://github.com/slackhq/PanModal/issues/new). Fill out the "Bug Report" section of the issue template. Even if an Issue is closed, feel free to comment and add details, it will still
be reviewed.
- Issues that have already been identified as a bug (note: able to reproduce) will be labelled `🐞 Bug`.
- If you'd like to submit a fix for a bug, [send a Pull Request](#creating_a_pull_request) and mention the Issue number.
### New Features :bulb:
- If you'd like to add new functionality to this project, describe the problem you want to solve in a [new Issue](https://github.com/Juanpe/SkeletonView/issues/new).
- Issues that have been identified as a feature request will be labelled `💡 Enhancement`.
- If you'd like to implement the new feature, please wait for feedback from the project
maintainers before spending too much time writing the code. In some cases, `💡 Enhancement`s may
not align well with the project objectives at the time.
### Miscellaneous :sparkles:
- If you have an alternative implementation of something that may have advantages over the way its currently
done, or you have any other change, we would be happy to hear about it!
- If its a trivial change, go ahead and [send a Pull Request](#creating_a_pull_request) with the changes you have in mind.
- If not, [open an Issue](https://github.com/Juanpe/SkeletonView/issues/new) to discuss the idea first.
If you're new to our project and looking for some way to make your first contribution, look for
Issues labelled `good first issue`.
## Requirements
For your contribution to be accepted:
- [x] The changes must be approved by code review.
- [x] Commits should be atomic and messages must be descriptive. Related issues should be mentioned by Issue number.
If the contribution doesn't meet the above criteria, you may fail our automated checks or a maintainer will discuss it with you. You can continue to improve a Pull Request by adding commits to the branch from which the PR was created.
## Creating a Pull Request
1. :fork_and_knife: Fork the repository on GitHub.
2. :runner: Clone/fetch your fork to your local development machine.
3. :herb: Create a new branch and check it out.
4. :crystal_ball: Make your changes and commit them locally.
5. :arrow_heading_up: Push your new branch to your fork. (e.g. `git push username fix-issue-300`).
6. :inbox_tray: Open a Pull Request on github.com from your new branch on your fork to `main` in this
repository.
## Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
- (a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
- (b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
- (c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
- (d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
*Wording of statement copied from [elinux.org](http://elinux.org/Developer_Certificate_Of_Origin)*
-10
View File
@@ -3,16 +3,6 @@ import Danger
let danger = Danger()
let github = danger.github
// Changelog entries are required for changes to library files.
let allSourceFiles = danger.git.modifiedFiles + danger.git.createdFiles
let noChangelogEntry = !allSourceFiles.contains("CHANGELOG.md")
let sourceChanges = allSourceFiles.contains { $0.hasPrefix("Sources") }
let isNotTrivial = !danger.github.pullRequest.title.contains("#trivial")
if isNotTrivial && noChangelogEntry && sourceChanges {
warn("Any changes to library code should be reflected in the Changelog.")
}
// Make it more obvious that a PR is a work in progress and shouldn't be merged yet
if danger.github.pullRequest.title.contains("WIP") {
warn("PR is classed as Work in Progress")
+54 -15
View File
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Va7-1y-Tel">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Va7-1y-Tel">
<device id="retina5_9" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15510"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@@ -25,7 +26,7 @@
<constraint firstAttribute="height" constant="78" id="gF5-G1-lKI"/>
</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>
<color key="textColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="textColor" systemColor="labelColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<userDefinedRuntimeAttributes>
@@ -39,7 +40,7 @@
</userDefinedRuntimeAttributes>
</textView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="avatar" translatesAutoresizingMaskIntoConstraints="NO" id="nMj-pU-5wJ">
<rect key="frame" x="141" y="20" width="93" height="93"/>
<rect key="frame" x="45" 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="gw9-nu-cKo"/>
@@ -49,16 +50,35 @@
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CJW-A4-Fb8">
<rect key="frame" x="166" y="27" width="166" height="12"/>
<color key="backgroundColor" red="0.92156862750000001" green="0.16862745100000001" blue="0.54901960780000003" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="10"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="linesCornerRadius">
<integer key="value" value="5"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="skeletonLineSpacing">
<real key="value" value="8"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</label>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="nMj-pU-5wJ" firstAttribute="centerX" secondItem="F9K-jU-100" secondAttribute="centerX" id="9X4-2r-AKx"/>
<constraint firstItem="CJW-A4-Fb8" firstAttribute="leading" secondItem="nMj-pU-5wJ" secondAttribute="trailing" constant="28" id="Drg-cD-6E8"/>
<constraint firstItem="e9V-mk-xH0" firstAttribute="leading" secondItem="F9K-jU-100" secondAttribute="leading" constant="45" id="HvQ-HY-zYU"/>
<constraint firstItem="e9V-mk-xH0" firstAttribute="centerX" secondItem="F9K-jU-100" secondAttribute="centerX" constant="1" id="KcB-tG-NXa"/>
<constraint firstAttribute="height" constant="243" id="MIj-xq-gr1"/>
<constraint firstItem="nMj-pU-5wJ" firstAttribute="leading" secondItem="F9K-jU-100" secondAttribute="leading" constant="45" id="TK6-Ws-2xY"/>
<constraint firstItem="e9V-mk-xH0" firstAttribute="top" secondItem="F9K-jU-100" secondAttribute="top" constant="142" id="Wcx-nZ-1lR"/>
<constraint firstAttribute="trailing" secondItem="e9V-mk-xH0" secondAttribute="trailing" constant="43" id="XbU-Og-rht"/>
<constraint firstItem="CJW-A4-Fb8" firstAttribute="top" secondItem="F9K-jU-100" secondAttribute="top" constant="27" id="ceh-gB-7Et"/>
<constraint firstItem="nMj-pU-5wJ" firstAttribute="top" secondItem="F9K-jU-100" secondAttribute="top" constant="20" id="hQL-cr-MaN"/>
<constraint firstAttribute="trailing" secondItem="CJW-A4-Fb8" secondAttribute="trailing" constant="43" id="nfT-a5-z36"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
@@ -76,7 +96,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<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"/>
<rect key="frame" x="16" y="18" width="82" height="82"/>
<color key="backgroundColor" red="0.56078431370000004" green="0.59607843140000005" blue="0.7843137255" alpha="0.90709546230000004" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="width" constant="82" id="4j0-PU-CmN"/>
@@ -86,10 +106,11 @@
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VhU-1t-AaI" userLabel="Label">
<rect key="frame" x="118" y="29" width="237" height="18"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VhU-1t-AaI" userLabel="Label">
<rect key="frame" x="119" y="29" width="235" height="20"/>
<color key="backgroundColor" red="0.92156862750000001" green="0.16862745100000001" blue="0.54901960780000003" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="height" relation="lessThanOrEqual" constant="71" id="HRL-cI-ieC"/>
<constraint firstAttribute="height" constant="20" id="HRL-cI-ieC"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
@@ -104,14 +125,25 @@
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</label>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="placeholder" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="dha-bH-Ipf">
<rect key="frame" x="119" y="57" width="235" height="34"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</textField>
</subviews>
<constraints>
<constraint firstItem="dha-bH-Ipf" firstAttribute="top" secondItem="VhU-1t-AaI" secondAttribute="bottom" constant="8" symbolic="YES" id="1Ek-1L-ZVs"/>
<constraint firstItem="oiE-tt-nc2" firstAttribute="leading" secondItem="7IN-F3-Mr6" secondAttribute="leadingMargin" id="1be-ak-AH1"/>
<constraint firstAttribute="bottom" secondItem="oiE-tt-nc2" secondAttribute="bottom" constant="20" id="CKt-oA-eBI"/>
<constraint firstItem="oiE-tt-nc2" firstAttribute="top" secondItem="7IN-F3-Mr6" secondAttribute="topMargin" constant="7" id="EKn-ST-LDX"/>
<constraint firstAttribute="trailingMargin" secondItem="VhU-1t-AaI" secondAttribute="trailing" constant="5" id="I7C-Bq-mfK"/>
<constraint firstItem="VhU-1t-AaI" firstAttribute="leading" secondItem="oiE-tt-nc2" secondAttribute="trailing" constant="21" id="Ojr-Kz-1k6"/>
<constraint firstItem="VhU-1t-AaI" firstAttribute="top" secondItem="7IN-F3-Mr6" secondAttribute="topMargin" constant="18" id="ZW6-JY-S4c"/>
<constraint firstItem="dha-bH-Ipf" firstAttribute="trailing" secondItem="VhU-1t-AaI" secondAttribute="trailing" id="baX-Nw-8sB"/>
<constraint firstItem="dha-bH-Ipf" firstAttribute="leading" secondItem="VhU-1t-AaI" secondAttribute="leading" id="kzA-mV-IDt"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
@@ -123,6 +155,7 @@
<connections>
<outlet property="avatar" destination="oiE-tt-nc2" id="Dkh-R5-Qhu"/>
<outlet property="label1" destination="VhU-1t-AaI" id="kUW-HV-KrD"/>
<outlet property="textField" destination="dha-bH-Ipf" id="OHI-6P-tuU"/>
</connections>
</tableViewCell>
</prototypes>
@@ -183,7 +216,7 @@
<action selector="btnChangeColorTouchUpInside:" destination="BYZ-38-t0r" eventType="touchUpInside" id="cB8-Ik-LIJ"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Tdu-YQ-saq">
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Tdu-YQ-saq">
<rect key="frame" x="263" y="69" width="94" height="30"/>
<state key="normal" title="Hide skeleton"/>
<connections>
@@ -203,7 +236,7 @@
</connections>
</stepper>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="l4N-LL-ZrJ" firstAttribute="leading" secondItem="mrw-PM-jJJ" secondAttribute="trailing" constant="8" id="5iU-dO-qVc"/>
<constraint firstItem="mrw-PM-jJJ" firstAttribute="centerY" secondItem="l4N-LL-ZrJ" secondAttribute="centerY" id="9OM-mx-4Jo"/>
@@ -228,7 +261,8 @@
</constraints>
</view>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="F9K-jU-100" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="1es-nY-bd3"/>
<constraint firstItem="F9K-jU-100" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="A3E-iv-1qp"/>
@@ -241,7 +275,6 @@
<constraint firstItem="UCB-SP-lQk" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="o6z-Dj-ppC"/>
<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="YES"/>
</userDefinedRuntimeAttributes>
@@ -270,8 +303,8 @@
<view key="view" contentMode="scaleToFill" id="Jwx-gI-Qod">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="Ao1-hk-zrH"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
<tabBarItem key="tabBarItem" title="Item" id="iKp-9S-aib"/>
</viewController>
@@ -301,5 +334,11 @@
</scenes>
<resources>
<image name="avatar" width="215" height="211"/>
<systemColor name="labelColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
+16 -2
View File
@@ -9,11 +9,25 @@
import UIKit
class Cell: UITableViewCell {
@IBOutlet weak var avatar: UIImageView!
@IBOutlet weak var label1: UILabel!
@IBOutlet weak var textField: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
setUpInputAccessoryView()
}
func setUpInputAccessoryView() {
let bar = UIToolbar()
let reset = UIBarButtonItem(title: "InputAccessoryView", style: .plain, target: self, action: #selector(resetTapped))
bar.items = [reset]
bar.sizeToFit()
textField.inputAccessoryView = bar
}
@objc func resetTapped() {
}
}
+4 -4
View File
@@ -52,11 +52,11 @@ class ViewController: UIViewController {
super.viewDidLoad()
tableview.isSkeletonable = true
transitionDurationStepper.value = 0.25
view.showAnimatedSkeleton()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
view.showAnimatedSkeleton()
}
@IBAction func changeAnimated(_ sender: Any) {
@@ -180,7 +180,7 @@ extension ViewController: SkeletonTableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellIdentifier", for: indexPath) as! Cell
cell.label1.text = "cell => \(indexPath.row)"
cell.label1.text = "cell -> \(indexPath.row)"
return cell
}
}
@@ -193,7 +193,7 @@ extension ViewController: SkeletonTableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView
.dequeueReusableHeaderFooterView(withIdentifier: "HeaderIdentifier") as! HeaderFooterSection
header.titleLabel.text = "header => \(section)"
header.titleLabel.text = "header -> \(section)"
return header
}
@@ -204,7 +204,7 @@ extension ViewController: SkeletonTableViewDelegate {
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footer = tableView
.dequeueReusableHeaderFooterView(withIdentifier: "FooterIdentifier") as! HeaderFooterSection
footer.titleLabel.text = "footer => \(section)"
footer.titleLabel.text = "footer -> \(section)"
return footer
}
}
+195 -174
View File
@@ -1,113 +1,104 @@
![](Assets/header2.jpg)
<p align="center">
<a href="https://github.com/Juanpe/SkeletonView/workflows/build">
<img src="https://github.com/Juanpe/SkeletonView/workflows/build/badge.svg">
<a href="https://github.com/Juanpe/SkeletonView/actions?query=workflow%3ACI">
<img src="https://github.com/Juanpe/SkeletonView/workflows/CI/badge.svg">
</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>
<img src="http://img.shields.io/badge/dependency%20manager-swiftpm%2Bcocoapods%2Bcarthage-green" />
<img src="https://img.shields.io/badge/platforms-ios%2Btvos-green" />
<a href="https://badge.bow-swift.io/recipe?name=SkeletonView&description=An%20elegant%20way%20to%20show%20users%20that%20something%20is%20happening%20and%20also%20prepare%20them%20to%20which%20contents%20he%20is%20waiting&url=https://github.com/juanpe/skeletonview&owner=Juanpe&avatar=https://avatars0.githubusercontent.com/u/1409041?v=4&tag=1.8.7"><img src="https://raw.githubusercontent.com/bow-swift/bow-art/master/badges/nef-playgrounds-badge.svg" alt="SkeletonView Playground" style="height:20px"></a>
<br/>
<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://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>
<br/>
<br/>
<p align="center">
<a href="#-features">Features</a>
• <a href="#-guides">Guides</a>
• <a href="#-installation">Installation</a>
• <a href="#-usage">Usage</a>
• <a href="#-miscellaneous">Miscellaneous</a>
• <a href="#-contributing">Contributing</a>
</p>
**🌎 README is available in other languages: [🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) [🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) [🇰🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_ko.md) [🇫🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_fr.md)**
**🌎 README is available in other languages: [🇪🇸](https://github.com/Juanpe/SkeletonView/blob/main/README_es.md) . [🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) . [🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) . [🇰🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_ko.md) . [🇫🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_fr.md)**
Today almost all apps have async processes, such as API requests, long running processes, etc. While the processes are working, usually developers place a loading view to show users that something is going on.
```SkeletonView``` has been conceived to address this need, an elegant way to show users that something is happening and also prepare them for which contents are waiting.
**SkeletonView** has been conceived to address this need, an elegant way to show users that something is happening and also prepare them for which contents are waiting.
Enjoy it! 🙂
* [Features](#-features)
* [Guides](#-guides)
* [Installation](#-installation)
* [Cocoapods](#using-cocoapods)
* [Carthage](#using-carthage)
* [SPM](#using-swift-package-manager)
* [How to use](#-how-to-use)
* [Collections](#-collections)
* [Multiline text](#-multiline-text)
* [Custom colors](#-custom-colors)
* [Appearance](#-appearance)
* [Custom animations](#-custom-animations)
* [Transitions](#-transitions)
* [Hierarchy](#-hierarchy)
* [Debug](#-debug)
* [Documentation](#-documentation)
* [Supported OS & SDK Versions](#-supported-os--sdk-versions)
* [Next steps](#-next-steps)
* [Contributing](#%EF%B8%8F-contributing)
* [Mentions](#-mentions)
* [Author](#-author)
* [License](#-license)
##
- [🌟 Features](#-features)
- [🎬 Guides](#-guides)
- [📲 Installation](#-installation)
- [🐒 Usage](#-usage)
- [🌿 Collections](#-collections)
- [🔠 Texts](#-texts)
- [🦋 Appearance](#-appearance)
- [🎨 Custom colors](#-custom-colors)
- [Image captured from website https://flatuicolors.com](#image-captured-from-website-httpsflatuicolorscom)
- [🏃‍♀️ Animations](#-animations)
- [🏄 Transitions](#-transitions)
- [✨ Miscellaneous](#-miscellaneous)
- [❤️ Contributing](#-contributing)
- [📢 Mentions](#-mentions)
- [👨🏻‍💻 Author](#-author)
- [👮🏻 License](#-license)
## 🌟 Features
- [x] Easy to use
- [x] All UIViews are skeletonables
- [x] Fully customizable
- [x] Universal (iPhone & iPad)
- [x] Interface Builder friendly
- [x] Simple Swift syntax
- [x] Lightweight readable codebase
* Easy to use
* All UIViews are skeletonables
* Fully customizable
* Universal (iPhone & iPad)
* Interface Builder friendly
* Simple Swift syntax
* Lightweight readable codebase
## 🎬 Guides
| [![](https://img.youtube.com/vi/75kgOhWsPNA/maxresdefault.jpg)](https://youtu.be/75kgOhWsPNA)|[![](https://img.youtube.com/vi/MVCiM_VdxVA/maxresdefault.jpg)](https://youtu.be/MVCiM_VdxVA)|[![](https://img.youtube.com/vi/Qq3Evspeea8/maxresdefault.jpg)](https://youtu.be/Qq3Evspeea8)|[![](https://img.youtube.com/vi/ZOoPtBwDRT0/maxresdefault.jpg)](https://youtu.be/ZOoPtBwDRT0)
|:---: | :---: |:---: | :---:
|[**SkeletonView Guides - Getting started**](https://youtu.be/75kgOhWsPNA)|[**How to Create Loading View with Skeleton View in Swift 5.2**](https://youtu.be/MVCiM_VdxVA) <br/> by iKh4ever Studio|[**Create Skeleton Loading View in App (Swift 5) - Xcode 11, 2020**](https://youtu.be/Qq3Evspeea8) <br/> by iOS Academy| [**Add An Elegant Loading Animation in Swift***](https://youtu.be/ZOoPtBwDRT0) <br/> by Gary Tokman
| [![](https://img.youtube.com/vi/75kgOhWsPNA/maxresdefault.jpg)](https://youtu.be/75kgOhWsPNA)|[![](https://img.youtube.com/vi/MVCiM_VdxVA/maxresdefault.jpg)](https://youtu.be/MVCiM_VdxVA)|[![](https://img.youtube.com/vi/Qq3Evspeea8/maxresdefault.jpg)](https://youtu.be/Qq3Evspeea8)|[![](https://img.youtube.com/vi/ZOoPtBwDRT0/maxresdefault.jpg)](https://youtu.be/ZOoPtBwDRT0)|[![](https://img.youtube.com/vi/Zx1Pg1gPfxA/maxresdefault.jpg)](https://www.youtube.com/watch?v=Zx1Pg1gPfxA)
|:---: | :---: |:---: | :---: | :---:
|[**SkeletonView Guides - Getting started**](https://youtu.be/75kgOhWsPNA)|[**How to Create Loading View with Skeleton View in Swift 5.2**](https://youtu.be/MVCiM_VdxVA) by iKh4ever Studio|[**Create Skeleton Loading View in App (Swift 5) - Xcode 11, 2020**](https://youtu.be/Qq3Evspeea8) by iOS Academy| [**Add An Elegant Loading Animation in Swift***](https://youtu.be/ZOoPtBwDRT0) by Gary Tokman| [**Cómo crear una ANIMACIÓN de CARGA de DATOS en iOS**](https://www.youtube.com/watch?v=Zx1Pg1gPfxA) by MoureDev
## 📲 Installation
#### Using [CocoaPods](https://cocoapods.org)
Edit your `Podfile` and specify the dependency:
* [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html):
```ruby
pod "SkeletonView"
pod 'SkeletonView'
```
#### Using [Carthage](https://github.com/carthage)
* [Carthage](https://github.com/Carthage/Carthage):
Edit your `Cartfile` and specify the dependency:
```bash
```ruby
github "Juanpe/SkeletonView"
```
#### Using [Swift Package Manager](https://github.com/apple/swift-package-manager)
Once you have your Swift package set up, adding `SkeletonView` as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`.
* [Swift Package Manager](https://swift.org/package-manager/):
```swift
dependencies: [
.package(url: "https://github.com/Juanpe/SkeletonView.git", from: "1.7.0")
]
dependencies: [
.package(url: "https://github.com/Juanpe/SkeletonView.git", from: "1.7.0")
]
```
## 🐒 How to use
## 🐒 Usage
Only **3** steps needed to use `SkeletonView`:
**1.** Import SkeletonView in proper place.
1️⃣ Import SkeletonView in proper place.
```swift
import SkeletonView
```
**2.** Now, set which views will be `skeletonables`. You achieve this in two ways:
2️⃣ Now, set which views will be `skeletonables`. You achieve this in two ways:
**Using code:**
```swift
@@ -117,7 +108,7 @@ avatarImageView.isSkeletonable = true
![](Assets/storyboard.png)
**3.** Once you've set the views, you can show the **skeleton**. To do so, you have **4** choices:
3️⃣ Once you've set the views, you can show the **skeleton**. To do so, you have **4** choices:
```swift
(1) view.showSkeleton() // Solid
@@ -159,41 +150,20 @@ avatarImageView.isSkeletonable = true
</tr>
</table>
> **IMPORTANT!**
>>```SkeletonView``` is recursive, so if you want show the skeleton in all skeletonable views, you only need to call the show method in the main container view. For example, with UIViewControllers
### Extra
> 📣 **IMPORTANT!**
>
> `SkeletonView` is recursive, so if you want show the skeleton in all skeletonable views, you only need to call the show method in the main container view. For example, with `UIViewControllers`.
#### Skeleton views layout
Sometimes skeleton layout may not fit your layout because the parent view bounds have changed. ~For example, rotating the device.~
You can relayout the skeleton views like so:
```swift
override func viewDidLayoutSubviews() {
view.layoutSkeletonIfNeeded()
}
```
⚠️⚠️ You shouldn't call this method. From *version 1.8.1* you don't need to call this method, the library does automatically. So, you can use this method *ONLY* in the cases when you need to update the layout of the skeleton manually.
#### Update skeleton configuration
You can change the skeleton configuration at any time like its colour, animation, etc. with the following methods:
```swift
(1) view.updateSkeleton() // Solid
(2) view.updateGradientSkeleton() // Gradient
(3) view.updateAnimatedSkeleton() // Solid animated
(4) view.updateAnimatedGradientSkeleton() // Gradient animated
```
### 🌿 Collections
```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.
@@ -202,8 +172,6 @@ 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
func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier?
func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier?
}
```
As you can see, this protocol inherits from ```UITableViewDataSource```, so you can replace this protocol with the skeleton protocol.
@@ -221,15 +189,9 @@ func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection s
// It calculates how many cells need to populate whole tableview
```
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier?
// Default: nil
```
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier?
// Default: nil
```
> 📣 **IMPORTANT!**
>
> If you return `UITableView.automaticNumberOfSkeletonRows` in the above method, it acts like the default behavior (i.e. it calculates how many cells needed to populate the whole tableview).
There is only one method you need to implement to let Skeleton know the cell identifier. This method doesn't have default implementation:
``` swift
@@ -242,42 +204,51 @@ There is only one method you need to implement to let Skeleton know the cell ide
return "CellIdentifier"
}
```
Besides, you can skeletonize both the headers and footers. You need to conform to `SkeletonTableViewDelegate` protocol.
> **IMPORTANT!**
> If you are using resizable cells (`tableView.rowHeight = UITableViewAutomaticDimension` ), it's mandatory define the `estimatedRowHeight`.
```swift
public protocol SkeletonTableViewDelegate: UITableViewDelegate {
func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier? // default: nil
func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier? // default: nil
}
```
👩🏼‍🏫 **How specify which elements are skeletonables?**
> 📣 **IMPORTANT!**
>
> 1️⃣ If you are using resizable cells (**`tableView.rowHeight = UITableViewAutomaticDimension`**), it's mandatory define the **`estimatedRowHeight`**.
>
> 2️⃣ When you add elements in a **`UITableViewCell`** you should add it to **`contentView`** and not to the cell directly.
> ```swift
> self.contentView.addSubview(titleLabel) ✅
> self.addSubview(titleLabel) ❌
> ```
Here is an illustration that shows how you should specify which elements are skeletonables when you are using an `UITableView`:
![](Assets/tableview_scheme.png)
**UICollectionView**
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.
For `UICollectionView`, you need to conform to `SkeletonCollectionViewDataSource` protocol.
``` swift
public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
func numSections(in collectionSkeletonView: UICollectionView) -> Int
func numSections(in collectionSkeletonView: UICollectionView) -> Int // default: 1
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier
func collectionSkeletonView(_ skeletonView: UICollectionView, supplementaryViewIdentifierOfKind: String, at indexPath: IndexPath) -> ReusableCellIdentifier? // default: nil
}
```
The rest of the process is the same as ```UITableView```
### 📰 Multiline text
### 🔠 Texts
![](Assets/multilines2.png)
When using elements with text, ```SkeletonView``` draws lines to simulate text.
Besides, you can decide how many lines you want. If ```numberOfLines``` is set to zero, it will calculate how many lines needed to populate the whole skeleton and it will be drawn. Instead, if you set it to one, two or any number greater than zero, it will only draw this number of lines.
##### 🎛 Customize
You can set some properties for multilines elements.
@@ -298,32 +269,10 @@ Or, if you prefer use **IB/Storyboard**:
![](Assets/multiline_customize.png)
### 🎨 Custom colors
You can decide which color the skeleton is tinted with. You only need to pass as a parameter the color or gradient you want.
**Using solid colors**
``` swift
view.showSkeleton(usingColor: UIColor.gray) // Solid
// or
view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0))
```
**Using gradients**
``` swift
let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
view.showGradientSkeleton(usingGradient: gradient) // Gradient
```
Besides, ```SkeletonView``` features 20 flat colors 🤙🏼
```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...```
![](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.
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
@@ -342,7 +291,7 @@ Default values:
- *default: 0*
To get these default values you can use `SkeletonAppearance.default`. Using this property you can set the values as well:
```Swift
```swift
SkeletonAppearance.default.multilineHeight = 20
SkeletonAppearance.default.tintColor = .green
```
@@ -354,9 +303,33 @@ You can also specifiy these line appearance properties on a per-label basis:
- **skeletonPaddingInsets**: UIEdgeInsets
### 🤓 Custom animations
### 🎨 Custom colors
```SkeletonView``` has two built-in animations, *pulse* for solid skeletons and *sliding* for gradients.
You can decide which color the skeleton is tinted with. You only need to pass as a parameter the color or gradient you want.
**Using solid colors**
```swift
view.showSkeleton(usingColor: UIColor.gray) // Solid
// or
view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0))
```
**Using gradients**
``` swift
let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
view.showGradientSkeleton(usingGradient: gradient) // Gradient
```
Besides, **SkeletonView** features 20 flat colors 🤙🏼
```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...```
![](Assets/flatcolors.png)
###### Image captured from website [https://flatuicolors.com](https://flatuicolors.com)
### 🏃‍♀️ Animations
**SkeletonView** has two built-in animations, *pulse* for solid skeletons and *sliding* for gradients.
Besides, if you want to do your own skeleton animation, it's really easy.
@@ -390,7 +363,7 @@ view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
```
```GradientDirection``` is an enum, with this cases:
```GradientDirection``` is an enum, with theses cases:
| Direction | Preview
|------- | -------
@@ -402,14 +375,17 @@ view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
| .bottomRightTopLeft | ![](Assets/sliding_bottomRight_to_topLeft.gif)
> **😉 TRICK!**
Exist another way to create sliding animations, just using this shortcut:
>>```let animation = GradientDirection.leftToRight.slidingAnimation()```
>
> Exist another way to create sliding animations, just using this shortcut:
> ```swift
> let animation = GradientDirection.leftToRight.slidingAnimation()
> ```
### 🏄 Transitions
```SkeletonView``` has build-in transitions to **show** or **hide** the skeletons in a *smoother* way 🤙
**SkeletonView** has built-in transitions to **show** or **hide** the skeletons in a *smoother* way 🤙
To use the transition, simply add the ```transition``` parameter to your ```showSkeleton()``` or ```hideSkeleton()``` function with the transition time, like this:
@@ -443,7 +419,11 @@ The default value is `crossDissolve(0.25)`
</table>
### 👨‍👧‍👦 Hierarchy
## ✨ Miscellaneous
**Hierarchy**
Since ```SkeletonView``` is recursive, and we want skeleton to be very efficient, we want to stop recursion as soon as possible. For this reason, you must set the container view as `Skeletonable`, because Skeleton will stop looking for `skeletonable` subviews as soon as a view is not Skeletonable, breaking then the recursion.
@@ -454,7 +434,7 @@ In this example we have a `UIViewController` with a `ContainerView` and a `UITab
view.showSkeleton()
```
> ```ìsSkeletonable```= ☠️
> ```isSkeletonable```= ☠️
| Configuration | Result|
|:-------:|:-------:|
@@ -465,10 +445,68 @@ view.showSkeleton()
|<img src="Assets/tableview_no_skeletonable.jpg" width="350"/> | <img src="Assets/tableview_no_skeletonable_result.png" height="350"/>|
|<img src="Assets/tableview_skeletonable.jpg" width="350"/> | <img src="Assets/tableview_skeletonable_result.png" height="350"/>|
### 🔬 Debug
**Hierarchy in collections**
**NEW** In order to facilitate the debug tasks when something is not working fine. `SkeletonView` has some new tools.
Here is an illustration that shows how you should specify which elements are skeletonables when you are using an `UITableView`:
<img src="Assets/tableview_scheme.png" width="700px">
As you can see, we have to make skeletonable the tableview, the cell and the UI elements, but we don't need to set as skeletonable the `contentView`
**Skeleton views layout**
Sometimes skeleton layout may not fit your layout because the parent view bounds have changed. ~For example, rotating the device.~
You can relayout the skeleton views like so:
```swift
override func viewDidLayoutSubviews() {
view.layoutSkeletonIfNeeded()
}
```
> 📣 **IMPORTANT!**
>
> You shouldn't call this method. From **version 1.8.1** you don't need to call this method, the library does automatically. So, you can use this method **ONLY** in the cases when you need to update the layout of the skeleton manually.
**Update skeleton**
You can change the skeleton configuration at any time like its colour, animation, etc. with the following methods:
```swift
(1) view.updateSkeleton() // Solid
(2) view.updateGradientSkeleton() // Gradient
(3) view.updateAnimatedSkeleton() // Solid animated
(4) view.updateAnimatedGradientSkeleton() // Gradient animated
```
**Hiding views when the animation starts**
Sometimes you wanna hide some view when the animation starts, so there is a quick property that you can use to make this happen:
```swift
view.isHiddenWhenSkeletonIsActive = true // This works only when isSkeletonable = true
```
**Don't modify user interaction when the skeleton is active**
By default, the user interaction is disabled for skeletonized items, but if you don't want to modify the user interaction indicator when skeleton is active, you can use the `isUserInteractionDisabledWhenSkeletonIsActive` property:
```swift
view.isUserInteractionDisabledWhenSkeletonIsActive = false // The view will be active when the skeleton will be active.
```
**Debug**
To facilitate the debug tasks when something is not working fine. **`SkeletonView`** has some new tools.
First, `UIView` has available a new property with his skeleton info:
```swift
@@ -490,30 +528,13 @@ Then, when the skeleton appears, you can see the view hierarchy in the Xcode con
<img src="Assets/hierarchy_output.png" />
</details>
### 📚 Documentation
Coming soon...😅
### 📋 Supported OS & SDK Versions
**Supported OS & SDK Versions**
* iOS 9.0+
* tvOS 9.0+
* Swift 5
## 📬 Next steps
* [x] Set the filling percent of the last line in multiline elements
* [x] Add more gradient animations
* [x] Supported resizable cells
* [x] CollectionView compatible
* [x] tvOS compatible
* [x] Add recovery state
* [x] Custom default appearance
* [x] Debug mode
* [x] Add animations when it shows/hides the skeletons
* [ ] Custom collections compatible
* [ ] MacOS and WatchOS compatible
## ❤️ Contributing
This is an open source project, so feel free to contribute. How?
@@ -523,7 +544,8 @@ This is an open source project, so feel free to contribute. How?
See [all contributors](https://github.com/Juanpe/SkeletonView/graphs/contributors)
###### Project generated with [SwiftPlate](https://github.com/JohnSundell/SwiftPlate)
For more information, please read the [contributing guidelines](https://github.com/Juanpe/SkeletonView/blob/main/CONTRIBUTING.md).
## 📢 Mentions
@@ -544,13 +566,12 @@ See [all contributors](https://github.com/Juanpe/SkeletonView/graphs/contributor
## 👨🏻‍💻 Author
[1.1]: http://i.imgur.com/tXSoThF.png
[1]: http://www.twitter.com/JuanpeCatalan
* Juanpe Catalán [![alt text][1.1]][1]
[Juanpe Catalán](http://www.twitter.com/JuanpeCatalan)
<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
```
+532
View File
@@ -0,0 +1,532 @@
![](Assets/header2.jpg)
<p align="center">
<a href="https://github.com/Juanpe/SkeletonView/actions?query=workflow%3ACI">
<img src="https://github.com/Juanpe/SkeletonView/workflows/CI/badge.svg">
</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>
<img src="http://img.shields.io/badge/dependency%20manager-swiftpm%2Bcocoapods%2Bcarthage-green" />
<img src="https://img.shields.io/badge/platforms-ios%2Btvos-green" />
<a href="https://badge.bow-swift.io/recipe?name=SkeletonView&description=An%20elegant%20way%20to%20show%20users%20that%20something%20is%20happening%20and%20also%20prepare%20them%20to%20which%20contents%20he%20is%20waiting&url=https://github.com/juanpe/skeletonview&owner=Juanpe&avatar=https://avatars0.githubusercontent.com/u/1409041?v=4&tag=1.8.7"><img src="https://raw.githubusercontent.com/bow-swift/bow-art/master/badges/nef-playgrounds-badge.svg" alt="SkeletonView Playground" style="height:20px"></a>
</p>
<p align="center">
<a href="#-destacado">Destacado</a>
• <a href="#-instalación">Instalación</a>
• <a href="#-cómo-funciona">¿Cómo funciona?</a>
• <a href="#-miscelánea">Miscelánea</a>
• <a href="#-contribuir">Contribuir</a>
</p>
**🌎 README está disponible en estos idiomas: [🇬🇧](https://github.com/Juanpe/SkeletonView/blob/master/README.md) . [🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) . [🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) . [🇰🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_ko.md) . [🇫🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_fr.md)**
Hoy en día, La mayoría de las apps tiene procesos asíncronos, como peticiones a una API, procesos que tardan mucho tiempo, etc. Mientras estos procesos se están ejecutando, se suele mostrar un aburrido spinner indicando que algo está pasando.
**SkeletonView** ha sido desarrollada para cubrir esta necesidad, un elegante manera de decirle a los usarios que algo se está procesando y además prepararlos, visualmente, para el contenido que están esperando.
Enjoy it! 🙂
##
- [](#)
- [🌟 Destacado](#-destacado)
- [🎬 Videotutoriales](#-videotutoriales)
- [📲 Instalación](#-instalación)
- [🐒 ¿Cómo funciona?](#-cómo-funciona)
- [](#-1)
- [🌿 Colecciones](#-colecciones)
- [🔠 Textos](#-textos)
- [🦋 Apariencia](#-apariencia)
- [🎨 Colores](#-colores)
- [Imagen extraída de la web https://flatuicolors.com](#imagen-extraída-de-la-web-httpsflatuicolorscom)
- [🏃‍♀️ Animaciones](#-animaciones)
- [🏄 Transiciones](#-transiciones)
- [✨ Miscelánea](#-miscelánea)
- [❤️ Contributing](#-contributing)
- [📢 Menciones](#-menciones)
- [👨🏻‍💻 Autor](#-autor)
- [👮🏻 Licencia](#-licencia)
## 🌟 Destacado
* Fácil de usar
* Todas las UIViews son skeletonables
* Personalizable
* Universal (iPhone & iPad)
* Interface Builder friendly
## 🎬 Videotutoriales
| [![](https://img.youtube.com/vi/75kgOhWsPNA/maxresdefault.jpg)](https://youtu.be/75kgOhWsPNA)|[![](https://img.youtube.com/vi/MVCiM_VdxVA/maxresdefault.jpg)](https://youtu.be/MVCiM_VdxVA)|[![](https://img.youtube.com/vi/Qq3Evspeea8/maxresdefault.jpg)](https://youtu.be/Qq3Evspeea8)|[![](https://img.youtube.com/vi/ZOoPtBwDRT0/maxresdefault.jpg)](https://youtu.be/ZOoPtBwDRT0)
|:---: | :---: |:---: | :---:
|[**SkeletonView Guides - Getting started**](https://youtu.be/75kgOhWsPNA)|[**How to Create Loading View with Skeleton View in Swift 5.2**](https://youtu.be/MVCiM_VdxVA) by iKh4ever Studio|[**Create Skeleton Loading View in App (Swift 5) - Xcode 11, 2020**](https://youtu.be/Qq3Evspeea8) by iOS Academy| [**Add An Elegant Loading Animation in Swift***](https://youtu.be/ZOoPtBwDRT0) by Gary Tokman
## 📲 Instalación
* [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html):
```ruby
pod 'SkeletonView'
```
* [Carthage](https://github.com/Carthage/Carthage):
```ruby
github "Juanpe/SkeletonView"
```
* [Swift Package Manager](https://swift.org/package-manager/):
```swift
dependencies: [
.package(url: "https://github.com/Juanpe/SkeletonView.git", from: "1.7.0")
]
```
## 🐒 ¿Cómo funciona?
Solo necesitas **3** pasos para usar `SkeletonView`:
1️⃣ Importa SkeletonView en el archivo donde vayas a utilizarlo.
```swift
import SkeletonView
```
2️⃣ Ahora, debes indicar qué elementos de tu vista son `skeletonables`
**Con código:**
```swift
avatarImageView.isSkeletonable = true
```
**Con IB/Storyboards:**
![](Assets/storyboard.png)
3️⃣ Una vez indicado, solo tienes que mostrar el **skeleton**. Tienes **4** opciones:
```swift
(1) view.showSkeleton() // Sólido
(2) view.showGradientSkeleton() // Degradado
(3) view.showAnimatedSkeleton() // Sólido animado
(4) view.showAnimatedGradientSkeleton() // Degradado animado
```
**Vista previa**
<table>
<tr>
<td width="25%">
<center>Sólido</center>
</td>
<td width="25%">
<center>Degradado</center>
</td>
<td width="25%">
<center>Sólido Animado</center>
</td>
<td width="25%">
<center>Degradado Animado</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` es recursivo. Por lo que si tienes una vsita que contiene varios elementos skeletonables, solo tienes queenecu For example, with `UIViewControllers`.
##
### 🌿 Colecciones
`SkeletonView` es compatible con `UITableView` and `UICollectionView`.
**UITableView**
Si quieres mostrar el skeleton en un `UITableView`, necesitas conformar el protocolo `SkeletonTableViewDataSource`.
``` swift
public protocol SkeletonTableViewDataSource: UITableViewDataSource {
// Por defecto: 1
func numSections(in collectionSkeletonView: UITableView) -> Int
// Por defecto:
// Calcula cuantas celdas necesita para rellenar todo el frame.
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
}
```
Este protocolo hereda de `UITableViewDataSource`, por lo que puedes reemplazar este protocolo por el protocolo de skeleton sin perder ninguna funcionalidad. El único método que es obligatorio implementar es `cellIdentifierForRowAt`, donde tienes que indicar el identificador de la celda.
**Ejemplo**
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "CellIdentifier"
}
```
Además, tu puedes mostrar el skeleton en las headers y en los footers, conformando el protocolo `SkeletonTableViewDelegate`.
```swift
public protocol SkeletonTableViewDelegate: UITableViewDelegate {
func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier? // default: nil
func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier? // default: nil
}
```
> 📣 **¡IMPORTANTE!**
>
> 1️⃣ Si estás usando celdas con altura dinámica (**`tableView.rowHeight = UITableViewAutomaticDimension`**), es obligatorio definir el **`estimatedRowHeight`**.
>
> 2️⃣ Cuando añades elemetos a una **`UITableViewCell`**, debes añadirlo al **`contentView`** y no a la celda directamente.
> ```swift
> cell.contentView.addSubview(titleLabel) ✅
> cell.addSubview(titleLabel) ❌
> ```
**UICollectionView**
Para los `UICollectionView`, debes conformar el protocolo `SkeletonCollectionViewDataSource`.
``` swift
public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
func numSections(in collectionSkeletonView: UICollectionView) -> Int // Por defecto: 1
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier
func collectionSkeletonView(_ skeletonView: UICollectionView, supplementaryViewIdentifierOfKind: String, at indexPath: IndexPath) -> ReusableCellIdentifier? // Por defecto: nil
}
```
El resto del proceso es exactamente igual que con las `UITableView`.
### 🔠 Textos
![](Assets/multilines2.png)
Cuando usas elementos que contienen texto,`SkeletonView` dibujo líneas para simular el texto.
Además, puedes decidir el número de líneas. Si `numberOfLines` es igual a **0**, se calculará automáticamente el número de líneas necesarias para ocupar todo el frame. Sin embargo, si es un número mayor que cero, solo se dibujarán esas líneas.
Puedes especificar algunos atributos para estos elementos:
| Atributo | Valores | Por defecto | Vista previa
| ------- | ------- |------- | -------
| **Porcentaje de relleno** de la última línea. | `0...100` | `70%` | ![](Assets/multiline_lastline.png)
| **Radio de las esquinas** de las líneas. | `0...10` | `0` | ![](Assets/multiline_corner.png)
Para modificar alguno de los valores lo puedes hacer **con código**::
```swift
descriptionTextView.lastLineFillPercent = 50
descriptionTextView.linesCornerRadius = 5
```
O usando **IB/Storyboards**:
![](Assets/multiline_customize.png)
### 🦋 Apariencia
Los skeletons tiene una apariencia por defecto. Así, cuando no especificas el color, el degradado o las propiedades para las multiíneas, `SkeletonView` usa estos valores.
Valores por defecto:
- **tintColor**: `UIColor`
- *default: `.skeletonDefault` (igual que `.clouds` pero se adapta al dark mode)*
- **gradient**: `SkeletonGradient`
- *default: `SkeletonGradient(baseColor: .skeletonDefault)`*
- **multilineHeight**: `CGFloat`
- *default: 15*
- **multilineSpacing**: `CGFloat`
- *default: 10*
- **multilineLastLineFillPercent**: `Int`
- *default: 70*
- **multilineCornerRadius**: `Int`
- *default: 0*
- **skeletonCornerRadius**: `CGFloat` (IBInspectable)
- *default: 0*
Para obtener o modificar estos valores tu puedes usar `SkeletonAppearance.default`:
```swift
SkeletonAppearance.default.multilineHeight = 20
SkeletonAppearance.default.tintColor = .green
```
### 🎨 Colores
Puedes decidir de qué color se tinta tu skeleton. Solo tienes que indicarlo pasándolo como parámetro:
**Usando colores sólidos**
```swift
view.showSkeleton(usingColor: UIColor.gray)
// o
view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0))
```
**Usando degradados**
``` swift
let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
view.showGradientSkeleton(usingGradient: gradient)
```
Además, **SkeletonView** añade 20 colores flat 🤙🏼
```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...```
![](Assets/flatcolors.png)
###### Imagen extraída de la web [https://flatuicolors.com](https://flatuicolors.com)
### 🏃‍♀️ Animaciones
**SkeletonView** tiene pre-definidas dos animaciones, *pulse* para skeleton sólidos y *sliding* para degradados.
Además, usando el método `showAnimatedSkeleton`, podemos incluir la `animation` que es de tipo `SkeletonLayerAnimation`, un bloque donde tu puedes crear tus propias animaciones:
```swift
public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation
```
Tu código quedaría así:
```swift
view.showAnimatedSkeleton { (layer) -> CAAnimation in
let animation = CAAnimation()
// Personaliza la animación aquí
return animation
}
```
`SkeletonAnimationBuilder` es un builder que permite crear `SkeletonLayerAnimation`.
Por ejemplo, tu puedes crear **sliding animations** para los degradados, decidiendo la **direction** y indicando la **duration** de la animación (default = 1.5s).
```swift
// func makeSlidingAnimation(withDirection direction: GradientDirection, duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation
let animation = SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: .leftToRight)
view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
```
```GradientDirection``` es un enumerado con estos `cases`:
| Dirección | Vista previa
|------- | -------
| .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)
> **😉 ¡Truco!**
>
> Puedes crear una animación sliding, con este shortcut:
> ```swift
> let animation = GradientDirection.leftToRight.slidingAnimation()
> ```
### 🏄 Transiciones
**SkeletonView** tiene algunas transiciones listas para usarse cuando **aparece** o se **oculta**. Puedes especificarla así:
```swift
view.showSkeleton(transition: .crossDissolve(0.25))
view.hideSkeleton(transition: .crossDissolve(0.25))
```
La transición por defecto es `crossDissolve(0.25)`
**Vista previa**
<table>
<tr>
<td width="50%">
<center>Sin transición</center>
</td>
<td width="50%">
<center>Cross dissolve</center>
</td>
</tr>
<tr>
<td width="50%">
<img src="Assets/skeleton_transition_nofade.gif"></img>
</td>
<td width="50%">
<img src="Assets/skeleton_transition_fade.gif"></img>
</td>
</tr>
</table>
## ✨ Miscelánea
**Jerarquía**
`SkeletonView` es recursivo, pero para que sea eficiente, tenemos que pararar la recursión tan pronto como sea posible. Por este motivo, el contenedor de las vistas debe ser **`skeletonable`**, porque `SkeletonView` parará de buscar vistas skeletonables cuando encuentre una que no lo sea, dentro de la jerarquía.
Como una imagen vale más que mil palabras:
En este ejemplo, tenemos un `UIViewController` con un `containerView` y una `UITableView`. Cuando la vista está lista, para mostrar el skeleton ejecutamos el método:
```
view.showSkeleton()
```
> `isSkeletonable`= ☠️
| Configuración | Resultado|
|:-------:|:-------:|
|<img src="Assets/no_skeletonable.jpg" width="350"/> | <img src="Assets/no_skeletonables_result.png" width="350"/>|
|<img src="Assets/container_no_skeletonable.jpg" width="350"/> | <img src="Assets/no_skeletonables_result.png" width="350"/>|
|<img src="Assets/container_skeletonable.jpg" width="350"/> | <img src="Assets/container_skeletonable_result.png" width="350"/>|
|<img src="Assets/all_skeletonables.jpg" width="350"/>| <img src="Assets/all_skeletonables_result.png" width="350"/>|
|<img src="Assets/tableview_no_skeletonable.jpg" width="350"/> | <img src="Assets/tableview_no_skeletonable_result.png" height="350"/>|
|<img src="Assets/tableview_skeletonable.jpg" width="350"/> | <img src="Assets/tableview_skeletonable_result.png" height="350"/>|
**Jerarquía en las colecciones**
Esta ilustración muestra como deberías específicar qué elementos son skeletonables cuando estás usando una `UITableView`:
<img src="Assets/tableview_scheme.png" width="700px">
**Actualiza el skeleton**
Puedes cambiar la configuración del skeleton, como el color, la animación, etc, con los siguientes métodos:
```swift
(1) view.updateSkeleton() // Sólido
(2) view.updateGradientSkeleton() // Degradado
(3) view.updateAnimatedSkeleton() // Sólido animado
(4) view.updateAnimatedGradientSkeleton() // Degradado animado
```
**Debug**
Para facilitar las tareas de debug cuando algo no está funcionando bien, **`SkeletonView`** tiene una nueva herramienta.
Primero, `UIView` tiene una nueva propiedad que contiene toda la info del skeleton:
```swift
var skeletonDescription: String
```
Y es representada de la siguiente manera:
![](Assets/debug_description.png)
Para activar el **modo debug**. Solo tienes que añadir una variable de entorno con esta clave `SKELETON_DEBUG` y activarla.
![](Assets/debug_mode.png)
Entonces, cuando el skeleton aparece, tu podrás ver la jerarquía de vistas en la consola de Xcode.
<details>
<summary>Abre para ver un ejemplo </summary>
<img src="Assets/hierarchy_output.png" />
</details>
**OS Soportado & Versiones SDK**
* iOS 9.0+
* tvOS 9.0+
* Swift 5
## ❤️ Contributing
Esto es un proyecto open source, siéntete libre de contribuir. ¿Cómo?
- Abre un [issue](https://github.com/Juanpe/SkeletonView/issues/new).
- Envía feedback a través del [email](mailto://juanpecatalan.com).
- Propone tus propies fixes, sugerencias y abre una Pull Request con los cambios.
Échale un vistazo a [los que ya han contribuído](https://github.com/Juanpe/SkeletonView/graphs/contributors)
Para más información, por favor, lee la [guía de contribución](https://github.com/Juanpe/SkeletonView/blob/main/CONTRIBUTING.md).
## 📢 Menciones
- [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)
- [Best iOS articles, new tools & more](https://medium.com/flawless-app-stories/best-ios-articles-new-tools-more-fcbe673e10d)
## 👨🏻‍💻 Autor
[Juanpe Catalán](http://www.twitter.com/JuanpeCatalan)
<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>
## 👮🏻 Licencia
```
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.
```
+32 -24
View File
@@ -1,8 +1,8 @@
![](Assets/header2.jpg)
<p align="center">
<a href="https://github.com/Juanpe/SkeletonView/workflows/build">
<img src="https://github.com/Juanpe/SkeletonView/workflows/build/badge.svg">
<a href="https://github.com/Juanpe/SkeletonView/actions?query=workflow%3ACI">
<img src="https://github.com/Juanpe/SkeletonView/workflows/CI/badge.svg">
</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>
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
@@ -37,28 +37,36 @@ Aujourd'hui, presque toutes les applications ont des processus asynchrones, tels
Profitez-en! 🙂
* [Caractéristiques](#-caractéristiques)
* [Guides](#-guides)
* [Installation](#-installation)
* [Cocoapods](#utilisation-de-cocoapods)
* [Carthage](#utilisation-de-carthage)
* [SPM](#utilisation-du-gestionnaire-de-paquets-Swift)
* [Comment utiliser](#-mode-d'emploi)
* [Collections](#-collections)
* [Texte Multiligne](#-texte-multiligne)
* [Couleurs personnalisées](#-couleurs-personnalisées)
* [Présentation](#-présentation)
* [Animations personnalisées](#-animations-personnalisées)
* [Transitions](#-transitions)
* [Hiérarchie](#-hiérarchie)
* [Débugger](#-débugger)
* [Documentation](#-documentation)
* [Versions OS & SDK supportées](#-versions-os-et-sdk-supportées)
* [Prochaines étapes](#-prochaines-étapes)
* [Contribuer](#-contribuer)
* [Mentions](#-mentions)
* [Auteur](#-auteur)
* [Licence](#-licence)
- [🌟 Caractéristiques](#-caractéristiques)
- [🎬 Guides](#-guides)
- [📲 Installation](#-installation)
- [Utilisation de CocoaPods](#utilisation-de-cocoapods)
- [Utilisation de Carthage](#utilisation-de-carthage)
- [Utilisation du gestionnaire de paquets Swift](#utilisation-du-gestionnaire-de-paquets-swift)
- [🐒 Mode d'emploi](#-mode-demploi)
- [Extra](#extra)
- [Mise en page des vues squelettes](#mise-en-page-des-vues-squelettes)
- [Mise à jour de la configuration du squelette](#mise-à-jour-de-la-configuration-du-squelette)
- [🌿 Collections](#-collections)
- [UITableView](#uitableview)
- [UICollectionView](#uicollectionview)
- [📰 Texte multiligne](#-texte-multiligne)
- [🎛 Personnaliser](#-personnaliser)
- [🎨 Couleurs personnalisées](#-couleurs-personnalisées)
- [Image tirée du site web https://flatuicolors.com](#image-tirée-du-site-web-httpsflatuicolorscom)
- [🦋 Présentation](#-présentation)
- [🤓 Animations personnalisées](#-animations-personnalisées)
- [🏄 Transitions](#-transitions)
- [👨👧👦 Hiérarchie](#-hiérarchie)
- [🔬 Débugger](#-débugger)
- [📚 Documentation](#-documentation)
- [📋 Versions OS et SDK supportées](#-versions-os-et-sdk-supportées)
- [📬 Prochaines étapes](#-prochaines-étapes)
- [❤️ Contribuer](#-contribuer)
- [Projet généré avec SwiftPlate](#projet-généré-avec-swiftplate)
- [📢 Mentions](#-mentions)
- [👨🏻‍💻 Auteur](#-auteur)
- [👮🏻 Licence](#-licence)
## 🌟 Caractéristiques
+2 -2
View File
@@ -1,8 +1,8 @@
![](Assets/header2.jpg)
<p align="center">
<a href="https://github.com/Juanpe/SkeletonView/workflows/build">
<img src="https://github.com/Juanpe/SkeletonView/workflows/build/badge.svg">
<a href="https://github.com/Juanpe/SkeletonView/actions?query=workflow%3ACI">
<img src="https://github.com/Juanpe/SkeletonView/workflows/CI/badge.svg">
</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>
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
+26 -21
View File
@@ -1,8 +1,8 @@
![](Assets/header2.jpg)
<p align="center">
<a href="https://github.com/Juanpe/SkeletonView/workflows/build">
<img src="https://github.com/Juanpe/SkeletonView/workflows/build/badge.svg">
<a href="https://github.com/Juanpe/SkeletonView/actions?query=workflow%3ACI">
<img src="https://github.com/Juanpe/SkeletonView/workflows/CI/badge.svg">
</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>
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
@@ -36,25 +36,30 @@ Hoje, quase todos os apps têm processos assíncronos, como requisições de API
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](#-features)
- [📋 Versões do SDK e OS suportados](#-versões-do-sdk-e-os-suportados)
- [🔮 Exemplo](#-exemplo)
- [📲 Instalação](#-instalação)
- [Usando CocoaPods](#usando-cocoapods)
- [Usando Carthage](#usando-carthage)
- [🐒 Como usar](#-como-usar)
- [🌿 Coleções](#-coleções)
- [UITableView](#uitableview)
- [UICollectionView](#uicollectionview)
- [📰 Texto de várias linhas](#-texto-de-várias-linhas)
- [🎛 Customização](#-customização)
- [🎨 Cores customizadas](#-cores-customizadas)
- [Imagem capturada do site https://flatuicolors.com](#imagem-capturada-do-site-httpsflatuicolorscom)
- [🦋 Aparência](#-aparência)
- [🤓 Animações customizadas](#-animações-customizadas)
- [👨‍👧‍👦 Hierarquia](#-hierarquia)
- [📚 Documentação](#-documentação)
- [📬 Próximos passos](#-próximos-passos)
- [❤️ Contribuindo](#-contribuindo)
- [Projeto gerado com SwiftPlate](#projeto-gerado-com-swiftplate)
- [📢 Menções](#-menções)
- [👨🏻‍💻 Autor](#-autor)
- [👮🏻 Licença](#-licença)
## 🌟 Features
+25 -20
View File
@@ -1,8 +1,8 @@
![](Assets/header2.jpg)
<p align="center">
<a href="https://github.com/Juanpe/SkeletonView/workflows/build">
<img src="https://github.com/Juanpe/SkeletonView/workflows/build/badge.svg">
<a href="https://github.com/Juanpe/SkeletonView/actions?query=workflow%3ACI">
<img src="https://github.com/Juanpe/SkeletonView/workflows/CI/badge.svg">
</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>
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
@@ -36,24 +36,29 @@
好好享受! 🙂
* [特征](#-特征)
* [版本要求](#-版本要求)
* [示例项目](#-示例)
* [安装](#-安装)
* [Cocoapods](#使用-cocoapods)
* [Carthage](#使用-carthage)
* [如何使用](#-如何使用)
* [集合](#-集合)
* [多行文字](#-多行文字)
* [自定义颜色](#-自定义颜色)
* [自定义动画](#-自定义动画)
* [等级制度](#-等级制度)
* [文档](#-文档)
* [下一步](#-下一步)
* [特约](#-特约)
* [提及](#-提及)
* [作者](#-作者)
* [许可证](#-许可证)
- [🌟 特征](#-特征)
- [📋 版本要求](#-版本要求)
- [🔮 示例](#-示例)
- [📲 安装](#-安装)
- [使用 CocoaPods](#使用-cocoapods)
- [使用 Carthage](#使用-carthage)
- [🐒 如何使用](#-如何使用)
- [🌿 集合](#-集合)
- [UITableView](#uitableview)
- [UICollectionView](#uicollectionview)
- [📰 多行文字](#-多行文字)
- [🎛 定制](#-定制)
- [🎨 自定义颜色](#-自定义颜色)
- [从网站 https://flatuicolors.com捕获的图像](#从网站-httpsflatuicolorscom捕获的图像)
- [🤓 自定义动画](#-自定义动画)
- [👨‍👧‍👦 等级制度](#-等级制度)
- [📚 文档](#-文档)
- [📬 下一步](#-下一步)
- [❤️ 特约](#-特约)
- [使用 SwiftPlate 生成的项目](#使用-swiftplate-生成的项目)
- [📢 提及](#-提及)
- [👨🏻‍💻 作者](#-作者)
- [👮🏻 许可证](#-许可证)
## 🌟 特征
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SkeletonView"
s.version = "1.8.8"
s.version = "1.17.1"
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.
+58 -2
View File
@@ -28,6 +28,8 @@
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 */; };
1E291F3E2540655D0018D602 /* UIView+Autolayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E291F3D2540655D0018D602 /* UIView+Autolayout.swift */; };
1E291F3F2540655D0018D602 /* UIView+Autolayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E291F3D2540655D0018D602 /* UIView+Autolayout.swift */; };
1E6C67A2230E76CC0019D87B /* SkeletonTransitionStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4CE587C22EEE65200333067 /* SkeletonTransitionStyle.swift */; };
1E6C67A3230E76CE0019D87B /* UIView+Transitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4CE587B22EEE63100333067 /* UIView+Transitions.swift */; };
1EE42E1F23FF25CC00BF665A /* ProcessInfo+XCTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE42E1E23FF25CC00BF665A /* ProcessInfo+XCTest.swift */; };
@@ -161,6 +163,7 @@
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>"; };
1E291F3D2540655D0018D602 /* UIView+Autolayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Autolayout.swift"; sourceTree = "<group>"; };
1EE42E1E23FF25CC00BF665A /* ProcessInfo+XCTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+XCTest.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>"; };
@@ -443,6 +446,7 @@
F5307E2D1FB0E5E400EE67C5 /* UIView+Frame.swift */,
F5804771230ECD0000066D02 /* UIView+IBInspectable.swift */,
F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */,
1E291F3D2540655D0018D602 /* UIView+Autolayout.swift */,
5600784323FD293D00669AD6 /* UITableView+VisibleSections.swift */,
1EE42E1E23FF25CC00BF665A /* ProcessInfo+XCTest.swift */,
);
@@ -526,6 +530,7 @@
17DD0DFC207FB27400C56334 /* Frameworks */,
17DD0DFD207FB27400C56334 /* Headers */,
17DD0DFE207FB27400C56334 /* Resources */,
1E3CFFD5250AD9A400DDB852 /* SwiftLint */,
);
buildRules = (
);
@@ -563,6 +568,7 @@
52D6D9781BEFF229002C0205 /* Frameworks */,
52D6D9791BEFF229002C0205 /* Headers */,
52D6D97A1BEFF229002C0205 /* Resources */,
1E3CFFD3250AD95300DDB852 /* SwiftLint */,
);
buildRules = (
);
@@ -613,6 +619,7 @@
52D6D97B1BEFF229002C0205 = {
CreatedOnToolsVersion = 7.1;
LastSwiftMigration = 1000;
ProvisioningStyle = Automatic;
};
F5F899F11FABA607002E8FDA = {
CreatedOnToolsVersion = 9.1;
@@ -679,12 +686,52 @@
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
1E3CFFD3250AD95300DDB852 /* SwiftLint */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = SwiftLint;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
};
1E3CFFD5250AD9A400DDB852 /* SwiftLint */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = SwiftLint;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
17DD0DFB207FB27400C56334 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
872D5A5721C177E20037D763 /* UIView+Extension.swift in Sources */,
1E291F3F2540655D0018D602 /* UIView+Autolayout.swift in Sources */,
17DD0E1F207FB32100C56334 /* RecursiveProtocol.swift in Sources */,
872D5A5D21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */,
17DD0E1E207FB32100C56334 /* PrepareForSkeletonProtocol.swift in Sources */,
@@ -745,6 +792,7 @@
buildActionMask = 2147483647;
files = (
E4CE588B22EEF70D00333067 /* UIView+Transitions.swift in Sources */,
1E291F3E2540655D0018D602 /* UIView+Autolayout.swift in Sources */,
872D5A5621C177E20037D763 /* UIView+Extension.swift in Sources */,
872D5A5C21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */,
F54CF5E42024CEB000330B0D /* UIView+CollectionSkeleton.swift in Sources */,
@@ -1070,8 +1118,10 @@
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = NO;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1084,6 +1134,8 @@
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = "com.SkeletonView.SkeletonView-iOS";
PRODUCT_NAME = SkeletonView;
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@@ -1095,8 +1147,10 @@
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = NO;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1108,6 +1162,8 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.SkeletonView.SkeletonView-iOS";
PRODUCT_NAME = SkeletonView;
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 5.0;
+1 -1
View File
@@ -22,7 +22,7 @@ class SkeletonViewAppearance: Appearance {
var tintColor: UIColor = .skeletonDefault
var gradient: SkeletonGradient = SkeletonGradient(baseColor: .skeletonDefault)
var gradient = SkeletonGradient(baseColor: .skeletonDefault)
var multilineHeight: CGFloat = 15
@@ -9,25 +9,30 @@ class SkeletonLayerBuilder {
var colors: [UIColor] = []
var holder: UIView?
@discardableResult
func setSkeletonType(_ type: SkeletonType) -> SkeletonLayerBuilder {
self.skeletonType = type
return self
}
@discardableResult
func addColor(_ color: UIColor) -> SkeletonLayerBuilder {
return addColors([color])
}
@discardableResult
func addColors(_ colors: [UIColor]) -> SkeletonLayerBuilder {
self.colors.append(contentsOf: colors)
return self
}
@discardableResult
func setHolder(_ holder: UIView) -> SkeletonLayerBuilder {
self.holder = holder
return self
}
@discardableResult
func build() -> SkeletonLayer? {
guard let type = skeletonType,
let holder = holder
@@ -12,41 +12,55 @@ class SkeletonMultilineLayerBuilder {
var cornerRadius: Int?
var multilineSpacing: CGFloat = SkeletonAppearance.default.multilineSpacing
var paddingInsets: UIEdgeInsets = .zero
var isRTL: Bool = false
@discardableResult
func setSkeletonType(_ type: SkeletonType) -> SkeletonMultilineLayerBuilder {
self.skeletonType = type
return self
}
@discardableResult
func setIndex(_ index: Int) -> SkeletonMultilineLayerBuilder {
self.index = index
return self
}
func setHeight(_ height: CGFloat) -> SkeletonMultilineLayerBuilder {
self.height = height
return self
}
@discardableResult
func setHeight(_ height: CGFloat) -> SkeletonMultilineLayerBuilder {
self.height = height
return self
}
@discardableResult
func setWidth(_ width: CGFloat) -> SkeletonMultilineLayerBuilder {
self.width = width
return self
}
@discardableResult
func setCornerRadius(_ radius: Int) -> SkeletonMultilineLayerBuilder {
self.cornerRadius = radius
return self
}
@discardableResult
func setMultilineSpacing(_ spacing: CGFloat) -> SkeletonMultilineLayerBuilder {
self.multilineSpacing = spacing
return self
}
@discardableResult
func setPadding(_ insets: UIEdgeInsets) -> SkeletonMultilineLayerBuilder {
self.paddingInsets = insets
return self
}
@discardableResult
func setIsRTL(_ isRTL: Bool) -> SkeletonMultilineLayerBuilder {
self.isRTL = isRTL
return self
}
func build() -> CALayer? {
guard let type = skeletonType,
@@ -59,7 +73,11 @@ class SkeletonMultilineLayerBuilder {
let layer = type.layer
layer.anchorPoint = .zero
layer.name = CALayer.skeletonSubLayersName
layer.updateLayerFrame(for: index, size: CGSize(width: width, height: height), multilineSpacing: self.multilineSpacing, paddingInsets: paddingInsets)
layer.updateLayerFrame(for: index,
size: CGSize(width: width, height: height),
multilineSpacing: multilineSpacing,
paddingInsets: paddingInsets,
isRTL: isRTL)
layer.cornerRadius = CGFloat(radius)
layer.masksToBounds = true
@@ -29,6 +29,18 @@ extension CollectionSkeleton where Self: UIScrollView {
var estimatedNumberOfRows: Int { return 0 }
func addDummyDataSource() {}
func removeDummyDataSource(reloadAfter: Bool) {}
func disableUserInteraction() { isUserInteractionEnabled = false; isScrollEnabled = false }
func enableUserInteraction() { isUserInteractionEnabled = true; isScrollEnabled = true }
func disableUserInteraction() {
if isUserInteractionDisabledWhenSkeletonIsActive {
isUserInteractionEnabled = false
isScrollEnabled = false
}
}
func enableUserInteraction() {
if isUserInteractionDisabledWhenSkeletonIsActive {
isUserInteractionEnabled = true
isScrollEnabled = true
}
}
}
@@ -8,7 +8,6 @@
import UIKit
public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
func numSections(in collectionSkeletonView: UICollectionView) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
@@ -18,7 +17,7 @@ public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
public extension SkeletonCollectionViewDataSource {
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return skeletonView.estimatedNumberOfRows
return UICollectionView.automaticNumberOfSkeletonItems
}
func collectionSkeletonView(_ skeletonView: UICollectionView,
@@ -31,4 +30,3 @@ public extension SkeletonCollectionViewDataSource {
}
public protocol SkeletonCollectionViewDelegate: UICollectionViewDelegate { }
@@ -7,11 +7,20 @@
//
import UIKit
extension UICollectionView: CollectionSkeleton {
public static let automaticNumberOfSkeletonItems = -1
var estimatedNumberOfRows: Int {
guard let flowlayout = collectionViewLayout as? UICollectionViewFlowLayout else { return 0 }
return Int(ceil(frame.height/flowlayout.itemSize.height))
switch flowlayout.scrollDirection {
case .vertical:
return Int(ceil(frame.height / flowlayout.itemSize.height))
case .horizontal:
return Int(ceil(frame.width / flowlayout.itemSize.width))
default:
return 0
}
}
var skeletonDataSource: SkeletonCollectionDataSource? {
@@ -70,7 +79,7 @@ public extension UICollectionView {
self.skeletonDataSource = dataSource
performBatchUpdates({
self.reloadData()
}) { (done) in
}) { done in
completion(done)
}
@@ -28,11 +28,21 @@ class SkeletonCollectionDataSource: NSObject {
// MARK: - UITableViewDataSource
extension SkeletonCollectionDataSource: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return originalTableViewDataSource?.numSections(in: tableView) ?? 0
originalTableViewDataSource?.numSections(in: tableView) ?? 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return originalTableViewDataSource?.collectionSkeletonView(tableView, numberOfRowsInSection: section) ?? 0
guard let originalTableViewDataSource = originalTableViewDataSource else {
return 0
}
let numberOfRows = originalTableViewDataSource.collectionSkeletonView(tableView, numberOfRowsInSection: section)
if numberOfRows == UITableView.automaticNumberOfSkeletonRows {
return tableView.estimatedNumberOfRows
} else {
return numberOfRows
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
@@ -46,11 +56,21 @@ extension SkeletonCollectionDataSource: UITableViewDataSource {
// MARK: - UICollectionViewDataSource
extension SkeletonCollectionDataSource: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return originalCollectionViewDataSource?.numSections(in: collectionView) ?? 0
originalCollectionViewDataSource?.numSections(in: collectionView) ?? 0
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return originalCollectionViewDataSource?.collectionSkeletonView(collectionView, numberOfItemsInSection: section) ?? 0
guard let originalCollectionViewDataSource = originalCollectionViewDataSource else {
return 0
}
let numberOfItems = originalCollectionViewDataSource.collectionSkeletonView(collectionView, numberOfItemsInSection: section)
if numberOfItems == UICollectionView.automaticNumberOfSkeletonItems {
return collectionView.estimatedNumberOfRows
} else {
return numberOfItems
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
@@ -63,9 +83,8 @@ extension SkeletonCollectionDataSource: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView,
viewForSupplementaryElementOfKind kind: String,
at indexPath: IndexPath) -> UICollectionReusableView {
if let viewIdentifier = originalCollectionViewDataSource?.collectionSkeletonView(collectionView, supplementaryViewIdentifierOfKind: kind, at: indexPath) {
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: viewIdentifier, for: indexPath)
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: viewIdentifier, for: indexPath)
skeletonViewIfContainerSkeletonIsActive(container: collectionView, view: view)
return view
}
@@ -21,25 +21,11 @@ class SkeletonCollectionDelegate: NSObject {
// MARK: - UITableViewDelegate
extension SkeletonCollectionDelegate: UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if let viewIdentifier = originalTableViewDelegate?.collectionSkeletonView(tableView, identifierForHeaderInSection: section),
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: viewIdentifier) {
skeletonViewIfContainerSkeletonIsActive(container: tableView, view: header)
return header
}
return nil
headerOrFooterView(tableView, for: originalTableViewDelegate?.collectionSkeletonView(tableView, identifierForHeaderInSection: section))
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
if let viewIdentifier = originalTableViewDelegate?.collectionSkeletonView(tableView, identifierForFooterInSection: section),
let footer = tableView.dequeueReusableHeaderFooterView(withIdentifier: viewIdentifier) {
skeletonViewIfContainerSkeletonIsActive(container: tableView, view: footer)
return footer
}
return nil
headerOrFooterView(tableView, for: originalTableViewDelegate?.collectionSkeletonView(tableView, identifierForFooterInSection: section))
}
func tableView(_ tableView: UITableView, didEndDisplayingHeaderView view: UIView, forSection section: Int) {
@@ -56,6 +42,12 @@ extension SkeletonCollectionDelegate: UITableViewDelegate {
cell.hideSkeleton()
originalTableViewDelegate?.tableView?(tableView, didEndDisplaying: cell, forRowAt: indexPath)
}
private func headerOrFooterView(_ tableView: UITableView, for viewIdentifier: String? ) -> UIView? {
guard let viewIdentifier = viewIdentifier, let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: viewIdentifier) else { return nil }
skeletonViewIfContainerSkeletonIsActive(container: tableView, view: header)
return header
}
}
// MARK: - UICollectionViewDelegate
@@ -16,7 +16,7 @@ public protocol SkeletonTableViewDataSource: UITableViewDataSource {
public extension SkeletonTableViewDataSource {
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int {
return skeletonView.estimatedNumberOfRows
return UITableView.automaticNumberOfSkeletonRows
}
func numSections(in collectionSkeletonView: UITableView) -> Int { return 1 }
@@ -11,8 +11,10 @@ import UIKit
public typealias ReusableHeaderFooterIdentifier = String
extension UITableView: CollectionSkeleton {
public static let automaticNumberOfSkeletonRows = -1
var estimatedNumberOfRows: Int {
return Int(ceil(frame.height/rowHeight))
return Int(ceil(frame.height / rowHeight))
}
var skeletonDataSource: SkeletonCollectionDataSource? {
@@ -43,8 +45,7 @@ extension UITableView: CollectionSkeleton {
self.skeletonDataSource = dataSource
if let originalDelegate = self.delegate as? SkeletonTableViewDelegate,
!(originalDelegate is SkeletonCollectionDelegate)
{
!(originalDelegate is SkeletonCollectionDelegate) {
let delegate = SkeletonCollectionDelegate(tableViewDelegate: originalDelegate)
self.skeletonDelegate = delegate
}
+3 -3
View File
@@ -1,4 +1,3 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import Foundation
@@ -10,6 +9,7 @@ enum SkeletonEnvironmentKey: String {
extension Dictionary {
subscript (_ key: SkeletonEnvironmentKey) -> Value? {
// swiftlint:disable:next force_cast
return self[key.rawValue as! Key]
}
}
@@ -19,7 +19,7 @@ func printSkeletonHierarchy(in view: UIView) {
}
func skeletonLog(_ message: String) {
if let _ = ProcessInfo.processInfo.environment[.debugMode] {
if ProcessInfo.processInfo.environment[.debugMode] != nil {
print(message)
}
}
@@ -28,7 +28,7 @@ extension UIView {
public var skeletonDescription: String {
var description = "<\(type(of: self)): \(Unmanaged.passUnretained(self).toOpaque())"
let subSkeletons = subviewsSkeletonables
if subSkeletons.count != 0 {
if !subSkeletons.isEmpty {
description += " | (\(subSkeletons.count)) subSkeletons"
}
if isSkeletonable {
+60 -36
View File
@@ -30,15 +30,23 @@ extension CAGradientLayer {
struct SkeletonMultilinesLayerConfig {
var lines: Int
var lineHeight: CGFloat? = nil
var lineHeight: CGFloat
var type: SkeletonType
var lastLineFillPercent: Int
var multilineCornerRadius: Int
var multilineSpacing: CGFloat
var paddingInsets: UIEdgeInsets
var isRTL: Bool
/// Returns padding insets taking into account if the RTL is activated
var calculatedPaddingInsets: UIEdgeInsets {
UIEdgeInsets(top: paddingInsets.top,
left: isRTL ? paddingInsets.right : paddingInsets.left,
bottom: paddingInsets.bottom,
right: isRTL ? paddingInsets.left : paddingInsets.right)
}
}
// MARK: Skeleton sublayers
extension CALayer {
static let skeletonSubLayersName = "SkeletonSubLayersName"
@@ -48,9 +56,10 @@ extension CALayer {
}
func addMultilinesLayers(for config: SkeletonMultilinesLayerConfig) {
let numberOfSublayers = config.lines == 1 ? 1 : calculateNumLines(for: config)
var height = config.lineHeight ?? SkeletonAppearance.default.multilineHeight
if numberOfSublayers == 1 {
let numberOfSublayers = config.lines > 0 ? config.lines : calculateNumLines(for: config)
var height = config.lineHeight
if numberOfSublayers == 1 && SkeletonAppearance.default.renderSingleLineAsView {
height = bounds.height
}
@@ -60,6 +69,7 @@ extension CALayer {
.setMultilineSpacing(config.multilineSpacing)
.setPadding(config.paddingInsets)
.setHeight(height)
.setIsRTL(config.isRTL)
(0..<numberOfSublayers).forEach { index in
let width = calculatedWidthForLine(at: index, totalLines: numberOfSublayers, lastLineFillPercent: config.lastLineFillPercent, paddingInsets: config.paddingInsets)
@@ -76,16 +86,21 @@ extension CALayer {
let currentSkeletonSublayers = skeletonSublayers
let numberOfSublayers = currentSkeletonSublayers.count
let lastLineFillPercent = config.lastLineFillPercent
let paddingInsets = config.paddingInsets
let paddingInsets = config.calculatedPaddingInsets
let multilineSpacing = config.multilineSpacing
var height = config.lineHeight ?? SkeletonAppearance.default.multilineHeight
if numberOfSublayers == 1 {
var height = config.lineHeight
if numberOfSublayers == 1 && SkeletonAppearance.default.renderSingleLineAsView {
height = bounds.height
}
for (index, layer) in currentSkeletonSublayers.enumerated() {
let width = calculatedWidthForLine(at: index, totalLines: numberOfSublayers, lastLineFillPercent: lastLineFillPercent, paddingInsets: paddingInsets)
layer.updateLayerFrame(for: index, size: CGSize(width: width, height: height), multilineSpacing: multilineSpacing, paddingInsets: paddingInsets)
layer.updateLayerFrame(for: index,
size: CGSize(width: width, height: height),
multilineSpacing: multilineSpacing,
paddingInsets: paddingInsets,
isRTL: config.isRTL)
}
}
@@ -97,16 +112,42 @@ extension CALayer {
return width
}
func updateLayerFrame(for index: Int, size: CGSize, multilineSpacing: CGFloat, paddingInsets: UIEdgeInsets) {
let spaceRequiredForEachLine = SkeletonAppearance.default.multilineHeight + multilineSpacing
frame = CGRect(x: paddingInsets.left, y: CGFloat(index) * spaceRequiredForEachLine + paddingInsets.top, width: size.width, height: size.height)
func updateLayerFrame(for index: Int, size: CGSize, multilineSpacing: CGFloat, paddingInsets: UIEdgeInsets, isRTL: Bool) {
let spaceRequiredForEachLine = size.height + multilineSpacing
let newFrame = CGRect(x: paddingInsets.left,
y: CGFloat(index) * spaceRequiredForEachLine + paddingInsets.top,
width: size.width,
height: size.height)
frame = flipRectForRTLIfNeeded(newFrame, isRTL: isRTL)
}
private func calculateNumLines(for config: SkeletonMultilinesLayerConfig) -> Int {
let definedNumberOfLines = config.lines
let requiredSpaceForEachLine = config.lineHeight + config.multilineSpacing
let neededLines = round(CGFloat(bounds.height - config.paddingInsets.top - config.paddingInsets.bottom) / CGFloat(requiredSpaceForEachLine))
guard neededLines.isNormal else {
return 0
}
private func calculateNumLines(for config: SkeletonMultilinesLayerConfig) -> Int {
let requiredSpaceForEachLine = (config.lineHeight ?? SkeletonAppearance.default.multilineHeight) + config.multilineSpacing
var numberOfSublayers = Int(round(CGFloat(bounds.height - config.paddingInsets.top - config.paddingInsets.bottom)/CGFloat(requiredSpaceForEachLine)))
if config.lines != 0, config.lines <= numberOfSublayers { numberOfSublayers = config.lines }
return numberOfSublayers
let calculatedNumberOfLines = Int(neededLines)
guard calculatedNumberOfLines > 0 else {
return 1
}
if definedNumberOfLines > 0, definedNumberOfLines <= calculatedNumberOfLines {
return definedNumberOfLines
}
return calculatedNumberOfLines
}
private func flipRectForRTLIfNeeded(_ rect: CGRect, isRTL: Bool) -> CGRect {
var newRect = rect
if isRTL {
newRect.origin.x = (superlayer?.bounds.width ?? 0) - rect.origin.x - rect.width
}
return newRect
}
}
@@ -115,6 +156,8 @@ public extension CALayer {
var pulse: CAAnimation {
let pulseAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.backgroundColor))
pulseAnimation.fromValue = backgroundColor
// swiftlint:disable:next force_unwrapping
pulseAnimation.toValue = UIColor(cgColor: backgroundColor!).complementaryColor.cgColor
pulseAnimation.duration = 1
pulseAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
@@ -124,25 +167,6 @@ public extension CALayer {
return pulseAnimation
}
var sliding: CAAnimation {
let startPointAnim = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.startPoint))
startPointAnim.fromValue = CGPoint(x: -1, y: 0.5)
startPointAnim.toValue = CGPoint(x:1, y: 0.5)
let endPointAnim = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.endPoint))
endPointAnim.fromValue = CGPoint(x: 0, y: 0.5)
endPointAnim.toValue = CGPoint(x:2, y: 0.5)
let animGroup = CAAnimationGroup()
animGroup.animations = [startPointAnim, endPointAnim]
animGroup.duration = 1.5
animGroup.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
animGroup.repeatCount = .infinity
animGroup.isRemovedOnCompletion = false
return animGroup
}
func playAnimation(_ anim: SkeletonLayerAnimation, key: String, completion: (() -> Void)? = nil) {
skeletonSublayers.recursiveSearch(leafBlock: {
DispatchQueue.main.async { CATransaction.begin() }
+2 -2
View File
@@ -4,10 +4,10 @@ import Foundation
extension Int {
var whitespace: String {
return whitespaces
whitespaces
}
var whitespaces: String {
return String(repeating: " ", count: self)
String(repeating: " ", count: self)
}
}
+11 -7
View File
@@ -22,7 +22,7 @@ extension UIColor {
public var complementaryColor: UIColor {
if #available(iOS 13, tvOS 13, *) {
return UIColor { traitCollection in
return UIColor { _ in
return self.isLight() ? self.darker : self.lighter
}
} else {
@@ -31,11 +31,11 @@ extension UIColor {
}
public var lighter: UIColor {
return adjust(by: 1.35)
adjust(by: 1.35)
}
public var darker: UIColor {
return adjust(by: 0.94)
adjust(by: 0.94)
}
func adjust(by percent: CGFloat) -> UIColor {
@@ -45,11 +45,12 @@ extension UIColor {
}
func makeGradient() -> [UIColor] {
return [self, self.complementaryColor, self]
[self, self.complementaryColor, self]
}
}
public extension UIColor {
// swiftlint:disable operator_usage_whitespace
static var greenSea = UIColor(0x16a085)
static var turquoise = UIColor(0x1abc9c)
static var emerald = UIColor(0x2ecc71)
@@ -71,13 +72,16 @@ public extension UIColor {
static var pomegranate = UIColor(0xc0392b)
static var silver = UIColor(0xbdc3c7)
static var asbestos = UIColor(0x7f8c8d)
// swiftlint:enable operator_usage_whitespace
static var skeletonDefault: UIColor {
if #available(iOS 13, tvOS 13, *) {
return UIColor { traitCollection in
switch traitCollection.userInterfaceStyle {
case .dark: return .darkClouds
default: return .clouds
case .dark:
return .darkClouds
default:
return .clouds
}
}
} else {
@@ -13,15 +13,13 @@ extension UITableView {
headerRect = rectForHeader(inSection: $1)
}
if headerRect != nil {
let visiblePartOfTableView: CGRect = CGRect(
x: contentOffset.x,
y: contentOffset.y,
width: bounds.size.width,
height: bounds.size.height
)
if let headerRect = headerRect {
let visiblePartOfTableView = CGRect(x: contentOffset.x,
y: contentOffset.y,
width: bounds.size.width,
height: bounds.size.height)
if (visiblePartOfTableView.intersects(headerRect!)) {
if visiblePartOfTableView.intersects(headerRect) {
return $0 + [$1]
}
}
@@ -0,0 +1,33 @@
// Copyright © 2020 SkeletonView. All rights reserved.
import UIKit
// MARK: Frame
extension UIView {
var widthConstraints: [NSLayoutConstraint] {
nonContentSizeLayoutConstraints.filter { $0.firstAttribute == NSLayoutConstraint.Attribute.width }
}
var heightConstraints: [NSLayoutConstraint] {
nonContentSizeLayoutConstraints.filter { $0.firstAttribute == NSLayoutConstraint.Attribute.height }
}
var skeletonHeightConstraints: [NSLayoutConstraint] {
nonContentSizeLayoutConstraints.filter {
$0.firstAttribute == NSLayoutConstraint.Attribute.height
&& $0.identifier?.contains("SkeletonView.Constraint.Height") ?? false
}
}
@discardableResult
func setHeight(equalToConstant constant: CGFloat) -> NSLayoutConstraint {
let heightConstraint = heightAnchor.constraint(equalToConstant: constant)
heightConstraint.identifier = "SkeletonView.Constraint.Height.\(constant)"
NSLayoutConstraint.activate([heightConstraint])
return heightConstraint
}
var nonContentSizeLayoutConstraints: [NSLayoutConstraint] {
constraints.filter({ "\(type(of: $0))" != "NSContentSizeLayoutConstraint" })
}
}
+11 -4
View File
@@ -5,6 +5,7 @@ import UIKit
// codebeat:disable[TOO_MANY_IVARS]
enum ViewAssociatedKeys {
static var skeletonable = "skeletonable"
static var hiddenWhenSkeletonIsActive = "hiddenWhenSkeletonIsActive"
static var status = "status"
static var skeletonLayer = "layer"
static var flowDelegate = "flowDelegate"
@@ -12,8 +13,10 @@ enum ViewAssociatedKeys {
static var viewState = "viewState"
static var labelViewState = "labelViewState"
static var imageViewState = "imageViewState"
static var buttonViewState = "buttonViewState"
static var currentSkeletonConfig = "currentSkeletonConfig"
static var skeletonCornerRadius = "skeletonCornerRadius"
static var disabledWhenSkeletonIsActive = "disabledWhenSkeletonIsActive"
}
// codebeat:enable[TOO_MANY_IVARS]
@@ -38,13 +41,17 @@ extension UIView {
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.currentSkeletonConfig) }
}
var status: Status! {
var status: Status {
get { return ao_get(pkey: &ViewAssociatedKeys.status) as? Status ?? .off }
set { ao_set(newValue ?? .off, pkey: &ViewAssociatedKeys.status) }
set { ao_set(newValue, pkey: &ViewAssociatedKeys.status) }
}
var isSkeletonAnimated: Bool! {
var isSkeletonAnimated: Bool {
get { return ao_get(pkey: &ViewAssociatedKeys.isSkeletonAnimated) as? Bool ?? false }
set { ao_set(newValue ?? false, pkey: &ViewAssociatedKeys.isSkeletonAnimated) }
set { ao_set(newValue, pkey: &ViewAssociatedKeys.isSkeletonAnimated) }
}
var isSuperviewAStackView: Bool {
superview is UIStackView
}
}
+18 -16
View File
@@ -10,32 +10,38 @@ import UIKit
// MARK: Frame
extension UIView {
var maxBoundsEstimated: CGRect {
var definedMaxBounds: CGRect {
if let parentStackView = (superview as? UIStackView) {
var origin: CGPoint = .zero
switch parentStackView.alignment {
case .trailing:
origin.x = maxWidthEstimated
origin.x = definedMaxWidth
default:
break
}
return CGRect(origin: origin, size: maxSizeEstimated)
return CGRect(origin: origin, size: definedMaxSize)
}
return CGRect(origin: .zero, size: maxSizeEstimated)
return CGRect(origin: .zero, size: definedMaxSize)
}
var maxSizeEstimated: CGSize {
return CGSize(width: maxWidthEstimated, height: maxHeightEstimated)
var definedMaxSize: CGSize {
CGSize(width: definedMaxWidth, height: definedMaxHeight)
}
var maxWidthEstimated: CGFloat {
let constraintsWidth = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutConstraint.Attribute.width })
return max(between: frame.size.width, andContantsOf: constraintsWidth)
var definedMaxWidth: CGFloat {
max(between: frame.size.width, andContantsOf: widthConstraints)
}
var maxHeightEstimated: CGFloat {
let constraintsHeight = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutConstraint.Attribute.height })
return max(between: frame.size.height, andContantsOf: constraintsHeight)
var definedMaxHeight: CGFloat {
max(between: frame.size.height, andContantsOf: heightConstraints)
}
var isRTL: Bool {
if #available(iOS 10.0, *), #available(tvOS 10.0, *) {
return effectiveUserInterfaceLayoutDirection == .rightToLeft
} else {
return false
}
}
private func max(between value: CGFloat, andContantsOf constraints: [NSLayoutConstraint]) -> CGFloat {
@@ -46,8 +52,4 @@ extension UIView {
})
return max
}
var nonContentSizeLayoutConstraints: [NSLayoutConstraint] {
return constraints.filter({ "\(type(of: $0))" != "NSContentSizeLayoutConstraint" })
}
}
+27 -6
View File
@@ -8,6 +8,18 @@ public extension UIView {
get { return skeletonable }
set { skeletonable = newValue }
}
@IBInspectable
var isHiddenWhenSkeletonIsActive: Bool {
get { return hiddenWhenSkeletonIsActive }
set { hiddenWhenSkeletonIsActive = newValue }
}
@IBInspectable
var isUserInteractionDisabledWhenSkeletonIsActive: Bool {
get { return disabledWhenSkeletonIsActive }
set { disabledWhenSkeletonIsActive = newValue }
}
@IBInspectable
var skeletonCornerRadius: Float {
@@ -16,17 +28,26 @@ public extension UIView {
}
var isSkeletonActive: Bool {
return status == .on || (subviewsSkeletonables.first(where: { $0.isSkeletonActive }) != nil)
return status == .on || subviewsSkeletonables.contains(where: { $0.isSkeletonActive })
}
private var skeletonable: Bool! {
private var skeletonable: Bool {
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonable) as? Bool ?? false }
set { ao_set(newValue ?? false, pkey: &ViewAssociatedKeys.skeletonable) }
set { ao_set(newValue, pkey: &ViewAssociatedKeys.skeletonable) }
}
private var hiddenWhenSkeletonIsActive: Bool {
get { return ao_get(pkey: &ViewAssociatedKeys.hiddenWhenSkeletonIsActive) as? Bool ?? false }
set { ao_set(newValue, pkey: &ViewAssociatedKeys.hiddenWhenSkeletonIsActive) }
}
private var disabledWhenSkeletonIsActive: Bool {
get { return ao_get(pkey: &ViewAssociatedKeys.disabledWhenSkeletonIsActive) as? Bool ?? true }
set { ao_set(newValue, pkey: &ViewAssociatedKeys.disabledWhenSkeletonIsActive) }
}
private var skeletonableCornerRadius: Float! {
private var skeletonableCornerRadius: Float {
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonCornerRadius) as? Float ?? 0.0 }
set { ao_set(newValue ?? 0.0, pkey: &ViewAssociatedKeys.skeletonCornerRadius) }
set { ao_set(newValue, pkey: &ViewAssociatedKeys.skeletonCornerRadius) }
}
}
+3 -2
View File
@@ -2,7 +2,7 @@
import Foundation
//Partially copy/pasted from https://github.com/jameslintaylor/AssociatedObjects/blob/master/AssociatedObjects/AssociatedObjects.swift
// Partially copy/pasted from https://github.com/jameslintaylor/AssociatedObjects/blob/master/AssociatedObjects/AssociatedObjects.swift
enum AssociationPolicy: UInt {
// raw values map to objc_AssociationPolicy's raw values
case assign = 0
@@ -12,11 +12,12 @@ enum AssociationPolicy: UInt {
case retainNonatomic = 1
var objc: objc_AssociationPolicy {
// swiftlint:disable:next force_unwrapping
return objc_AssociationPolicy(rawValue: rawValue)!
}
}
protocol AssociatedObjects: class { }
protocol AssociatedObjects: AnyObject { }
// transparent wrappers
extension AssociatedObjects {
@@ -10,6 +10,10 @@ import UIKit
extension UIView {
@objc func prepareViewForSkeleton() {
if isUserInteractionDisabledWhenSkeletonIsActive {
isUserInteractionEnabled = false
}
startTransition { [weak self] in
self?.backgroundColor = .clear
}
@@ -17,10 +21,48 @@ extension UIView {
}
extension UILabel {
var desiredHeightBasedOnNumberOfLines: CGFloat {
let lineHeight = constraintHeight ?? SkeletonAppearance.default.multilineHeight
let spaceNeededForEachLine = lineHeight * CGFloat(numberOfLines)
let spaceNeededForSpaces = skeletonLineSpacing * CGFloat(numberOfLines - 1)
let padding = paddingInsets.top + paddingInsets.bottom
return spaceNeededForEachLine + spaceNeededForSpaces + padding
}
func updateHeightConstraintsIfNeeded() {
guard numberOfLines > 1 || numberOfLines == 0 else { return }
// Workaround to simulate content when the label is contained in a `UIStackView`.
if isSuperviewAStackView, bounds.height == 0 {
// This is a placeholder text to simulate content because it's contained in a stack view in order to prevent that the content size will be zero.
text = " "
}
let desiredHeight = desiredHeightBasedOnNumberOfLines
if desiredHeight > definedMaxHeight {
backupHeightConstraints = heightConstraints
NSLayoutConstraint.deactivate(heightConstraints)
setHeight(equalToConstant: desiredHeight)
}
}
func restoreBackupHeightConstraintsIfNeeded() {
guard !backupHeightConstraints.isEmpty else { return }
NSLayoutConstraint.activate(backupHeightConstraints)
backupHeightConstraints.removeAll()
}
override func prepareViewForSkeleton() {
backgroundColor = .clear
if isUserInteractionDisabledWhenSkeletonIsActive {
isUserInteractionEnabled = false
}
resignFirstResponder()
startTransition { [weak self] in
self?.updateHeightConstraintsIfNeeded()
self?.textColor = .clear
}
}
@@ -29,6 +71,11 @@ extension UILabel {
extension UITextView {
override func prepareViewForSkeleton() {
backgroundColor = .clear
if isUserInteractionDisabledWhenSkeletonIsActive {
isUserInteractionEnabled = false
}
resignFirstResponder()
startTransition { [weak self] in
self?.textColor = .clear
@@ -36,11 +83,42 @@ extension UITextView {
}
}
extension UITextField {
override func prepareViewForSkeleton() {
backgroundColor = .clear
resignFirstResponder()
startTransition { [weak self] in
self?.textColor = .clear
self?.placeholder = nil
}
}
}
extension UIImageView {
override func prepareViewForSkeleton() {
backgroundColor = .clear
if isUserInteractionDisabledWhenSkeletonIsActive {
isUserInteractionEnabled = false
}
startTransition { [weak self] in
self?.image = nil
}
}
}
extension UIButton {
override func prepareViewForSkeleton() {
backgroundColor = .clear
if isUserInteractionDisabledWhenSkeletonIsActive {
isUserInteractionEnabled = false
}
startTransition { [weak self] in
self?.setTitle(nil, for: .normal)
}
}
}
+2 -4
View File
@@ -9,7 +9,7 @@ protocol IterableElement {}
extension UIView: IterableElement {}
extension CALayer: IterableElement {}
//MARK: Recursive
// MARK: Recursive
protocol Recursive {
associatedtype Element: IterableElement
func recursiveSearch(leafBlock: VoidBlock, recursiveBlock: RecursiveBlock<Element>)
@@ -17,12 +17,10 @@ protocol Recursive {
extension Array: Recursive where Element: IterableElement {
func recursiveSearch(leafBlock: VoidBlock, recursiveBlock: RecursiveBlock<Element>) {
guard count > 0 else {
guard !isEmpty else {
leafBlock()
return
}
forEach { recursiveBlock($0) }
}
}
+12 -5
View File
@@ -4,22 +4,29 @@ import Foundation
extension DispatchQueue {
private static var _onceTracker = [String]()
class func once(token: String, block:()->Void) {
class func once(token: String, block: () -> Void) {
objc_sync_enter(self); defer { objc_sync_exit(self) }
guard !_onceTracker.contains(token) else { return }
_onceTracker.append(token)
block()
}
class func removeOnce(token: String, block: () -> Void) {
objc_sync_enter(self); defer { objc_sync_exit(self) }
guard let index = _onceTracker.firstIndex(of: token) else { return }
_onceTracker.remove(at: index)
block()
}
}
func swizzle(selector originalSelector: Selector, with swizzledSelector: Selector, inClass: AnyClass, usingClass: AnyClass) {
guard let originalMethod = class_getInstanceMethod(inClass, originalSelector),
let swizzledMethod = class_getInstanceMethod(usingClass, swizzledSelector)
else { return }
if (class_addMethod(inClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))) {
if class_addMethod(inClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)) {
class_replaceMethod(inClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
@@ -7,17 +7,14 @@ enum MultilineAssociatedKeys {
static var multilineCornerRadius = "multilineCornerRadius"
static var multilineSpacing = "multilineSpacing"
static var paddingInsets = "paddingInsets"
static var backupHeightConstraints = "backupHeightConstraints"
}
protocol ContainsMultilineText {
var multilineTextFont: UIFont? { get }
var constraintHeight: CGFloat? { get }
var numLines: Int { get }
var lastLineFillingPercent: Int { get }
var multilineCornerRadius: Int { get }
var multilineSpacing: CGFloat { get }
var paddingInsets: UIEdgeInsets { get }
}
extension ContainsMultilineText {
var numLines: Int { return 0 }
}
+14 -7
View File
@@ -8,17 +8,19 @@ public extension UILabel {
get { return lastLineFillingPercent }
set { lastLineFillingPercent = min(newValue, 100) }
}
@IBInspectable
var linesCornerRadius: Int {
get { return multilineCornerRadius }
set { multilineCornerRadius = min(newValue, 10) }
set { multilineCornerRadius = newValue }
}
@IBInspectable
var skeletonLineSpacing: CGFloat {
get { return multilineSpacing }
set { multilineSpacing = min(newValue, 10) }
set { multilineSpacing = newValue }
}
@IBInspectable
var skeletonPaddingInsets: UIEdgeInsets {
get { return paddingInsets }
set { paddingInsets = newValue }
@@ -26,10 +28,10 @@ public extension UILabel {
}
extension UILabel: ContainsMultilineText {
var multilineTextFont: UIFont? {
return font
}
var constraintHeight: CGFloat? {
backupHeightConstraints.first?.constant
}
var numLines: Int {
return numberOfLines
}
@@ -53,4 +55,9 @@ extension UILabel: ContainsMultilineText {
get { return ao_get(pkey: &MultilineAssociatedKeys.paddingInsets) as? UIEdgeInsets ?? .zero }
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.paddingInsets) }
}
var backupHeightConstraints: [NSLayoutConstraint] {
get { return ao_get(pkey: &MultilineAssociatedKeys.backupHeightConstraints) as? [NSLayoutConstraint] ?? [] }
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.backupHeightConstraints) }
}
}
@@ -12,16 +12,15 @@ public extension UITextView {
@IBInspectable
var linesCornerRadius: Int {
get { return multilineCornerRadius }
set { multilineCornerRadius = min(newValue, 10) }
set { multilineCornerRadius = newValue }
}
@IBInspectable
var skeletonLineSpacing: CGFloat {
get { return multilineSpacing }
set { multilineSpacing = min(newValue, 10) }
set { multilineSpacing = newValue }
}
@IBInspectable
var skeletonPaddingInsets: UIEdgeInsets {
get { return paddingInsets }
set { paddingInsets = newValue }
@@ -29,9 +28,13 @@ public extension UITextView {
}
extension UITextView: ContainsMultilineText {
var multilineTextFont: UIFont? {
return font
}
var constraintHeight: CGFloat? {
heightConstraints.first?.constant
}
var numLines: Int {
-1
}
var lastLineFillingPercent: Int {
get {
+77 -13
View File
@@ -24,20 +24,26 @@ extension UIView: Recoverable {
}
@objc func recoverViewState(forced: Bool) {
guard let safeViewState = viewState else { return }
guard let storedViewState = viewState else { return }
startTransition { [weak self] in
self?.layer.cornerRadius = safeViewState.cornerRadius
self?.layer.masksToBounds = safeViewState.clipToBounds
guard let self = self else { return }
if safeViewState.backgroundColor != self?.backgroundColor || forced {
self?.backgroundColor = safeViewState.backgroundColor
self.layer.cornerRadius = storedViewState.cornerRadius
self.layer.masksToBounds = storedViewState.clipToBounds
if self.isUserInteractionDisabledWhenSkeletonIsActive {
self.isUserInteractionEnabled = storedViewState.isUserInteractionsEnabled
}
if self.backgroundColor == .clear || forced {
self.backgroundColor = storedViewState.backgroundColor
}
}
}
}
extension UILabel{
extension UILabel {
var labelState: RecoverableTextViewState? {
get { return ao_get(pkey: &ViewAssociatedKeys.labelViewState) as? RecoverableTextViewState }
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.labelViewState) }
@@ -51,14 +57,22 @@ extension UILabel{
override func recoverViewState(forced: Bool) {
super.recoverViewState(forced: forced)
startTransition { [weak self] in
self?.textColor = self?.labelState?.textColor
self?.text = self?.labelState?.text
self?.isUserInteractionEnabled = self?.labelState?.isUserInteractionsEnabled ?? false
guard let self = self,
let storedLabelState = self.labelState else {
return
}
NSLayoutConstraint.deactivate(self.skeletonHeightConstraints)
self.restoreBackupHeightConstraintsIfNeeded()
if self.textColor == .clear || forced {
self.textColor = storedLabelState.textColor
}
}
}
}
extension UITextView{
extension UITextView {
var textState: RecoverableTextViewState? {
get { return ao_get(pkey: &ViewAssociatedKeys.labelViewState) as? RecoverableTextViewState }
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.labelViewState) }
@@ -72,9 +86,38 @@ extension UITextView{
override func recoverViewState(forced: Bool) {
super.recoverViewState(forced: forced)
startTransition { [weak self] in
self?.textColor = self?.textState?.textColor
self?.text = self?.textState?.text
self?.isUserInteractionEnabled = self?.textState?.isUserInteractionsEnabled ?? false
guard let storedLabelState = self?.textState else { return }
if self?.textColor == .clear || forced {
self?.textColor = storedLabelState.textColor
}
}
}
}
extension UITextField {
var textState: RecoverableTextFieldState? {
get { return ao_get(pkey: &ViewAssociatedKeys.labelViewState) as? RecoverableTextFieldState }
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.labelViewState) }
}
override func saveViewState() {
super.saveViewState()
textState = RecoverableTextFieldState(view: self)
}
override func recoverViewState(forced: Bool) {
super.recoverViewState(forced: forced)
startTransition { [weak self] in
guard let storedLabelState = self?.textState else { return }
if self?.textColor == .clear || forced {
self?.textColor = storedLabelState.textColor
}
if self?.placeholder == nil || forced {
self?.placeholder = storedLabelState.placeholder
}
}
}
}
@@ -97,3 +140,24 @@ extension UIImageView {
}
}
}
extension UIButton {
var buttonState: RecoverableButtonViewState? {
get { return ao_get(pkey: &ViewAssociatedKeys.buttonViewState) as? RecoverableButtonViewState }
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.buttonViewState) }
}
override func saveViewState() {
super.saveViewState()
buttonState = RecoverableButtonViewState(view: self)
}
override func recoverViewState(forced: Bool) {
super.recoverViewState(forced: forced)
startTransition { [weak self] in
if self?.title(for: .normal) == nil {
self?.setTitle(self?.buttonState?.title, for: .normal)
}
}
}
}
+20 -6
View File
@@ -12,29 +12,35 @@ struct RecoverableViewState {
var backgroundColor: UIColor?
var cornerRadius: CGFloat
var clipToBounds: Bool
var isUserInteractionsEnabled: Bool
init(view: UIView) {
self.backgroundColor = view.backgroundColor
self.clipToBounds = view.layer.masksToBounds
self.cornerRadius = view.layer.cornerRadius
self.isUserInteractionsEnabled = view.isUserInteractionEnabled
}
}
struct RecoverableTextViewState {
var text: String?
var textColor: UIColor?
var isUserInteractionsEnabled: Bool
init(view: UILabel) {
self.textColor = view.textColor
self.text = view.text
self.isUserInteractionsEnabled = view.isUserInteractionEnabled
}
init(view: UITextView) {
self.textColor = view.textColor
self.text = view.text
self.isUserInteractionsEnabled = view.isUserInteractionEnabled
}
}
struct RecoverableTextFieldState {
var textColor: UIColor?
var placeholder: String?
init(view: UITextField) {
self.textColor = view.textColor
self.placeholder = view.placeholder
}
}
@@ -45,3 +51,11 @@ struct RecoverableImageViewState {
self.image = view.image
}
}
struct RecoverableButtonViewState {
var title: String?
init(view: UIButton) {
self.title = view.titleLabel?.text
}
}
+17 -17
View File
@@ -18,42 +18,42 @@ public enum GradientDirection {
case topLeftBottomRight
case bottomRightTopLeft
public func slidingAnimation(duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation {
return SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: self, duration: duration)
public func slidingAnimation(duration: CFTimeInterval = 1.5, autoreverses: Bool = false) -> SkeletonLayerAnimation {
return SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: self, duration: duration, autoreverses: autoreverses)
}
// codebeat:disable[ABC]
var startPoint: GradientAnimationPoint {
switch self {
case .leftRight:
return (from: CGPoint(x:-1, y:0.5), to: CGPoint(x:1, y:0.5))
return (from: CGPoint(x: -1, y: 0.5), to: CGPoint(x: 1, y: 0.5))
case .rightLeft:
return (from: CGPoint(x:1, y:0.5), to: CGPoint(x:-1, y:0.5))
return (from: CGPoint(x: 1, y: 0.5), to: CGPoint(x: -1, y: 0.5))
case .topBottom:
return (from: CGPoint(x:0.5, y:-1), to: CGPoint(x:0.5, y:1))
return (from: CGPoint(x: 0.5, y: -1), to: CGPoint(x: 0.5, y: 1))
case .bottomTop:
return (from: CGPoint(x:0.5, y:1), to: CGPoint(x:0.5, y:-1))
return (from: CGPoint(x: 0.5, y: 1), to: CGPoint(x: 0.5, y: -1))
case .topLeftBottomRight:
return (from: CGPoint(x:-1, y:-1), to: CGPoint(x:1, y:1))
return (from: CGPoint(x: -1, y: -1), to: CGPoint(x: 1, y: 1))
case .bottomRightTopLeft:
return (from: CGPoint(x:1, y:1), to: CGPoint(x:-1, y:-1))
return (from: CGPoint(x: 1, y: 1), to: CGPoint(x: -1, y: -1))
}
}
var endPoint: GradientAnimationPoint {
switch self {
case .leftRight:
return (from: CGPoint(x:0, y:0.5), to: CGPoint(x:2, y:0.5))
return (from: CGPoint(x: 0, y: 0.5), to: CGPoint(x: 2, y: 0.5))
case .rightLeft:
return ( from: CGPoint(x:2, y:0.5), to: CGPoint(x:0, y:0.5))
return ( from: CGPoint(x: 2, y: 0.5), to: CGPoint(x: 0, y: 0.5))
case .topBottom:
return ( from: CGPoint(x:0.5, y:0), to: CGPoint(x:0.5, y:2))
return ( from: CGPoint(x: 0.5, y: 0), to: CGPoint(x: 0.5, y: 2))
case .bottomTop:
return ( from: CGPoint(x:0.5, y:2), to: CGPoint(x:0.5, y:0))
return ( from: CGPoint(x: 0.5, y: 2), to: CGPoint(x: 0.5, y: 0))
case .topLeftBottomRight:
return ( from: CGPoint(x:0, y:0), to: CGPoint(x:2, y:2))
return ( from: CGPoint(x: 0, y: 0), to: CGPoint(x: 2, y: 2))
case .bottomRightTopLeft:
return ( from: CGPoint(x:2, y:2), to: CGPoint(x:0, y:0))
return ( from: CGPoint(x: 2, y: 2), to: CGPoint(x: 0, y: 0))
}
}
// codebeat:enable[ABC]
@@ -62,9 +62,8 @@ public enum GradientDirection {
public class SkeletonAnimationBuilder {
public init() { }
public func makeSlidingAnimation(withDirection direction: GradientDirection, duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation {
return { layer in
public func makeSlidingAnimation(withDirection direction: GradientDirection, duration: CFTimeInterval = 1.5, autoreverses: Bool = false) -> SkeletonLayerAnimation {
return { _ in
let startPointAnim = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.startPoint))
startPointAnim.fromValue = direction.startPoint.from
startPointAnim.toValue = direction.startPoint.to
@@ -78,6 +77,7 @@ public class SkeletonAnimationBuilder {
animGroup.duration = duration
animGroup.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
animGroup.repeatCount = .infinity
animGroup.autoreverses = autoreverses
animGroup.isRemovedOnCompletion = false
return animGroup
+6 -8
View File
@@ -22,14 +22,12 @@ struct SkeletonConfig {
/// Transition style
var transition: SkeletonTransitionStyle
init(
type: SkeletonType,
colors: [UIColor],
gradientDirection: GradientDirection? = nil,
animated: Bool = false,
animation: SkeletonLayerAnimation? = nil,
transition: SkeletonTransitionStyle = .crossDissolve(0.25)
) {
init(type: SkeletonType,
colors: [UIColor],
gradientDirection: GradientDirection? = nil,
animated: Bool = false,
animation: SkeletonLayerAnimation? = nil,
transition: SkeletonTransitionStyle = .crossDissolve(0.25)) {
self.type = type
self.colors = colors
self.gradientDirection = gradientDirection
+1 -1
View File
@@ -2,7 +2,7 @@
import UIKit
protocol SkeletonFlowDelegate {
protocol SkeletonFlowDelegate: AnyObject {
func willBeginShowingSkeletons(rootView: UIView)
func didShowSkeletons(rootView: UIView)
func willBeginUpdatingSkeletons(rootView: UIView)
+14 -11
View File
@@ -23,12 +23,12 @@ public enum SkeletonType {
}
}
var layerAnimation: SkeletonLayerAnimation {
func defaultLayerAnimation(isRTL: Bool) -> SkeletonLayerAnimation {
switch self {
case .solid:
return { $0.pulse }
case .gradient:
return { $0.sliding }
return { SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: isRTL ? .rightLeft : .leftRight) }()
}
}
}
@@ -49,7 +49,7 @@ struct SkeletonLayer {
self.holder = holder
self.maskLayer = type.layer
self.maskLayer.anchorPoint = .zero
self.maskLayer.bounds = holder.maxBoundsEstimated
self.maskLayer.bounds = holder.definedMaxBounds
self.maskLayer.cornerRadius = CGFloat(holder.skeletonCornerRadius)
addTextLinesIfNeeded()
self.maskLayer.tint(withColors: colors)
@@ -61,7 +61,7 @@ struct SkeletonLayer {
}
func layoutIfNeeded() {
if let bounds = holder?.maxBoundsEstimated {
if let bounds = holder?.definedMaxBounds {
maskLayer.bounds = bounds
}
updateLinesIfNeeded()
@@ -83,34 +83,37 @@ struct SkeletonLayer {
/// If there is more than one line, or custom preferences have been set for a single line, draw custom layers
func addTextLinesIfNeeded() {
guard let textView = holderAsTextView else { return }
let lineHeight = textView.constraintHeight ?? SkeletonAppearance.default.multilineHeight
let config = SkeletonMultilinesLayerConfig(lines: textView.numLines,
lineHeight: textView.multilineTextFont?.lineHeight,
lineHeight: lineHeight,
type: type,
lastLineFillPercent: textView.lastLineFillingPercent,
multilineCornerRadius: textView.multilineCornerRadius,
multilineSpacing: textView.multilineSpacing,
paddingInsets: textView.paddingInsets)
paddingInsets: textView.paddingInsets,
isRTL: holder?.isRTL ?? false)
maskLayer.addMultilinesLayers(for: config)
}
func updateLinesIfNeeded() {
guard let textView = holderAsTextView else { return }
let lineHeight = textView.constraintHeight ?? SkeletonAppearance.default.multilineHeight
let config = SkeletonMultilinesLayerConfig(lines: textView.numLines,
lineHeight: textView.multilineTextFont?.lineHeight,
lineHeight: lineHeight,
type: type,
lastLineFillPercent: textView.lastLineFillingPercent,
multilineCornerRadius: textView.multilineCornerRadius,
multilineSpacing: textView.multilineSpacing,
paddingInsets: textView.paddingInsets)
paddingInsets: textView.paddingInsets,
isRTL: holder?.isRTL ?? false)
maskLayer.updateMultilinesLayers(for: config)
}
var holderAsTextView: ContainsMultilineText? {
guard let textView = holder as? ContainsMultilineText,
(textView.numLines == 0 || textView.numLines > 1 || textView.numLines == 1 && !SkeletonAppearance.default.renderSingleLineAsView) else {
(textView.numLines == -1 || textView.numLines == 0 || textView.numLines > 1 || textView.numLines == 1 && !SkeletonAppearance.default.renderSingleLineAsView) else {
return nil
}
return textView
@@ -119,7 +122,7 @@ struct SkeletonLayer {
extension SkeletonLayer {
func start(_ anim: SkeletonLayerAnimation? = nil, completion: (() -> Void)? = nil) {
let animation = anim ?? type.layerAnimation
let animation = anim ?? type.defaultLayerAnimation(isRTL: holder?.isRTL ?? false)
contentLayer.playAnimation(animation, key: "skeletonAnimation", completion: completion)
}
+32 -3
View File
@@ -114,6 +114,9 @@ extension UIView {
}
private func recursiveShowSkeleton(skeletonConfig config: SkeletonConfig, root: UIView? = nil) {
if isHiddenWhenSkeletonIsActive {
isHidden = true
}
guard isSkeletonable && !isSkeletonActive else { return }
currentSkeletonConfig = config
swizzleLayoutSubviews()
@@ -121,7 +124,7 @@ extension UIView {
addDummyDataSourceIfNeeded()
subviewsSkeletonables.recursiveSearch(leafBlock: {
showSkeletonIfNotActive(skeletonConfig: config)
}){ subview in
}) { subview in
subview.recursiveShowSkeleton(skeletonConfig: config)
}
@@ -133,7 +136,7 @@ extension UIView {
private func showSkeletonIfNotActive(skeletonConfig config: SkeletonConfig) {
guard !isSkeletonActive else { return }
saveViewState()
isUserInteractionEnabled = false
prepareViewForSkeleton()
addSkeletonLayer(skeletonConfig: config)
}
@@ -183,8 +186,12 @@ extension UIView {
private func recursiveHideSkeleton(reloadDataAfter reload: Bool, transition: SkeletonTransitionStyle, root: UIView? = nil) {
guard isSkeletonActive else { return }
if isHiddenWhenSkeletonIsActive {
isHidden = false
}
currentSkeletonConfig?.transition = transition
isUserInteractionEnabled = true
unSwizzleLayoutSubviews()
unSwizzleTraitCollectionDidChange()
removeDummyDataSourceIfNeeded(reloadAfter: reload)
subviewsSkeletonables.recursiveSearch(leafBlock: {
recoverViewState(forced: false)
@@ -228,6 +235,17 @@ extension UIView {
}
}
private func unSwizzleLayoutSubviews() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
DispatchQueue.removeOnce(token: "UIView.SkeletonView.swizzleLayoutSubviews") {
swizzle(selector: #selector(UIView.skeletonLayoutSubviews),
with: #selector(UIView.layoutSubviews),
inClass: UIView.self,
usingClass: UIView.self)
}
}
}
private func swizzleTraitCollectionDidChange() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
DispatchQueue.once(token: "UIView.SkeletonView.swizzleTraitCollectionDidChange") {
@@ -238,6 +256,17 @@ extension UIView {
}
}
}
private func unSwizzleTraitCollectionDidChange() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
DispatchQueue.removeOnce(token: "UIView.SkeletonView.swizzleTraitCollectionDidChange") {
swizzle(selector: #selector(UIView.skeletonTraitCollectionDidChange(_:)),
with: #selector(UIView.traitCollectionDidChange(_:)),
inClass: UIView.self,
usingClass: UIView.self)
}
}
}
}
extension UIView {
+12 -8
View File
@@ -4,46 +4,50 @@ import UIKit
extension UIView {
@objc var subviewsSkeletonables: [UIView] {
return subviewsToSkeleton.filter { $0.isSkeletonable }
subviewsToSkeleton.filter { $0.isSkeletonable }
}
@objc var subviewsToSkeleton: [UIView] {
return subviews
subviews
}
}
extension UITableView {
override var subviewsToSkeleton: [UIView] {
return visibleCells + visibleSectionHeaders + visibleSectionFooters
// on `UIViewController'S onViewDidLoad`, the window is still nil.
// Some developer trying to call `view.showAnimatedSkeleton()`
// when the request or data is loading which sometimes happens before the ViewDidAppear
guard window != nil else { return [] }
return subviews
}
}
extension UITableViewCell {
override var subviewsToSkeleton: [UIView] {
return contentView.subviews
contentView.subviews
}
}
extension UITableViewHeaderFooterView {
override var subviewsToSkeleton: [UIView] {
return contentView.subviews
contentView.subviews
}
}
extension UICollectionView {
override var subviewsToSkeleton: [UIView] {
return subviews
subviews
}
}
extension UICollectionViewCell {
override var subviewsToSkeleton: [UIView] {
return contentView.subviews
contentView.subviews
}
}
extension UIStackView {
override var subviewsToSkeleton: [UIView] {
return arrangedSubviews
arrangedSubviews
}
}
@@ -8,7 +8,6 @@ extension CALayer {
switch transition {
case .none:
completion?()
break
case .crossDissolve(let duration):
layer.contentLayer.setOpacity(from: 0, to: 1, duration: duration, completion: completion)
}
+4
View File
@@ -1,6 +1,10 @@
default_platform(:ios)
podspec_name = "SkeletonView.podspec"
lane :bump_version do |options|
version_bump_podspec(path: @podspec_name, version_number: options[:next_version])
end
lane :release_current do
version = version_get_podspec(path: @podspec_name)
if git_tag_exists(tag: version)
+6 -1
View File
@@ -12,9 +12,14 @@ Install _fastlane_ using
```
[sudo] gem install fastlane -NV
```
or alternatively using `brew cask install fastlane`
or alternatively using `brew install fastlane`
# Available Actions
### bump_version
```
fastlane bump_version
```
### release_current
```
fastlane release_current