Compare commits

...

119 Commits

Author SHA1 Message Date
Juanpe Catalán d665d01469 Merge pull request #160 from techinpark/master
[Feature] Add korean README.md
2019-06-30 12:59:57 +02:00
Eduard Bosch Bertran 623b326e7b Merge pull request #158 from eduardbosch/feature/allow_updating_skeleton_layers
Update skeleton layout and configuration
2019-06-25 23:32:16 +02:00
Fernando 3a4bbadcbe feat: add translator of korean documents 2019-06-21 21:19:51 +09:00
Fernando dc92e652cb feat: update document korean 2019-06-21 21:17:56 +09:00
Fernando e3f38fdd25 feat: update korean docs for custom animations 2019-06-21 18:47:01 +09:00
Fernando 6c1c4dda42 feat: update korean document. 2019-06-21 18:36:15 +09:00
Fernando 39c60b6da2 feat: update korean documents 2019-06-21 13:05:39 +09:00
Fernando c75d34e7de Merge pull request #1 from techinpark/feature/korean-docs
[Feature] Docs: add Korean document
2019-06-21 11:30:34 +09:00
Fernando c5a0bc0050 feat: update korean document 2019-06-20 20:20:09 +09:00
Fernando be74f093f3 feat: add korean document 2019-06-20 20:18:07 +09:00
Fernando 0f90edfc8e feat: update korean documents 2019-06-20 20:07:43 +09:00
Fernando 67daffd7c9 feat: translate for korean document 2019-06-20 18:41:52 +09:00
Fernando 961c731888 feat: add korean documentation 2019-06-20 10:54:47 +09:00
Eduard Bosch Bertran 39fa679ddc doc: Add new functionality to documentation 2019-06-19 23:59:29 +02:00
Eduard Bosch Bertran 40c107af23 doc: Update version 2019-06-19 23:49:54 +02:00
Eduard Bosch Bertran 0781fceab7 doc: Update changelog 2019-06-19 23:49:35 +02:00
Juanpe Catalán eef7a87eb6 Merge pull request #156 from gorillatech/master
fix - support UICollectionView's headers and footers (UICollectionReusableView)
2019-06-19 09:51:04 +02:00
Eduard Bosch Bertran be25db821b config: Add colletion view example scheme 2019-06-18 23:21:54 +02:00
Eduard Bosch Bertran 9e014dab0e refactor: Reorder methods 2019-06-18 23:21:54 +02:00
Eduard Bosch Bertran f53b722b52 refactor: Use SkeletonConfig to show and update skeleton views 2019-06-18 23:21:54 +02:00
Eduard Bosch Bertran bd02497124 fix: Skeleton new cells if container is skeletoned 2019-06-18 23:21:54 +02:00
Eduard Bosch Bertran 1802e76da9 fix: Avoid showing non skeletoned views on sample app 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran 318b2546c6 feat: Add method to layout skeleton view if needed 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran 37df94b09f style: Add space 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran 81599f2980 refactor: Rename showSkeletonLeafBlock and do not return a block 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran b28358663b refactor: showOrUpdate is now update and only updates skeleton properties 2019-06-18 23:21:53 +02:00
Juanpe Catalán b364218213 feat: Add SkeletonConfig documentation 2019-06-18 23:21:53 +02:00
Juanpe Catalán 18048c9bcd feat: Create SkeletonConfig to save the config of skeleton active 2019-06-18 23:21:53 +02:00
Juanpe Catalán 510fe34476 feat: REAMD zh 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran 932579edc0 doc: Add missing dot 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran 96673df005 doc: Add feature to changelog file 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran 43c1f2e5ac refactor: Rename update methods to showOrUpdate 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran 37f6205421 refactor: Rename layer to skeletonLayer to match the other methods 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran 3471288307 refactor: Wrap layer with guard 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran b77edf84b2 config: Ignore .idea folder 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran 3e87293a84 fix: Request layout if needed befor updating skeleton views 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran f637c20ea9 fix: Update multiline skeleton layers on skeleton layer update 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran 1737536057 refactor: Use update instead of show when updating skeleton views 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran 31fe04b92e refactor: Reuse show skeleton 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran f7d577c269 feat: Add begin updating notification 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran 3a7be98057 fix: Update table / collection skeleton views 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran dab24e40a7 fix: Only create flowDelegate if update is called before show 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran c729dec667 test: Update skeleton views in controller bounds update 2019-06-18 23:21:53 +02:00
Eduard Bosch Bertran f6f0a11968 feat: Update skeleton layer bounds and color 2019-06-18 23:21:53 +02:00
Guglielmo Faglioni 6784de9597 fix - support UICollectionView's headers and footers (UICollectionReusableView) 2019-06-18 17:44:50 +02:00
Juanpe Catalán 6e91b3fa71 Merge branch 'master' into develop 2019-06-04 19:01:14 +02:00
Juanpe Catalán 4b6f55cf66 fix: readme 2019-06-04 19:01:01 +02:00
Juanpe Catalán beaf22d696 Merge branch 'master' of https://github.com/Juanpe/SkeletonView 2019-06-04 18:57:16 +02:00
Juanpe Catalán c62586ccff Merge branch 'develop' 2019-06-04 18:57:02 +02:00
Juanpe Catalán 55cb95e3fa Merge branch 'swift_package_manager' into develop 2019-06-04 18:56:49 +02:00
Juanpe Catalán d72b28dc53 feat: Update readme file 2019-06-04 18:56:35 +02:00
Juanpe Catalán b742179fec Create FUNDING.yml 2019-06-04 18:47:15 +02:00
Juanpe Catalán f2b0329db9 upload Package.swift 2019-06-04 18:02:44 +02:00
Juanpe Catalán 8271110682 feat: update scheme 2019-04-26 09:52:58 +02:00
Juanpe Catalán 096099e2a0 feat: Update travis file 2019-04-23 22:00:57 +02:00
Juanpe Catalán 4d4fda2177 Merge branch 'develop' 2019-04-23 21:36:50 +02:00
Juanpe Catalán 4296d1cb04 feat: Update gemfile 2019-04-23 21:36:32 +02:00
Juanpe Catalán db19eae968 feat: Update swift-version 2019-04-23 20:44:39 +02:00
Juanpe Catalán 1d2256b4e6 feat: Update podspec 2019-04-23 20:43:21 +02:00
Juanpe Catalán 4e5c8627dd feat: Update Gemfile 2019-04-23 20:42:05 +02:00
Juanpe Catalán 6012b0c84b Merge pull request #148 from Juanpe/develop
feat: Migrate to Swift 5
2019-04-23 20:39:37 +02:00
Juanpe Catalán 09e1fe31e0 feat: Migrate to Swift 5 2019-04-23 20:38:03 +02:00
Juanpe Catalán 4fb8020c83 Merge pull request #144 from Juanpe/develop
Add tableview illustration
2019-04-03 16:43:57 +02:00
Juanpe Catalán 303ae5aeb8 feat: Update readme 2019-04-03 16:40:35 +02:00
Juanpe Catalán 4649c7e61f feat: Added tableview illustration 2019-04-03 16:39:00 +02:00
Juanpe Catalán 1671af6d28 Update README file 2019-02-28 15:24:00 +01:00
Juanpe Catalán 83e7e48caa feat: Bump cocoapod version 2019-02-26 17:54:53 +01:00
Juanpe Catalán c03af15070 Merge branch 'master' into develop 2019-02-02 10:30:30 +01:00
Juanpe Catalán ed82f74ba8 Merge pull request #131 from grifas/hotfix/reloadDataAfter
🐛 Fix  removeDummyDataSourceIfNeeded passing the reload parameter
2019-02-02 10:27:18 +01:00
Juanpe Catalán 01aba0b3ff Merge pull request #128 from jehlert-trip/feature/podspec_swift_version
Include swift_version in podspec
2019-02-02 10:24:14 +01:00
Aurélien Grifasi 6352030012 🐛 Fix removeDummyDataSourceIfNeeded passing the reload parameter 2019-01-15 11:53:45 +01:00
Justin Ehlert 4b06c30b9d Include swift_version in podspec 2019-01-10 14:33:15 -05:00
Juanpe Catalán 3dcde46fe9 feat: Update gitignore 2018-12-18 11:48:58 +01:00
Juanpe Catalán c9de28eb37 feat: update README file 2018-12-13 10:23:09 +01:00
Juanpe Catalán 915e838994 Merge branch 'develop'
# Conflicts:
#	README.md
2018-12-13 10:17:38 +01:00
Juanpe Catalán 74b44264ec Merge pull request #117 from Juanpe/feature/codebeat_refactor
Feature/codebeat refactor
2018-12-13 10:17:03 +01:00
Juanpe Catalán a9ad72aa0c feat: Update README file 2018-12-13 10:16:28 +01:00
Juanpe Catalán d535529be8 feat: Mute gradient switch 2018-12-13 10:03:13 +01:00
Juanpe Catalán a6bff05ca4 feat: Split line into two 2018-12-13 09:48:35 +01:00
Juanpe Catalán bc98cd600e feat: Refactor associated keys 2018-12-13 09:42:44 +01:00
Juanpe Catalán 8f1cfc0045 feat: Mute associated keys many instance variables 2018-12-13 08:51:35 +01:00
Juanpe Catalán 432ec6caa2 feat: Split associated keys 2018-12-12 18:59:34 +01:00
Juanpe Catalán e18aedbbf8 feat: Move examples files 2018-12-12 18:32:52 +01:00
Juanpe Catalán 4dc67ac3d7 feat: Refactor some classes 2018-12-12 18:28:42 +01:00
Juanpe Catalán aa29cd3d6b feat: Mute too many instance variables 2018-12-12 18:02:54 +01:00
Juanpe Catalán a2def55880 feat: Reorder section in README file 2018-12-12 16:01:44 +01:00
Juanpe Catalán 97f5e3e2e1 feat: Update README file 2018-12-12 15:59:40 +01:00
Juanpe Catalán 420cc7a669 Merge branch 'develop' 2018-12-12 09:11:09 +01:00
Juanpe Catalán b9601f56c3 Merge pull request #115 from Juanpe/feature/configure_fastlane
Feature/configure fastlane
2018-12-12 09:09:59 +01:00
Juanpe Catalán fbca1092c3 feat: Remove Appfile 2018-12-12 09:05:50 +01:00
Juanpe Catalán eb90faf413 fix: URL of bitrise badge 2018-12-11 11:21:57 +01:00
Juanpe Catalán f0cc7125ef feat: update readme 2018-12-11 11:20:44 +01:00
Juanpe Catalán f5b493d6f0 feat: Add bitrise in README file 2018-12-11 11:05:08 +01:00
Juanpe Catalán 48bc217413 feat: Update lane to create new version 2018-12-11 10:56:45 +01:00
Juanpe Catalán 516fcecce2 testing bitrise trigger 2018-12-11 10:41:51 +01:00
Juanpe Catalán 9052d5d806 feat: test bitrise 2018-12-11 09:44:15 +01:00
Juanpe Catalán 12dff52238 feat: remove useless code 2018-12-11 09:43:03 +01:00
Juanpe Catalán f0b4575de6 feat: Create fastfile 2018-12-11 09:42:09 +01:00
Juanpe Catalán 7be8ddd70a Merge pull request #114 from Juanpe/refactor_builder
feat: Create SkeletonLayerBuilder and SkeletonMultilineLayerBuilder
2018-12-10 20:48:49 +01:00
Juanpe Catalán a9628c0de8 feat: Create SkeletonLayerBuilder and SkeletonMultilineLayerBuilder 2018-12-10 20:04:07 +01:00
Juanpe Catalán 339191f016 feat: update changelog file 2018-12-10 19:34:33 +01:00
Juanpe Catalán 7022942d92 feat: Update Changelog file 2018-12-10 19:33:02 +01:00
Juanpe Catalán 03cf1bfea8 Merge pull request #113 from osterbergmarcus/patch-1
Adding swift news to mentioned section
2018-12-10 18:13:46 +01:00
Marcus Osterberg 9f46e7a647 Adding swift news to mentioned section 2018-12-06 19:31:12 +01:00
Juanpe Catalán 674cd1aa9f feat: Update README file to include swift 4.2 2018-10-04 16:15:40 +02:00
Juanpe Catalán 36a863b557 feat: Bump cocoapod version 2018-10-02 11:37:25 +02:00
Juanpe Catalán 3b83b85249 feat: Upgrade swift version 2018-10-02 11:33:55 +02:00
Juanpe Catalán afbb10d785 Update .travis.yml 2018-10-02 11:25:06 +02:00
Juanpe Catalán 99ccd1e640 Update .travis.yml 2018-10-02 11:15:02 +02:00
Juanpe Catalán e6834d1296 Merge pull request #99 from mohpor/master
Updated to Swift 4.2
2018-10-02 11:12:14 +02:00
Mohammad Porooshani 89cb6f4116 Updated to Swift 4.2
Signed-off-by: Mohammad Porooshani <porooshani@gmail.com>
2018-09-15 11:36:30 +04:30
Juanpe Catalán b74592e6be feat: Change guide image 2018-09-04 09:16:10 +02:00
Juanpe Catalán 5f79708f03 feat: Update thumbnail guide 2018-09-04 02:49:10 +02:00
Juanpe Catalán a83e1a00bf feat: Add guides section in README file 2018-09-04 00:52:14 +02:00
Juanpe Catalán 61e98b26ad Merge pull request #95 from arn00s/fix/demoProjectSettings
Fix/demo project settings
2018-08-20 23:48:30 +02:00
Arnaud 0ec754dfc5 use safe area for iPhone X 2018-08-20 17:22:09 +02:00
Arnaud 78b4b7b31c fix info.plist path 2018-08-20 17:17:51 +02:00
Juanpe Catalán e3ddc4fe98 Merge pull request #89 from brunomunizaf/translate-readme-brazil-portuguese
Translate readme file to Brazilian Portuguese
2018-08-13 11:23:34 +02:00
Bruno Muniz 13485e61f7 Translate readme file to Brazilian Portuguese 2018-08-13 11:11:23 +02:00
64 changed files with 2288 additions and 382 deletions
+9
View File
@@ -0,0 +1,9 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: skeletonview
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
custom: # Replace with a single custom sponsorship URL
+5 -1
View File
@@ -46,7 +46,7 @@ playground.xcworkspace
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
Pods/
# Carthage
#
@@ -66,3 +66,7 @@ fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
# JetBrains
.idea
+1 -1
View File
@@ -1 +1 @@
4.1
5.0
+4 -4
View File
@@ -1,7 +1,7 @@
language: objective-c
osx_image: xcode9.3
osx_image: xcode10.2
script:
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-iOS -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=11'
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-tvOS -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV,OS=11'
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonViewExample -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=11'
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-iOS -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=12'
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-tvOS -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV,OS=12'
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonViewExample -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=12'
after_success:
Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

+15
View File
@@ -1,6 +1,21 @@
# Change Log
All notable changes to this project will be documented in this file
## Next version
### New
- Adding swift news to mentioned section (thanks @osterbergmarcus).
## [Layout update (1.7)](https://github.com/Juanpe/SkeletonView/releases/tag/1.7)
### New
- Allow updating skeleton layout to recalculate skeleton bounds: `layoutSkeletonIfNeeded`. See the examples to know how to use it. (thanks @eduardbosch)
### Improvements
- 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)
### New
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="irH-dz-xqL">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="irH-dz-xqL">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -13,13 +13,60 @@
<!--View Controller-->
<scene sceneID="qda-qV-vJk">
<objects>
<viewController id="irH-dz-xqL" customClass="ViewController" customModule="SkeletonView" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="irH-dz-xqL" customClass="ViewController" customModule="SkeletonViewExampleUICollectionView" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Fso-nq-n6t">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eHI-ka-8vS">
<rect key="frame" x="0.0" y="20" width="375" height="243"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="obr-b6-dib">
<rect key="frame" x="45" y="142" width="287" height="78"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="78" id="jx6-c1-U0j"/>
</constraints>
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </string>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="lastLineFillPercent">
<integer key="value" value="40"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="avatar" translatesAutoresizingMaskIntoConstraints="NO" id="Ql9-Jy-aWM">
<rect key="frame" x="141" y="20" width="93" height="93"/>
<color key="backgroundColor" red="0.56078431370000004" green="0.59607843140000005" blue="0.7843137255" alpha="0.90709546230000004" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="height" constant="93" id="jlG-7K-wMd"/>
<constraint firstAttribute="width" constant="93" id="xHX-Y1-dvi"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</imageView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="243" id="0g6-3g-uII"/>
<constraint firstAttribute="trailing" secondItem="obr-b6-dib" secondAttribute="trailing" constant="43" id="3ms-Wk-qcn"/>
<constraint firstItem="obr-b6-dib" firstAttribute="centerX" secondItem="eHI-ka-8vS" secondAttribute="centerX" constant="1" id="B5s-DM-eR8"/>
<constraint firstAttribute="height" constant="243" id="GX5-3W-tUt"/>
<constraint firstItem="Ql9-Jy-aWM" firstAttribute="centerX" secondItem="eHI-ka-8vS" secondAttribute="centerX" id="HsA-ID-oSK"/>
<constraint firstItem="Ql9-Jy-aWM" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="top" constant="20" id="Hxu-ae-hXQ"/>
<constraint firstItem="obr-b6-dib" firstAttribute="leading" secondItem="eHI-ka-8vS" secondAttribute="leading" constant="45" id="eop-Gq-7mO"/>
<constraint firstItem="obr-b6-dib" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="top" constant="142" id="inJ-75-hGX"/>
</constraints>
<viewLayoutGuide key="safeArea" id="36i-gK-pIa"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</view>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="HKL-L0-T2w">
<rect key="frame" x="0.0" y="243" width="375" height="284"/>
<rect key="frame" x="0.0" y="263" width="375" height="264"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="mGU-kn-rfE">
<size key="itemSize" width="50" height="50"/>
@@ -86,52 +133,6 @@
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="140" id="qR5-cz-YAm"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eHI-ka-8vS">
<rect key="frame" x="0.0" y="0.0" width="375" height="243"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="obr-b6-dib">
<rect key="frame" x="45" y="142" width="287" height="78"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="78" id="jx6-c1-U0j"/>
</constraints>
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </string>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="lastLineFillPercent">
<integer key="value" value="40"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="avatar" translatesAutoresizingMaskIntoConstraints="NO" id="Ql9-Jy-aWM">
<rect key="frame" x="141" y="20" width="93" height="93"/>
<color key="backgroundColor" red="0.56078431370000004" green="0.59607843140000005" blue="0.7843137255" alpha="0.90709546230000004" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="height" constant="93" id="jlG-7K-wMd"/>
<constraint firstAttribute="width" constant="93" id="xHX-Y1-dvi"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</imageView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="243" id="0g6-3g-uII"/>
<constraint firstAttribute="trailing" secondItem="obr-b6-dib" secondAttribute="trailing" constant="43" id="3ms-Wk-qcn"/>
<constraint firstItem="obr-b6-dib" firstAttribute="centerX" secondItem="eHI-ka-8vS" secondAttribute="centerX" constant="1" id="B5s-DM-eR8"/>
<constraint firstAttribute="height" constant="243" id="GX5-3W-tUt"/>
<constraint firstItem="Ql9-Jy-aWM" firstAttribute="centerX" secondItem="eHI-ka-8vS" secondAttribute="centerX" id="HsA-ID-oSK"/>
<constraint firstItem="Ql9-Jy-aWM" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="top" constant="20" id="Hxu-ae-hXQ"/>
<constraint firstItem="obr-b6-dib" firstAttribute="leading" secondItem="eHI-ka-8vS" secondAttribute="leading" constant="45" id="eop-Gq-7mO"/>
<constraint firstItem="obr-b6-dib" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="top" constant="142" id="inJ-75-hGX"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
@@ -141,10 +142,10 @@
<constraint firstItem="JjA-MK-YzZ" firstAttribute="bottom" secondItem="2Gq-Y8-1TU" secondAttribute="bottom" id="AAr-ke-R7M"/>
<constraint firstItem="JjA-MK-YzZ" firstAttribute="trailing" secondItem="2Gq-Y8-1TU" secondAttribute="trailing" id="DtS-9c-zBC"/>
<constraint firstItem="HKL-L0-T2w" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="bottom" id="Jgf-jS-PLT"/>
<constraint firstItem="eHI-ka-8vS" firstAttribute="top" secondItem="2Gq-Y8-1TU" secondAttribute="top" id="Ux2-GF-HLK"/>
<constraint firstItem="JjA-MK-YzZ" firstAttribute="top" secondItem="HKL-L0-T2w" secondAttribute="bottom" id="XEd-Gf-KFI"/>
<constraint firstItem="2Gq-Y8-1TU" firstAttribute="trailing" secondItem="HKL-L0-T2w" secondAttribute="trailing" id="bNo-98-pE4"/>
<constraint firstItem="HKL-L0-T2w" firstAttribute="leading" secondItem="2Gq-Y8-1TU" secondAttribute="leading" id="iIq-cx-paX"/>
<constraint firstItem="eHI-ka-8vS" firstAttribute="top" secondItem="Fso-nq-n6t" secondAttribute="top" id="tlZ-Wl-lvE"/>
</constraints>
<viewLayoutGuide key="safeArea" id="2Gq-Y8-1TU"/>
</view>
@@ -14,7 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}

Before

Width:  |  Height:  |  Size: 933 B

After

Width:  |  Height:  |  Size: 933 B

@@ -13,7 +13,7 @@ class ViewController: UIViewController {
@IBOutlet weak var tableview: UITableView! {
didSet {
tableview.rowHeight = UITableViewAutomaticDimension
tableview.rowHeight = UITableView.automaticDimension
tableview.estimatedRowHeight = 120.0
}
}
@@ -44,6 +44,10 @@ class ViewController: UIViewController {
super.viewDidLoad()
tableview.isSkeletonable = true
}
override func viewDidLayoutSubviews() {
view.layoutSkeletonIfNeeded()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
@@ -67,25 +71,24 @@ class ViewController: UIViewController {
}
func refreshSkeleton() {
self.view.hideSkeleton()
if type == .gradient { showGradientSkeleton() }
else { showSolidSkeleton() }
if type == .gradient { showOrUpdateGradientSkeleton() }
else { showOrUpdatepdateSolidSkeleton() }
}
func showSolidSkeleton() {
func showOrUpdatepdateSolidSkeleton() {
if switchAnimated.isOn {
view.showAnimatedSkeleton(usingColor: colorSelectedView.backgroundColor!)
view.updateAnimatedSkeleton(usingColor: colorSelectedView.backgroundColor!)
} else {
view.showSkeleton(usingColor: colorSelectedView.backgroundColor!)
view.updateSkeleton(usingColor: colorSelectedView.backgroundColor!)
}
}
func showGradientSkeleton() {
func showOrUpdateGradientSkeleton() {
let gradient = SkeletonGradient(baseColor: colorSelectedView.backgroundColor!)
if switchAnimated.isOn {
view.showAnimatedGradientSkeleton(usingGradient: gradient)
view.updateAnimatedGradientSkeleton(usingGradient: gradient)
} else {
view.showGradientSkeleton(usingGradient: gradient)
view.updateGradientSkeleton(usingGradient: gradient)
}
}
@@ -133,7 +136,7 @@ extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource {
extension ViewController: SkeletonTableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 9
return 0
}
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
+4
View File
@@ -0,0 +1,4 @@
source "https://rubygems.org"
gem "fastlane"
gem 'cocoapods', '~> 1.7.0.beta.2'
+212
View File
@@ -0,0 +1,212 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.0)
activesupport (4.2.11.1)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.6.0)
public_suffix (>= 2.0.2, < 4.0)
atomos (0.1.3)
babosa (1.0.2)
claide (1.0.2)
cocoapods (1.7.0.beta.3)
activesupport (>= 4.0.2, < 5)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.7.0.beta.3)
cocoapods-deintegrate (>= 1.0.3, < 2.0)
cocoapods-downloader (>= 1.2.2, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-stats (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.3.1, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
fourflusher (>= 2.2.0, < 3.0)
gh_inspector (~> 1.0)
molinillo (~> 0.6.6)
nap (~> 1.0)
ruby-macho (~> 1.4)
xcodeproj (>= 1.8.2, < 2.0)
cocoapods-core (1.7.0.beta.3)
activesupport (>= 4.0.2, < 6)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
cocoapods-deintegrate (1.0.4)
cocoapods-downloader (1.2.2)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.0)
cocoapods-stats (1.1.0)
cocoapods-trunk (1.3.1)
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.1.0)
colored (1.2)
colored2 (3.1.2)
commander-fastlane (4.4.6)
highline (~> 1.7.2)
concurrent-ruby (1.1.5)
declarative (0.0.10)
declarative-option (0.1.0)
digest-crc (0.4.1)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.2)
emoji_regex (1.0.1)
escape (0.0.4)
excon (0.64.0)
faraday (0.15.4)
multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6)
faraday (>= 0.7.4)
http-cookie (~> 1.0.0)
faraday_middleware (0.13.1)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.5)
fastlane (2.121.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0)
colored
commander-fastlane (>= 4.4.6, < 5.0.0)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 2.0)
excon (>= 0.45.0, < 1.0.0)
faraday (~> 0.9)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 0.9)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-api-client (>= 0.21.2, < 0.24.0)
google-cloud-storage (>= 1.15.0, < 2.0.0)
highline (>= 1.7.2, < 2.0.0)
json (< 3.0.0)
mini_magick (~> 4.5.1)
multi_json
multi_xml (~> 0.5)
multipart-post (~> 2.0.0)
plist (>= 3.1.0, < 4.0.0)
public_suffix (~> 2.0.0)
rubyzip (>= 1.2.2, < 2.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
slack-notifier (>= 2.0.0, < 3.0.0)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.8.1, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
fourflusher (2.2.0)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
google-api-client (0.23.9)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.5, < 0.7.0)
httpclient (>= 2.8.1, < 3.0)
mime-types (~> 3.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
signet (~> 0.9)
google-cloud-core (1.3.0)
google-cloud-env (~> 1.0)
google-cloud-env (1.0.5)
faraday (~> 0.11)
google-cloud-storage (1.16.0)
digest-crc (~> 0.4)
google-api-client (~> 0.23)
google-cloud-core (~> 1.2)
googleauth (>= 0.6.2, < 0.10.0)
googleauth (0.6.7)
faraday (~> 0.12)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.7)
highline (1.7.10)
http-cookie (1.0.3)
domain_name (~> 0.5)
httpclient (2.8.3)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
json (2.2.0)
jwt (2.1.0)
memoist (0.16.0)
mime-types (3.2.2)
mime-types-data (~> 3.2015)
mime-types-data (3.2019.0331)
mini_magick (4.5.1)
minitest (5.11.3)
molinillo (0.6.6)
multi_json (1.13.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
nanaimo (0.2.6)
nap (1.1.0)
naturally (2.2.0)
netrc (0.11.0)
os (1.0.0)
plist (3.5.0)
public_suffix (2.0.5)
representable (3.0.4)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rouge (2.0.7)
ruby-macho (1.4.0)
rubyzip (1.2.2)
security (0.1.3)
signet (0.11.0)
addressable (~> 2.3)
faraday (~> 0.9)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.5)
CFPropertyList
naturally
slack-notifier (2.3.2)
terminal-notifier (2.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thread_safe (0.3.6)
tty-cursor (0.6.1)
tty-screen (0.6.5)
tty-spinner (0.9.0)
tty-cursor (~> 0.6.0)
tzinfo (1.2.5)
thread_safe (~> 0.1)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.6)
unicode-display_width (1.5.0)
word_wrap (1.0.0)
xcodeproj (1.8.2)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.2.6)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.0)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
ruby
DEPENDENCIES
cocoapods (~> 1.7.0.beta.2)
fastlane
BUNDLED WITH
1.16.6
+23
View File
@@ -0,0 +1,23 @@
// swift-tools-version:5.0
import PackageDescription
let package = Package(
name: "SkeletonView",
platforms: [
.iOS(.v9),
.tvOS(.v9)
],
products: [
.library(
name: "SkeletonView",
targets: ["SkeletonView"])
],
targets: [
.target(
name: "SkeletonView",
dependencies: [],
path: "Sources")
],
swiftLanguageVersions: [.v5]
)
+72 -26
View File
@@ -1,37 +1,36 @@
![](Assets/header2.jpg)
<p align="center">
<a href="https://travis-ci.org/Juanpe/SkeletonView">
<img src="https://img.shields.io/travis/Juanpe/SkeletonView.svg">
<a href="https://app.bitrise.io/app/6d289a17e22c8323">
<img src="https://app.bitrise.io/app/6d289a17e22c8323/status.svg?token=fI7gKC41XD9-aRXDScCKBw&branch=master">
</a>
<a href="https://instagram.github.io/IGListKit/">
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
<a href="https://github.com/Juanpe/SkeletonView">
<img src="https://img.shields.io/cocoapods/p/SkeletonView.svg" alt="Platforms">
</a>
<img src="https://img.shields.io/badge/Swift-4.1-orange.svg" />
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
</a>
<a href="https://github.com/Carthage/Carthage">
<img src="https://img.shields.io/badge/carthage-compatible-4BC51D.svg?style=flat" alt="Carthage" />
<img src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat" alt="Carthage" />
</a>
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/dt/SkeletonView.svg?style=flat" alt="CocoaPods downloads" />
<a href="https://github.com/apple/swift-package-manager">
<img src="https://img.shields.io/badge/SPM-compatible-brightgreen.svg" alt="SPM" />
</a>
<a href="https://twitter.com/JuanpeCatalan">
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
</a>
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">
<img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Paypal" />
</a>
<br/>
<a href="https://twitter.com/intent/tweet?text=Wow%20This%20library%20is%20awesome:&url=https%3A%2F%2Fgithub.com%2FJuanpe%2FSkeletonView">
<img src="https://img.shields.io/twitter/url/https/github.com/Juanpe/SkeletonView.svg?style=social" alt="License" />
</a>
<a href="https://twitter.com/JuanpeCatalan">
<img src="https://img.shields.io/twitter/follow/JuanpeCatalan.svg?style=social&label=Follow" alt="Twitter" />
</a>
</p>
🌎 Translations: [ [🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) by [@WhatsXie](https://twitter.com/WhatsXie) ]
🌎 Translations: </br>
[🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) by [@WhatsXie](https://twitter.com/WhatsXie) </br>
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) by [@brunomunizaf](https://twitter.com/brunomuniz_af) </br>
[🇰🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_ko.md) by [@techinpark](https://twitter.com/techinpark)
Today almost all apps have async processes, such as Api requests, long running processes, etc. And while the processes are working, usually developers place a loading view to show users that something is going on.
@@ -40,11 +39,11 @@ Today almost all apps have async processes, such as Api requests, long running p
Enjoy it! 🙂
* [Features](#-features)
* [Requirements](#-supported-os--sdk-versions)
* [Example Project](#-example)
* [Guides](#-guides)
* [Installation](#-installation)
* [Cocoapods](#using-cocoapods)
* [Carthage](#using-carthage)
* [SPM](#using-swift-package-manager)
* [How to use](#-how-to-use)
* [Collections](#-collections)
* [Multiline text](#-multiline-text)
@@ -54,6 +53,7 @@ Enjoy it! 🙂
* [Hierarchy](#-hierarchy)
* [Debug](#-debug)
* [Documentation](#-documentation)
* [Supported OS & SDK Versions](#-supported-os--sdk-versions)
* [Next steps](#-next-steps)
* [Contributing](#-contributing)
* [Mentions](#-mentions)
@@ -71,15 +71,9 @@ Enjoy it! 🙂
- [x] Simple Swift syntax
- [x] Lightweight readable codebase
### 📋 Supported OS & SDK Versions
## 🎬 Guides
* iOS 9.0+
* tvOS 9.0+
* Swift 4
### 🔮 Example
To run the example project, clone the repo and run `SkeletonViewExample` target.
[<img src="Assets/thumb_getting_started.png">](https://youtu.be/75kgOhWsPNA)
## 📲 Installation
@@ -99,6 +93,18 @@ Edit your `Cartfile` and specify the dependency:
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
dependencies: [
.package(url: "https://github.com/Juanpe/SkeletonView.git", from: "1.7")
]
```
## 🐒 How to use
Only **3** steps needed to use `SkeletonView`:
@@ -163,11 +169,36 @@ avatarImageView.isSkeletonable = true
> **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
#### 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()
}
```
#### 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
Now, ```SkeletonView``` is compatible with ```UITableView``` and ```UICollectionView```.
###### UITableView
#### UITableView
If you want to show the skeleton in a ```UITableView```, you need to conform to ```SkeletonTableViewDataSource``` protocol.
@@ -208,7 +239,15 @@ There is only one method you need to implement to let Skeleton know the cell ide
> **IMPORTANT!**
> If you are using resizable cells (`tableView.rowHeight = UITableViewAutomaticDimension` ), it's mandatory define the `estimatedRowHeight`.
###### UICollectionView
👩🏼‍🏫 **How specify which elements are skeletonables?**
Here is an illustration that shows how you should specify which elements are skeletonables when you are using an `UITableView`:
![](Assets/tableview_scheme.png)
As you can see, we have to make skeletonable the tableview, the cell and the UI elements, but we don't need to set as skeletonable the `contentView`
#### UICollectionView
For ```UICollectionView```, you need to conform to ```SkeletonCollectionViewDataSource``` protocol.
@@ -396,6 +435,12 @@ Then, when the skeleton appears, you can see the view hierarchy in the Xcode con
### 📚 Documentation
Coming soon...😅
### 📋 Supported OS & SDK Versions
* iOS 9.0+
* tvOS 9.0+
* Swift 4.2
## 📬 Next steps
* [x] Set the filling percent of the last line in multiline elements
@@ -433,6 +478,7 @@ See [all contributors](https://github.com/Juanpe/SkeletonView/graphs/contributor
- [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)
+501
View File
@@ -0,0 +1,501 @@
![](Assets/header2.jpg)
<p align="center">
<a href="https://app.bitrise.io/app/6d289a17e22c8323">
<img src="https://app.bitrise.io/app/6d289a17e22c8323/status.svg?token=fI7gKC41XD9-aRXDScCKBw&branch=master">
</a>
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
<a href="https://github.com/Juanpe/SkeletonView">
<img src="https://img.shields.io/cocoapods/p/SkeletonView.svg" alt="Platforms">
</a>
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
</a>
<a href="https://github.com/Carthage/Carthage">
<img src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat" alt="Carthage" />
</a>
<a href="https://github.com/apple/swift-package-manager">
<img src="https://img.shields.io/badge/SPM-compatible-brightgreen.svg" alt="SPM" />
</a>
<a href="https://twitter.com/JuanpeCatalan">
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
</a>
<br/>
<a href="https://twitter.com/intent/tweet?text=Wow%20This%20library%20is%20awesome:&url=https%3A%2F%2Fgithub.com%2FJuanpe%2FSkeletonView">
<img src="https://img.shields.io/twitter/url/https/github.com/Juanpe/SkeletonView.svg?style=social" alt="License" />
</a>
</p>
🌎 번역에 도움을 주신분들: </br>
[🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) by [@WhatsXie](https://twitter.com/WhatsXie) </br>
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) by [@brunomunizaf](https://twitter.com/brunomuniz_af) </br>
[🇰🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_ko.md) by [@techinpark](https://twitter.com/techinpark)
오늘날 거의 대부분의 앱들은 비동기 방식의 API 호출을 사용하는 프로세스를 가지고 있습니다.
프로세스가 작동하는동안 개발자들은 작업이 실행되고 있다는것을 사용자들에게 보여주기 위해서 로딩 뷰를 배치합니다.
```SkeletonView```는 이러한 필요에 의해 고안되었고, 사용자들에게 무엇인가 로딩이 되고 있다는것을 보여주면서 기다리는 콘텐츠에 대해서도 미리 준비할 수 있게 해주는 우아하게 표현할수 있는 방법입니다
맘껏 누리세요 🙂
* [기능](#-features)
* [가이드](#-guides)
* [설치방법](#-installation)
* [Cocoapods](#using-cocoapods)
* [Carthage](#using-carthage)
* [SPM](#using-swift-package-manager)
* [어떻게 사용하나요?](#-how-to-use)
* [Collections](#-collections)
* [Multiline text](#-multiline-text)
* [Custom colors](#-custom-colors)
* [Appearance](#-appearance)
* [Custom animations](#-custom-animations)
* [Hierarchy](#-hierarchy)
* [Debug](#-debug)
* [문서화](#-documentation)
* [지원되는 OS와 SDK 버전](#-supported-os--sdk-versions)
* [Next steps](#-next-steps)
* [Contributing](#-contributing)
* [Mentions](#-mentions)
* [개발자](#-author)
* [라이센스](#-license)
## 🌟 기능
- [x] 사용이 쉽습니다
- [x] 모든 `UIView`에서 사용가능합니다
- [x] 전체 커스터마이징이 가능합니다
- [x] 공통으로 이용가능합니다 (iPhone & iPad)
- [x] `Interface Builder` 에서 사용 가능합니다.
- [x] 간단한 스위프트 문법
- [x] 가볍고 가독성 좋은 코드
## 🎬 사용가이드
[<img src="Assets/thumb_getting_started.png">](https://youtu.be/75kgOhWsPNA)
## 📲 설치 방법
#### [CocoaPods](https://cocoapods.org) 로 사용하기
당신의 프로젝트 `Podfile` 파일에 아래와 같이 입력합니다:
```ruby
pod "SkeletonView"
```
#### [Carthage](https://github.com/carthage)로 사용하기
당신의 프로젝트 `Cartfile` 파일에 아래와 같이 입력합니다:
```bash
github "Juanpe/SkeletonView"
```
#### [Swift Package Manager](https://github.com/apple/swift-package-manager)로 사용하기
당신의 프로젝트에 Swift package를 설정한다면, `SkeletonView` 를 `Package.swift` 파일에 있는 `dependencies`에 추가하시면 됩니다.
```swift
dependencies: [
.package(url: "https://github.com/Juanpe/SkeletonView.git", from: "1.6")
]
```
## 🐒 어떻게 사용하나요?
`SkeletonView` 를 이용하기 위해서는 딱 **3** 단계만 기억하세요:
**1.** 사용하고자 하는 파일에서 `SkeletonView` 를 `Import` 합니다.
```swift
import SkeletonView
```
**2.** 자, 그렇다면 UIView 속성에 `skeletonables` 를 이용하실 수 있습니다. 두가지 옵션이 있습니다
**코드로 사용하는 방법:**
```swift
avatarImageView.isSkeletonable = true
```
**인터페이스빌더 / 스토리보드를 이용하는 방법:**
![](Assets/storyboard.png)
**3.** 당신이 뷰를 세팅할때, **skeleton** 옵션을 사용 할 수 있습니다. 총 **4** 가지 옵션을 지원합니다:
```swift
(1) view.showSkeleton() // Solid
(2) view.showGradientSkeleton() // Gradient
(3) view.showAnimatedSkeleton() // Solid animated
(4) view.showAnimatedGradientSkeleton() // Gradient animated
```
**미리보기**
<table>
<tr>
<td width="25%">
<center>Solid</center>
</td>
<td width="25%">
<center>Gradient</center>
</td>
<td width="25%">
<center>Solid Animated</center>
</td>
<td width="25%">
<center>Gradient Animated</center>
</td>
</tr>
<tr>
<td width="25%">
<img src="Assets/solid.png"></img>
</td>
<td width="25%">
<img src="Assets/gradient.png"></img>
</td>
<td width="25%">
<img src="Assets/solid_animated.gif"></img>
</td>
<td width="25%">
<img src="Assets/gradient_animated.gif"></img>
</td>
</tr>
</table>
> **중요!**
>>```SkeletonView``` 는 재귀적으로 되어있습니다, 만약 모든 뷰에 대해서 skeleton을 호출하고 싶다면, 메인 컨테이너 뷰에서 show `method`를 호출하여야 합니다. 예를 들자면 UIViewControllers가 있습니다.
### 🌿 Collections
현재, ```SkeletonView``` 는 ```UITableView``` 와 ```UICollectionView```에서 호환됩니다.
#### UITableView
만약 ```UITableView```에서 skeleton을 호출하고 싶다면, ```SkeletonTableViewDataSource``` protocol 을 구현하여야 합니다.
``` swift
public protocol SkeletonTableViewDataSource: UITableViewDataSource {
func numSections(in collectionSkeletonView: UITableView) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
}
```
해당 프로토클은 보시다시피 ```UITableViewDataSource```를 상속받아 구현하였으므로, skeleton의 protocol과 대체 가능합니다.
프로토콜의 기본 구현은 다음과 같습니다:
``` swift
func numSections(in collectionSkeletonView: UITableView) -> Int
// Default: 1
```
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
// Default:
// 전체 테이블 뷰를 채우는데 필요한 셀 수를 계산합니다
```
해당 메소드는 당신이 구현하여야할 cell identifier을 아는 경우에만 사용합니다, 해당 메소드는 기본으로 구현하지 않아도됩니다 :
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
```
**Example**
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "CellIdentifier"
}
```
> **중요!**
> 만약 사이즈가 변하는 셀을 사용한다면 (`tableView.rowHeight = UITableViewAutomaticDimension` ),`estimatedRowHeight`를 무조건 정의해주세요.
👩🏼‍🏫 **어떻게 특정 요소에 skeleton 을 지정할까요?**
아래의 그림은 `UITableView` 에서 특정한 요소에 skeleton 을 지정하는 방법을 보여주는 이미지 입니다:
![](Assets/tableview_scheme.png)
위의 이미지에서 보이듯, 테이블 뷰와 셀에 들어가는 UI 요소들에는 적용을 해야하지만, `contentView`에 skeleton을 적용할 필요는 없습니다.
#### UICollectionView
```UICollectionView``` 에 적용을 하기 위해서는, ```SkeletonCollectionViewDataSource``` protocol 을 구현할 필요가 있습니다.
``` swift
public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
func numSections(in collectionSkeletonView: UICollectionView) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier
}
```
```UITableView``` 와 사용방법은 같습니다.
### 📰 Multiline text
![](Assets/multilines2.png)
텍스트가 들어있는 요소를 사용한다면, ```SkeletonView``` 에서 텍스트의 라인을 그려줍니다.
그리고, 원하는 라인 수를 설정할 수 있습니다. 만약 ```numberOfLines``` 을 0으로 설정한다면, 자동으로 필요한 라인수를 계산해서 그려줍니다. 대신 값이 설정되어있다면 설정된 수만큼의 라인이 그려집니다.
##### 🎛 Customize
당신은 멀티라인을 위해 몇가지 옵션을 설정할 수 있습니다.
| 속성 | 값 | 기본값 | 미리보기 |
| ----------------------------------------------- | --------- | ----- | ---------------------------------- |
| 마지막 라인의 **퍼센트** 를 지정 할 수 있습니다. | `0...100` | `70%` | ![](Assets/multiline_lastline.png) |
| 라인의 **Corner radius** 를 지정할 수 있습니다. (**새로운기능**) | `0...10` | `0` | ![](Assets/multiline_corner.png) |
라인의 radius를 지정하기 위해서는 **코드** 를 이용합니다, 아래 처럼 코드를 작성합니다:
```swift
descriptionTextView.lastLineFillPercent = 50
descriptionTextView.linesCornerRadius = 5
```
혹은 **IB/Storyboard** 를 이용하실 수 있습니다:
![](Assets/multiline_customize.png)
### 🎨 Custom colors
당신은 skeleton의 색상을 지정 할 수 있습니다. 간단하게 원하는 색상을 파라미터로 넘겨주시면 됩니다.
**단색 이용방법**
``` swift
view.showSkeleton(usingColor: UIColor.gray) // Solid
// or
view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0))
```
**그라디언트 이용 방법**
``` swift
let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
view.showGradientSkeleton(usingGradient: gradient) // Gradient
```
게다가, ```SkeletonView``` 에서는 20가지의 기본 컬러를 지원합니다 🤙🏼
```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...```
![](Assets/flatcolors.png)
###### 위 이미지는 [https://flatuicolors.com](https://flatuicolors.com) 사이트에서 발췌했습니다.
### 🦋 Appearance
**새로운 사항** skeleton 은 기본설정 값이 정해져 있습니다. 만약 커스텀 컬러를 사용할 필요가 없다면, `SkeletonView` 에 지정 되어있는 기본설정을 사용하시면 됩니다.
기본 설정값:
- **tintColor**: UIColor
- *기본값: .clouds*
- **gradient**: SkeletonGradient
- *기본값: SkeletonGradient(baseColor: .clouds)*
- **multilineHeight**: CGFloat
- *기본값: 15*
- **multilineSpacing**: CGFloat
- *기본값: 10*
- **multilineLastLineFillPercent**: Int
- *기본값: 70*
- **multilineCornerRadius**: Int
- *기본값: 0*
`SkeletonAppearance.default` 에는 사용 되어지는 기본 값들이 설정되어 있습니다 . 아래의 코드와 같이 사용할 수 있습니다:
```Swift
SkeletonAppearance.default.multilineHeight = 20
SkeletonAppearance.default.tintColor = .green
```
### 🤓 커스텀 애니메이션
```SkeletonView``` 에는 두가지 애니메이션이 내장되어 있습니다, 단색 *바운스* 애니메이션과 그라디언트 *슬라이드* 애니메이션 입니다 .
게다가, 직접 애니메이션을 추가하고 싶다면 정말 간단합니다.
Skeleton 에서는 `showAnimatedSkeleton` 함수를 ```SkeletonLayerAnimation```에 정의하여 맞춤형 애니메이션을 정의할 수 있도록 되어 있습니다.
```swift
public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation
```
함수는 이렇게 호출 가능합니다:
```swift
view.showAnimatedSkeleton { (layer) -> CAAnimation in
let animation = CAAnimation()
// Customize here your animation
return animation
}
```
```SkeletonAnimationBuilder```의 사용이 가능합니다. ```SkeletonLayerAnimation```을 만들기 위해 사용됩니다.
이제, 그라디언트를 위한 **슬라이딩 애니메이션** 을 만들 수 있습니다, 애니메이션을 위한 **방향** 과 **지속시간** 을 설정 할 수 있습니다. (기본값 = 1.5초).
```swift
// func makeSlidingAnimation(withDirection direction: GradientDirection, duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation
let animation = SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: .leftToRight)
view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
```
```GradientDirection``` 는 enum 으로 정의 되어있습니다., 아래의 케이스를 참조하세요:
| 방향 | 미리보기 |
| ------------------- | ---------------------------------------------- |
| .leftRight | ![](Assets/sliding_left_to_right.gif) |
| .rightLeft | ![](Assets/sliding_right_to_left.gif) |
| .topBottom | ![](Assets/sliding_top_to_bottom.gif) |
| .bottomTop | ![](Assets/sliding_bottom_to_top.gif) |
| .topLeftBottomRight | ![](Assets/sliding_topLeft_to_bottomRight.gif) |
| .bottomRightTopLeft | ![](Assets/sliding_bottomRight_to_topLeft.gif) |
> **😉 꿀팁!**
슬라이딩 애니메이션을 만들기 위한 또다른 방법이 있습니다, 아래의 코드를 참조하세요:
>>```let animation = GradientDirection.leftToRight.slidingAnimation()```
### 👨‍👧‍👦 계층 구조
```SkeletonView```는 재귀적입니다 , 그리고 우리는 skeleton이 효율적으로 작동하기를 원하기 때문에, 가능한 빨리 재귀작업을 중단하기를 원합니다. 이러한 이유때문에 반드시 컨테이너 뷰를 `Skeletonable` 로 설정해야 합니다, `skeletonable` 되지 않는 뷰를 만나는 순간 재귀 작업을 중단하기 떄문입니다.
아래의 이미지를 참고하세요 이미지는 한눈에 이해되실겁니다:
> ```ìsSkeletonable```= ☠️
| 설정값 | 결과 |
| ----------------------------------------- | --------------------------------------------- |
| ![](Assets/no_skeletonable.png) | ![](Assets/no_skeletonables_result.png) |
| ![](Assets/container_no_skeletonable.png) | ![](Assets/no_skeletonables_result.png) |
| ![](Assets/container_skeletonable.png) | ![](Assets/container_skeletonable_result.png) |
| ![](Assets/all_skeletonables.png) | ![](Assets/all_skeletonables_result.png) |
### 🔬 디버그
**새로운소식** 어떤것들이 잘 동작 하지 않을때를 위해 디버그 작업을 용이하게 하기 위해서 `SkeletonView` 에는 몇가지 새로운 것들이 있습니다.
첫번쨰로, `UIView` 에서 skeleton 정보를 보기위해 다음과 같이 지원하고 있습니다:
```swift
var skeletonDescription: String
```
skeleton은 이렇게 생겼습니다:
![](Assets/debug_description.png)
그리고, 새로운 **디버그 모드**를 활성화 시킬 수 있습니다. 간단하게 `SKELETON_DEBUG` 이라는 환경 변수를 추가해 활성화 하면 됩니다.
![](Assets/debug_mode.png)
그런 이후 skeleton이 나오면 Xcode 콘솔창에서 계층 구조를 볼 수 있습니다.
<details>
<summary>예제를 확인해보세요. </summary>
<img src="Assets/hierarchy_output.png" />
</details>
### 📚 문서화
조금만 기다려주세요...😅
### 📋 지원 가능한 OS & SDK 버전
* iOS 9.0+
* tvOS 9.0+
* Swift 4.2
## 📬 예정된 기능들
* [x] 멀티라인 에서의 마지막 라인의 채우기 비율 설정
* [x] 더많은 그라디언트 애니메이션
* [x] resizable cells 지원
* [x] CollectionView 호환
* [x] tvOS 호환
* [x] recovery state 추가
* [x] Custom default appearance
* [x] 디버그 모드
* [ ] Custom collections 호환
* [ ] skeletons 가 보이거나 가려질때 애니메이션 추가
* [ ] MacOS 와 WatchOS 호환
## ❤️ 기여하기
이 프로젝트는 오픈소스 프로젝트 입니다, 마음편하게 기여해주시면 됩니다 어떻게 하냐구요?
- 새로운 [이슈](https://github.com/Juanpe/SkeletonView/issues/new)를 등록합니다.
- [email](mailto://juanpecatalan.com)을 보냅니다.
- 당신의 수정을 제안합니다, pull request를 포함한 수정을 권장합니다.
전체 [기여자목록](https://github.com/Juanpe/SkeletonView/graphs/contributors)
###### [SwiftPlate](https://github.com/JohnSundell/SwiftPlate)를 통해 프로젝트가 생성되었습니다
## 📢 소식들
- [iOS Dev Weekly #327](https://iosdevweekly.com/issues/327#start)
- [Hacking with Swift Articles](https://www.hackingwithswift.com/articles/40/skeletonview-makes-loading-content-beautiful)
- [Top 10 Swift Articles November](https://medium.mybridge.co/swift-top-10-articles-for-the-past-month-v-nov-2017-dfed7861cd65)
- [30 Amazing iOS Swift Libraries (v2018)](https://medium.mybridge.co/30-amazing-ios-swift-libraries-for-the-past-year-v-2018-7cf15027eee9)
- [AppCoda Weekly #44](http://digest.appcoda.com/issues/appcoda-weekly-issue-44-81899)
- [iOS Cookies Newsletter #103](https://us11.campaign-archive.com/?u=cd1f3ed33c6527331d82107ba&id=48131a516d)
- [Swift Developments Newsletter #113](https://andybargh.com/swiftdevelopments-113/)
- [iOS Goodies #204](http://ios-goodies.com/post/167557280951/week-204)
- [Swift Weekly #96](http://digest.swiftweekly.com/issues/swift-weekly-issue-96-81759)
- [CocoaControls](https://www.cocoacontrols.com/controls/skeletonview)
- [Awesome iOS Newsletter #74](https://ios.libhunt.com/newsletter/74)
- [Swift News #36](https://www.youtube.com/watch?v=mAGpsQiy6so)
## 👨🏻‍💻 개발자
[1.1]: http://i.imgur.com/tXSoThF.png
[1]: http://www.twitter.com/JuanpeCatalan
* Juanpe Catalán [![alt text][1.1]][1]
<a class="bmc-button" target="_blank" href="https://www.buymeacoffee.com/CDou4xtIK"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy me a coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;"><span style="margin-left:5px"></span></a>
## 👮🏻 라이센스
```
MIT License
Copyright (c) 2017 Juanpe Catalán
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
+442
View File
@@ -0,0 +1,442 @@
![](Assets/header2.jpg)
<p align="center">
<a href="https://app.bitrise.io/app/6d289a17e22c8323">
<img src="https://app.bitrise.io/app/6d289a17e22c8323/status.svg?token=fI7gKC41XD9-aRXDScCKBw&branch=master">
</a>
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
<a href="https://github.com/Juanpe/SkeletonView">
<img src="https://img.shields.io/cocoapods/p/SkeletonView.svg" alt="Platforms">
</a>
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
</a>
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/dt/SkeletonView.svg?style=flat" alt="CocoaPods downloads" />
</a>
<a href="https://twitter.com/JuanpeCatalan">
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
</a>
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">
<img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Paypal" />
</a>
<br/>
<a href="https://twitter.com/intent/tweet?text=Wow%20This%20library%20is%20awesome:&url=https%3A%2F%2Fgithub.com%2FJuanpe%2FSkeletonView">
<img src="https://img.shields.io/twitter/url/https/github.com/Juanpe/SkeletonView.svg?style=social" alt="License" />
</a>
</p>
🌎 Traduções: </br>
[Original](https://github.com/Juanpe/SkeletonView) </br>
[🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) por [@WhatsXie](https://twitter.com/WhatsXie) </br>
Hoje, quase todos os apps têm processos assíncronos, como requisições de API, processos longos, etc. E enquanto os processos estão ocorrendo, normalmente os desenvolvedores usam uma view que mostra os usuarios que algo está ocorrendo.
```SkeletonView``` foi criado para essa necessidade, um jeito elegante de mostrar aos usuários que algo está acontecendo e já prepará-los para qual conteúdo será carregado.
Aproveite! 🙂
* [Features](#-features)
* [Requerimentos](#-supported-os--sdk-versions)
* [Projeto de exemplo](#-example)
* [Instalação](#-installation)
* [Cocoapods](#using-cocoapods)
* [Carthage](#using-carthage)
* [Como usar](#-how-to-use)
* [Coleções](#-collections)
* [Texto em várias linhas](#-multiline-text)
* [Cores customizadas](#-custom-colors)
* [Aparência](#-appearance)
* [Animaçōes customizadas](#-custom-animations)
* [Hierarquia](#-hierarchy)
* [Documentação](#-documentation)
* [Próximos passos](#-next-steps)
* [Contribuindo](#-contributing)
* [Menções](#-mentions)
* [Autor](#-author)
* [Licença](#-license)
## 🌟 Features
- [x] Fácil de usar
- [x] Todas as UIViews são skeletonables
- [x] Completamente customizável
- [x] Universal (iPhone & iPad)
- [x] Interface Builder friendly
- [x] Sintaxe simples em Swift
- [x] Código leve e legível
### 📋 Versões do SDK e OS suportados
* iOS 9.0+
* tvOS 9.0+
* Swift 4.2
### 🔮 Exemplo
Para rodar o projeto de exemplo, clone o repositório e use o target `SkeletonViewExample`.
## 📲 Instalação
#### Usando [CocoaPods](https://cocoapods.org)
Edite seu `Podfile` e especifíque a dependência:
```ruby
pod "SkeletonView"
```
#### Usando [Carthage](https://github.com/carthage)
Edite seu `Cartfile` e especifíque a dependência:
```bash
github "Juanpe/SkeletonView"
```
## 🐒 Como usar
Apenas **3** passos necessários para usar `SkeletonView`:
**1.** Importe SkeletonView no lugar desejado.
```swift
import SkeletonView
```
**2.** Agora, especifíque quais views serão `skeletonables`. Você consegue fazer isso de duas formas:
**Usando código:**
```swift
avatarImageView.isSkeletonable = true
```
**Usando IB/Storyboards:**
![](Assets/storyboard.png)
**3.** Uma vez que você setou as views, você pode mostrar o **skeleton**. Para fazê-lo, você tem **4** escolhas:
```swift
(1) view.showSkeleton() // Solid
(2) view.showGradientSkeleton() // Gradient
(3) view.showAnimatedSkeleton() // Solid animated
(4) view.showAnimatedGradientSkeleton() // Gradient animated
```
**Pré-visualização**
<table>
<tr>
<td width="25%">
<center>Solid</center>
</td>
<td width="25%">
<center>Gradient</center>
</td>
<td width="25%">
<center>Solid Animated</center>
</td>
<td width="25%">
<center>Gradient Animated</center>
</td>
</tr>
<tr>
<td width="25%">
<img src="Assets/solid.png"></img>
</td>
<td width="25%">
<img src="Assets/gradient.png"></img>
</td>
<td width="25%">
<img src="Assets/solid_animated.gif"></img>
</td>
<td width="25%">
<img src="Assets/gradient_animated.gif"></img>
</td>
</tr>
</table>
> **IMPORTANTE!**
>>```SkeletonView``` é recursivo, então se você quer mostrar o esqueleto em todas as views skeletonables, você só precisa chamar o método na main container view. Por exemplo, com UIViewControllers
### 🌿 Coleções
```SkeletonView``` é compatível com ```UITableView``` e ```UICollectionView```.
###### UITableView
Se você quer mostrar o skeleton em uma ```UITableView```, você precisa conformar com o protocolo ```SkeletonTableViewDataSource```.
``` swift
public protocol SkeletonTableViewDataSource: UITableViewDataSource {
func numSections(in collectionSkeletonView: UITableView) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
}
```
Como você pode ver, esse protocolo herda de ```UITableViewDataSource```, então você pode substituir esse protocolo com o protocolo do skeleton.
Esse protocolo tem uma implementação padrão:
``` swift
func numSections(in collectionSkeletonView: UITableView) -> Int
// Default: 1
```
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
// Default:
// It calculates how many cells need to populate whole tableview
```
Esse é o único método que você precisa implementar para informar o skeleton sobre o cell identifier. Esse método não possui uma implementação padrão:
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
```
**Exemplo**
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "CellIdentifier"
}
```
> **IMPORTANTE!**
> Se você está usando resizable cells (`tableView.rowHeight = UITableViewAutomaticDimension` ), é obrigatório definir a `estimatedRowHeight`.
###### UICollectionView
Para ```UICollectionView```, você precisa conformar com o protocolo ```SkeletonCollectionViewDataSource```.
``` swift
public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
func numSections(in collectionSkeletonView: UICollectionView) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier
}
```
O resto do processo é o mesmo da ```UITableView```
### 📰 Texto de várias linhas
![](Assets/multilines2.png)
Quando você usar elementos com texto, ```SkeletonView``` desenha linhas para simular o texto.
Além disso, você pode decidir quantas linhas você quer. Se ```numberOfLines``` está setado para zero (0), haverá um cálculo para saber quantas linhas são necessárias para preencher o skeleton inteiro e será desenhado. Caso contrário, se você setar para um (1) ou qualquer outro número maior que zero, só serão desenhadas aquele número de linhas.
##### 🎛 Customização
Você pode setar algumas propriedades para elementos de várias linhas.
| Property | Values | Default | Preview
| ------- | ------- |------- | -------
| **Filling percent** of the last line. | `0...100` | `70%` | ![](Assets/multiline_lastline.png)
| **Corner radius** of lines. (**NEW**) | `0...10` | `0` | ![](Assets/multiline_corner.png)
Para modificar a percentagem ou o raio **usando código**, especifique as propriedades:
```swift
descriptionTextView.lastLineFillPercent = 50
descriptionTextView.linesCornerRadius = 5
```
Ou, se você preferir use **IB/Storyboard**:
![](Assets/multiline_customize.png)
### 🎨 Cores customizadas
Você pode decidir que cor o skeleton esta pintado. Você só precisa parametrizar a cor e o gradiente que deseja.
**Usando cores sólidas**
``` swift
view.showSkeleton(usingColor: UIColor.gray) // Solid
// or
view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0))
```
**Usando gradientes**
``` swift
let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
view.showGradientSkeleton(usingGradient: gradient) // Gradient
```
Além do mais, ```SkeletonView``` tem 20 cores flat 🤙🏼
```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...```
![](Assets/flatcolors.png)
###### Imagem capturada do site [https://flatuicolors.com](https://flatuicolors.com)
### 🦋 Aparência
**NOVIDADE** Os skeletons tem uma aparência padrão. Então, quando você não especifíca a cor, gradiente ou propriedades de várias linhas, `SkeletonView` usa os valores padrões.
Valores padrões:
- **tintColor**: UIColor
- *default: .clouds*
- **gradient**: SkeletonGradient
- *default: SkeletonGradient(baseColor: .clouds)*
- **multilineHeight**: CGFloat
- *default: 15*
- **multilineSpacing**: CGFloat
- *default: 10*
- **multilineLastLineFillPercent**: Int
- *default: 70*
- **multilineCornerRadius**: Int
- *default: 0*
Para obter esses valores padrões você pode usar `SkeletonAppearance.default`. Usando essa propriedade você pode declarar os valores também:
```Swift
SkeletonAppearance.default.multilineHeight = 20
SkeletonAppearance.default.tintColor = .green
```
### 🤓 Animações customizadas
```SkeletonView``` tem duas animações pré-definidas, *pulse* para skeletons solidos e *sliding* para gradientes.
Além disso, se você quiser fazer suas próprias animações no skeleton, é muito fácil.
Skeleton disponibiliza a função `showAnimatedSkeleton` que tem o closure ```SkeletonLayerAnimation``` onde você pode definir sua animação customizada.
```swift
public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation
```
Você pode chamar esta função assim:
```swift
view.showAnimatedSkeleton { (layer) -> CAAnimation in
let animation = CAAnimation()
// Customize here your animation
return animation
}
```
Está disponível ```SkeletonAnimationBuilder```. É um construtor para ```SkeletonLayerAnimation```.
Hoje, você pode criar **sliding animations** para gradientes, decidindo a **direction** e setando a **duration** da animaçāo (padrão = 1.5s).
```swift
// func makeSlidingAnimation(withDirection direction: GradientDirection, duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation
let animation = SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: .leftToRight)
view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
```
```GradientDirection``` é um enum, com os seguintes cases:
| Direction | Preview
|------- | -------
| .leftRight | ![](Assets/sliding_left_to_right.gif)
| .rightLeft | ![](Assets/sliding_right_to_left.gif)
| .topBottom | ![](Assets/sliding_top_to_bottom.gif)
| .bottomTop | ![](Assets/sliding_bottom_to_top.gif)
| .topLeftBottomRight | ![](Assets/sliding_topLeft_to_bottomRight.gif)
| .bottomRightTopLeft | ![](Assets/sliding_bottomRight_to_topLeft.gif)
> **😉 TRUQUE!**
Existe outra forma de criar sliding animations, apenas usando este atalho:
>>```let animation = GradientDirection.leftToRight.slidingAnimation()```
### 👨‍👧‍👦 Hierarquia
Já que ```SkeletonView``` é recursiva, e queremos que o skeleton seja muito eficiente, queremos parar a recursão assim que possível. Por este motivo, você deve setar a container view como `Skeletonable`, porque o Skeleton vai parar de procurar por subviews `skeletonable` assim que a view não for mais skeletonable, quebrando a recursão.
Porque uma imagem vale mais que mil palavras:
> ```ìsSkeletonable```= ☠️
| Configuration | Result
|------- | -------
|![](Assets/no_skeletonable.png) | ![](Assets/no_skeletonables_result.png)
|![](Assets/container_no_skeletonable.png) | ![](Assets/no_skeletonables_result.png)
|![](Assets/container_skeletonable.png) | ![](Assets/container_skeletonable_result.png)
|![](Assets/all_skeletonables.png) | ![](Assets/all_skeletonables_result.png)
### 📚 Documentação
Em breve...😅
## 📬 Próximos passos
* [x] Setar o percentual de preenchimento da última linha em elementos de várias linhas
* [x] Adicionar mais animações de gradiente
* [x] Suporte para resizable cells
* [x] Compatível com CollectionView
* [x] Compatível com tvOS
* [x] Adicionar recovery state
* [x] Aparência padrão customizável
* [ ] Compatível com coleções customizáveis
* [ ] Adicionar animações quando mostra/esconde os skeletons
* [ ] Compatível com MacOS e WatchOS
## ❤️ Contribuindo
Este é um projeto de código aberto, então sinta-se a vontade para contribuir. Como?
- Abra uma [issue](https://github.com/Juanpe/SkeletonView/issues/new).
- Envie feedback por [email](mailto://juanpecatalan.com).
- Proponha seus próprios fixes, sugestões e abra um pull request com as alterações.
Ver [todos os contribuidores](https://github.com/Juanpe/SkeletonView/graphs/contributors)
###### Projeto gerado com [SwiftPlate](https://github.com/JohnSundell/SwiftPlate)
## 📢 Menções
- [iOS Dev Weekly #327](https://iosdevweekly.com/issues/327#start)
- [Hacking with Swift Articles](https://www.hackingwithswift.com/articles/40/skeletonview-makes-loading-content-beautiful)
- [Top 10 Swift Articles November](https://medium.mybridge.co/swift-top-10-articles-for-the-past-month-v-nov-2017-dfed7861cd65)
- [30 Amazing iOS Swift Libraries (v2018)](https://medium.mybridge.co/30-amazing-ios-swift-libraries-for-the-past-year-v-2018-7cf15027eee9)
- [AppCoda Weekly #44](http://digest.appcoda.com/issues/appcoda-weekly-issue-44-81899)
- [iOS Cookies Newsletter #103](https://us11.campaign-archive.com/?u=cd1f3ed33c6527331d82107ba&id=48131a516d)
- [Swift Developments Newsletter #113](https://andybargh.com/swiftdevelopments-113/)
- [iOS Goodies #204](http://ios-goodies.com/post/167557280951/week-204)
- [Swift Weekly #96](http://digest.swiftweekly.com/issues/swift-weekly-issue-96-81759)
- [CocoaControls](https://www.cocoacontrols.com/controls/skeletonview)
- [Awesome iOS Newsletter #74](https://ios.libhunt.com/newsletter/74)
## 👨🏻‍💻 Autor
[1.1]: http://i.imgur.com/tXSoThF.png
[1]: http://www.twitter.com/JuanpeCatalan
* Juanpe Catalán [![alt text][1.1]][1]
<a class="bmc-button" target="_blank" href="https://www.buymeacoffee.com/CDou4xtIK"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy me a coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;"><span style="margin-left:5px"></span></a>
## 👮🏻 Licença
```
MIT License
Copyright (c) 2017 Juanpe Catalán
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
+13 -14
View File
@@ -1,19 +1,17 @@
![](Assets/header2.jpg)
<p align="center">
<a href="https://travis-ci.org/Juanpe/SkeletonView">
<img src="https://img.shields.io/travis/Juanpe/SkeletonView.svg">
<a href="https://app.bitrise.io/app/6d289a17e22c8323">
<img src="https://app.bitrise.io/app/6d289a17e22c8323/status.svg?token=fI7gKC41XD9-aRXDScCKBw&branch=master">
</a>
<a href="https://instagram.github.io/IGListKit/">
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
<a href="https://github.com/Juanpe/SkeletonView">
<img src="https://img.shields.io/cocoapods/p/SkeletonView.svg" alt="Platforms">
</a>
<img src="https://img.shields.io/badge/Swift-4.1-orange.svg" />
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
</a>
<a href="https://github.com/Carthage/Carthage">
<img src="https://img.shields.io/badge/carthage-compatible-4BC51D.svg?style=flat" alt="Carthage" />
</a>
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/dt/SkeletonView.svg?style=flat" alt="CocoaPods downloads" />
</a>
@@ -23,15 +21,16 @@
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">
<img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Paypal" />
</a>
<br/>
<a href="https://twitter.com/intent/tweet?text=Wow%20This%20library%20is%20awesome:&url=https%3A%2F%2Fgithub.com%2FJuanpe%2FSkeletonView">
<img src="https://img.shields.io/twitter/url/https/github.com/Juanpe/SkeletonView.svg?style=social" alt="License" />
</a>
<a href="https://twitter.com/JuanpeCatalan">
<img src="https://img.shields.io/twitter/follow/JuanpeCatalan.svg?style=social&label=Follow" alt="Twitter" />
</a>
</p>
🌎 翻译: [ [原版的](https://github.com/Juanpe/SkeletonView) ]
🌎 翻译: [ [原版的](https://github.com/Juanpe/SkeletonView) ] </br>
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) [@brunomunizaf](https://twitter.com/brunomuniz_af)
今天,几乎所有的应用程序都有异步流程,例如:Api请求、长时间运行的流程等。虽然流程正在运行,但通常开发人员会设置一个加载视图来向用户显示正在发生的事情。
@@ -71,9 +70,9 @@
### 📋 版本要求
* iOS 9.0+
* tvOS 9.0+
* Swift 4
* iOS 10.0+
* tvOS 10.0+
* Swift 4.2
### 🔮 示例
+2 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SkeletonView"
s.version = "1.4"
s.version = "1.7"
s.summary = "An elegant way to show users that something is happening and also prepare them to which contents he is waiting"
s.description = <<-DESC
Today almost all apps have async processes, as API requests, long runing processes, etc. And while the processes are working, usually developers place a loading view to show users that something is going on.
@@ -12,6 +12,7 @@ Pod::Spec.new do |s|
s.social_media_url = "https://twitter.com/JuanpeCatalan"
s.ios.deployment_target = "9.0"
s.tvos.deployment_target = "9.0"
s.swift_version = "5.0"
s.source = { :git => "https://github.com/Juanpe/SkeletonView.git", :tag => s.version.to_s }
s.source_files = "Sources/**/*"
end
+104 -49
View File
@@ -29,7 +29,6 @@
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 */; };
3B83EE4720C41488005178A4 /* ContainsMultilineText.swift in Headers */ = {isa = PBXBuildFile; fileRef = 3B83EE4520C41488005178A4 /* ContainsMultilineText.swift */; settings = {ATTRIBUTES = (Public, ); }; };
3B83EE4820C41488005178A4 /* UIView+IBInspectable.swift in Headers */ = {isa = PBXBuildFile; fileRef = 3B83EE4620C41488005178A4 /* UIView+IBInspectable.swift */; settings = {ATTRIBUTES = (Public, ); }; };
42ABD063210B548200BEEFF4 /* SkeletonView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* SkeletonView.framework */; };
42ABD069210B548200BEEFF4 /* SkeletonView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* SkeletonView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -38,10 +37,21 @@
42ABD07A210B54E200BEEFF4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD072210B54E100BEEFF4 /* Assets.xcassets */; };
42ABD07B210B54E200BEEFF4 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD073210B54E100BEEFF4 /* ViewController.swift */; };
42ABD07C210B54E200BEEFF4 /* Base.lproj in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD074210B54E100BEEFF4 /* Base.lproj */; };
42ABD07E210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD076210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist */; };
42ABD07F210B54E200BEEFF4 /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */; };
870F4E4321CAC07300B9233B /* SkeletonConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 870F4E4221CAC07300B9233B /* SkeletonConfig.swift */; };
870F4E4421CAC07300B9233B /* SkeletonConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 870F4E4221CAC07300B9233B /* SkeletonConfig.swift */; };
872D5A5621C177E20037D763 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5521C177E20037D763 /* UIView+Extension.swift */; };
872D5A5721C177E20037D763 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5521C177E20037D763 /* UIView+Extension.swift */; };
872D5A5C21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */; };
872D5A5D21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */; };
872D5A5F21C24F8E0037D763 /* UITextView+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */; };
872D5A6021C24F8E0037D763 /* UITextView+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */; };
8748240D20C6A88A00E92179 /* UIView+IBInspectable.swift in Headers */ = {isa = PBXBuildFile; fileRef = F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */; settings = {ATTRIBUTES = (Public, ); }; };
8748240E20C6A88E00E92179 /* ContainsMultilineText.swift in Headers */ = {isa = PBXBuildFile; fileRef = F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */; settings = {ATTRIBUTES = (Public, ); }; };
877EFA4521BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */; };
877EFA4621BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */; };
877EFA4821BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */; };
877EFA4921BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */; };
8785E3A1211C9C9800CC9DFD /* SkeletonDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */; };
8785E3A2211C9CA500CC9DFD /* SkeletonDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */; };
8785E3A3211C9CE800CC9DFD /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88DEA97D1FCDBD1F006C80EF /* Constants.swift */; };
@@ -138,7 +148,6 @@
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>"; };
3B83EE4520C41488005178A4 /* ContainsMultilineText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ContainsMultilineText.swift; path = Sources/Helpers/ContainsMultilineText.swift; sourceTree = "<group>"; };
3B83EE4620C41488005178A4 /* UIView+IBInspectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "UIView+IBInspectable.swift"; path = "Sources/Extensions/UIView+IBInspectable.swift"; sourceTree = "<group>"; };
42ABD06D210B548200BEEFF4 /* SkeletonViewExampleUICollectionView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SkeletonViewExampleUICollectionView.app; sourceTree = BUILT_PRODUCTS_DIR; };
42ABD070210B54E100BEEFF4 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -149,6 +158,12 @@
42ABD076210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SkeletonViewExampleCollectionview-Info.plist"; sourceTree = "<group>"; };
42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCell.swift; sourceTree = "<group>"; };
52D6D97C1BEFF229002C0205 /* SkeletonView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SkeletonView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
870F4E4221CAC07300B9233B /* SkeletonConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonConfig.swift; sourceTree = "<group>"; };
872D5A5521C177E20037D763 /* UIView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = "<group>"; };
872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Multiline.swift"; sourceTree = "<group>"; };
872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+Multiline.swift"; sourceTree = "<group>"; };
877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonLayerBuilder.swift; sourceTree = "<group>"; };
877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonMultilineLayerBuilder.swift; sourceTree = "<group>"; };
8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonDebug.swift; sourceTree = "<group>"; };
88DEA97D1FCDBD1F006C80EF /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
8933C7841EB5B820000D00A4 /* SkeletonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkeletonView.swift; sourceTree = "<group>"; };
@@ -224,25 +239,10 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
42ABD06F210B54BC00BEEFF4 /* Example UICollectionView */ = {
isa = PBXGroup;
children = (
42ABD070210B54E100BEEFF4 /* AppDelegate.swift */,
42ABD072210B54E100BEEFF4 /* Assets.xcassets */,
42ABD074210B54E100BEEFF4 /* Base.lproj */,
42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */,
42ABD071210B54E100BEEFF4 /* Main.storyboard */,
42ABD073210B54E100BEEFF4 /* ViewController.swift */,
);
path = "Example UICollectionView";
sourceTree = "<group>";
};
52D6D9721BEFF229002C0205 = {
isa = PBXGroup;
children = (
3B83EE4520C41488005178A4 /* ContainsMultilineText.swift */,
3B83EE4620C41488005178A4 /* UIView+IBInspectable.swift */,
42ABD06F210B54BC00BEEFF4 /* Example UICollectionView */,
F5F899F31FABA607002E8FDA /* Example */,
8933C7811EB5B7E0000D00A4 /* Sources */,
52D6D99C1BEFF38C002C0205 /* Configs */,
@@ -271,6 +271,52 @@
path = Configs;
sourceTree = "<group>";
};
872D5A5821C17D850037D763 /* CollectionView */ = {
isa = PBXGroup;
children = (
42ABD070210B54E100BEEFF4 /* AppDelegate.swift */,
42ABD072210B54E100BEEFF4 /* Assets.xcassets */,
42ABD074210B54E100BEEFF4 /* Base.lproj */,
42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */,
42ABD071210B54E100BEEFF4 /* Main.storyboard */,
42ABD073210B54E100BEEFF4 /* ViewController.swift */,
);
path = CollectionView;
sourceTree = "<group>";
};
872D5A5921C17D980037D763 /* TableView */ = {
isa = PBXGroup;
children = (
F5F899F41FABA607002E8FDA /* AppDelegate.swift */,
88DEA97D1FCDBD1F006C80EF /* Constants.swift */,
F5F899F61FABA607002E8FDA /* ViewController.swift */,
F5F622441FACA338007C062A /* Cell.swift */,
F5F899F81FABA607002E8FDA /* Main.storyboard */,
F5F899FB1FABA607002E8FDA /* Assets.xcassets */,
F5F899FD1FABA607002E8FDA /* LaunchScreen.storyboard */,
);
path = TableView;
sourceTree = "<group>";
};
872D5A5A21C24E7C0037D763 /* Multilines */ = {
isa = PBXGroup;
children = (
F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */,
872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */,
872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */,
);
path = Multilines;
sourceTree = "<group>";
};
877EFA4321BEE9C40031FC00 /* Builders */ = {
isa = PBXGroup;
children = (
877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */,
877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */,
);
path = Builders;
sourceTree = "<group>";
};
8785E39E211C9C6D00CC9DFD /* Appearance */ = {
isa = PBXGroup;
children = (
@@ -290,6 +336,8 @@
8933C7811EB5B7E0000D00A4 /* Sources */ = {
isa = PBXGroup;
children = (
872D5A5A21C24E7C0037D763 /* Multilines */,
877EFA4321BEE9C40031FC00 /* Builders */,
8785E39F211C9C7C00CC9DFD /* Debug */,
8785E39E211C9C6D00CC9DFD /* Appearance */,
F58A6E7020A8C87100612494 /* Recoverable */,
@@ -302,6 +350,7 @@
F5F899E81FAB9D2B002E8FDA /* SkeletonLayer.swift */,
8933C7841EB5B820000D00A4 /* SkeletonView.swift */,
F5D3FB0A209DCAA300003FCF /* SubviewsSkeletonables.swift */,
870F4E4221CAC07300B9233B /* SkeletonConfig.swift */,
);
path = Sources;
sourceTree = "<group>";
@@ -363,6 +412,7 @@
F5F622401FAC6E31007C062A /* UIColor+Skeleton.swift */,
F5307E2D1FB0E5E400EE67C5 /* UIView+Frame.swift */,
F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */,
872D5A5521C177E20037D763 /* UIView+Extension.swift */,
F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */,
F5A5E8B3211DCE7000FD20D0 /* Int+Whitespaces.swift */,
);
@@ -373,7 +423,6 @@
isa = PBXGroup;
children = (
F5F899D11FAB9630002E8FDA /* AssociationPolicy.swift */,
F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */,
F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */,
F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */,
);
@@ -392,13 +441,8 @@
F5F899F31FABA607002E8FDA /* Example */ = {
isa = PBXGroup;
children = (
F5F899F41FABA607002E8FDA /* AppDelegate.swift */,
88DEA97D1FCDBD1F006C80EF /* Constants.swift */,
F5F899F61FABA607002E8FDA /* ViewController.swift */,
F5F622441FACA338007C062A /* Cell.swift */,
F5F899F81FABA607002E8FDA /* Main.storyboard */,
F5F899FB1FABA607002E8FDA /* Assets.xcassets */,
F5F899FD1FABA607002E8FDA /* LaunchScreen.storyboard */,
872D5A5921C17D980037D763 /* TableView */,
872D5A5821C17D850037D763 /* CollectionView */,
);
path = Example;
sourceTree = "<group>";
@@ -435,7 +479,6 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
3B83EE4720C41488005178A4 /* ContainsMultilineText.swift in Headers */,
3B83EE4820C41488005178A4 /* UIView+IBInspectable.swift in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -524,7 +567,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0910;
LastUpgradeCheck = 0930;
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = SkeletonView;
TargetAttributes = {
17DD0DFF207FB27400C56334 = {
@@ -532,24 +575,23 @@
ProvisioningStyle = Automatic;
};
42ABD05A210B548200BEEFF4 = {
DevelopmentTeam = KWEMDK92F4;
LastSwiftMigration = 0940;
ProvisioningStyle = Automatic;
};
52D6D97B1BEFF229002C0205 = {
CreatedOnToolsVersion = 7.1;
LastSwiftMigration = 0800;
LastSwiftMigration = 1000;
};
F5F899F11FABA607002E8FDA = {
CreatedOnToolsVersion = 9.1;
DevelopmentTeam = KWEMDK92F4;
LastSwiftMigration = 1000;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "SkeletonView" */;
compatibilityVersion = "Xcode 6.3";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
@@ -580,7 +622,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
42ABD07E210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist in Resources */,
42ABD07A210B54E200BEEFF4 /* Assets.xcassets in Resources */,
42ABD07C210B54E200BEEFF4 /* Base.lproj in Resources */,
42ABD079210B54E200BEEFF4 /* Main.storyboard in Resources */,
@@ -611,7 +652,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
872D5A5721C177E20037D763 /* UIView+Extension.swift in Sources */,
17DD0E1F207FB32100C56334 /* RecursiveProtocol.swift in Sources */,
872D5A5D21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */,
17DD0E1E207FB32100C56334 /* PrepareForSkeletonProtocol.swift in Sources */,
F51ED28520973CC9008B2434 /* SkeletonReusableCell.swift in Sources */,
17DD0E17207FB28F00C56334 /* SkeletonLayer.swift in Sources */,
@@ -621,6 +664,7 @@
17DD0E0B207FB28900C56334 /* SkeletonTableViewProtocols.swift in Sources */,
17DD0E0D207FB28900C56334 /* UITableView+CollectionSkeleton.swift in Sources */,
17DD0E0E207FB28900C56334 /* UIView+CollectionSkeleton.swift in Sources */,
877EFA4921BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */,
F51ED28320973CBB008B2434 /* SkeletonCollectionDelegate.swift in Sources */,
F58A6E6F20A8C66300612494 /* Recoverable.swift in Sources */,
17DD0E18207FB28F00C56334 /* SkeletonView.swift in Sources */,
@@ -633,11 +677,14 @@
17DD0E14207FB28F00C56334 /* SkeletonAnimationBuilder.swift in Sources */,
17DD0E11207FB28C00C56334 /* UIView+Frame.swift in Sources */,
17DD0E15207FB28F00C56334 /* SkeletonAppearance.swift in Sources */,
872D5A6021C24F8E0037D763 /* UITextView+Multiline.swift in Sources */,
17DD0E10207FB28C00C56334 /* UIColor+Skeleton.swift in Sources */,
F51ED28420973CC6008B2434 /* GenericCollectionView.swift in Sources */,
17DD0E0A207FB28900C56334 /* SkeletonCollectionViewProtocols.swift in Sources */,
17DD0E1C207FB32100C56334 /* AssociationPolicy.swift in Sources */,
877EFA4621BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */,
F5A5E8B5211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */,
870F4E4421CAC07300B9233B /* SkeletonConfig.swift in Sources */,
17DD0E1D207FB32100C56334 /* ContainsMultilineText.swift in Sources */,
17DD0E0F207FB28C00C56334 /* CALayer+Extensions.swift in Sources */,
17DD0E16207FB28F00C56334 /* SkeletonGradient.swift in Sources */,
@@ -659,7 +706,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
872D5A5621C177E20037D763 /* UIView+Extension.swift in Sources */,
F5F899D01FAA6A4D002E8FDA /* UIView+IBInspectable.swift in Sources */,
872D5A5C21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */,
F54CF5E42024CEB000330B0D /* UIView+CollectionSkeleton.swift in Sources */,
F5307E371FB1076E00EE67C5 /* SkeletonTableViewProtocols.swift in Sources */,
8933C7851EB5B820000D00A4 /* SkeletonView.swift in Sources */,
@@ -669,6 +718,7 @@
F54CF5E52024CEB000330B0D /* UITableView+CollectionSkeleton.swift in Sources */,
F5307E321FB0F42F00EE67C5 /* RecursiveProtocol.swift in Sources */,
F5F622431FAC81FD007C062A /* CALayer+Extensions.swift in Sources */,
877EFA4821BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */,
F5F899ED1FAB9F04002E8FDA /* CollectionSkeletonProtocol.swift in Sources */,
F58A6E6E20A8C66300612494 /* Recoverable.swift in Sources */,
F5307E2C1FAF6BC900EE67C5 /* SkeletonGradient.swift in Sources */,
@@ -681,11 +731,14 @@
F54CF5E62024CEB000330B0D /* UICollectionView+CollectionSkeleton.swift in Sources */,
F56B94461FAE20AF0095662F /* PrepareForSkeletonProtocol.swift in Sources */,
F5307E391FB1078E00EE67C5 /* SkeletonCollectionViewProtocols.swift in Sources */,
872D5A5F21C24F8E0037D763 /* UITextView+Multiline.swift in Sources */,
F51DF873206E91FB00D23301 /* GenericCollectionView.swift in Sources */,
F5307E3B1FB123C100EE67C5 /* ContainsMultilineText.swift in Sources */,
F5F899E91FAB9D2B002E8FDA /* SkeletonLayer.swift in Sources */,
F5307E2E1FB0E5E400EE67C5 /* UIView+Frame.swift in Sources */,
877EFA4521BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */,
F5A5E8B4211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */,
870F4E4321CAC07300B9233B /* SkeletonConfig.swift in Sources */,
F51DF871206E91B300D23301 /* SkeletonReusableCell.swift in Sources */,
F51DF879206E9F5500D23301 /* SkeletonCollectionDelegate.swift in Sources */,
F51DE1091FBF70A70037919A /* SkeletonAnimationBuilder.swift in Sources */,
@@ -764,7 +817,7 @@
SDKROOT = appletvos;
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.0;
};
@@ -796,7 +849,7 @@
SDKROOT = appletvos;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.0;
};
@@ -815,16 +868,16 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = KWEMDK92F4;
DEVELOPMENT_TEAM = "";
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "SkeletonViewExample copy-Info.plist";
INFOPLIST_FILE = "Configs/SkeletonViewExampleCollectionview-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@@ -842,15 +895,15 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = KWEMDK92F4;
DEVELOPMENT_TEAM = "";
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "SkeletonViewExample copy-Info.plist";
INFOPLIST_FILE = "Configs/SkeletonViewExampleCollectionview-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
@@ -859,6 +912,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@@ -907,7 +961,7 @@
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
@@ -918,6 +972,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
@@ -958,7 +1013,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
@@ -987,7 +1042,7 @@
PRODUCT_NAME = SkeletonView;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
@@ -1011,7 +1066,7 @@
PRODUCT_NAME = SkeletonView;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
};
name = Release;
};
@@ -1027,7 +1082,7 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = KWEMDK92F4;
DEVELOPMENT_TEAM = "";
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "$(SRCROOT)/Configs/SkeletonViewExampleInfo.plist";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
@@ -1035,7 +1090,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@@ -1052,7 +1107,7 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = KWEMDK92F4;
DEVELOPMENT_TEAM = "";
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "$(SRCROOT)/Configs/SkeletonViewExampleInfo.plist";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
@@ -1060,7 +1115,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0930"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -29,6 +29,15 @@
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "52D6D97B1BEFF229002C0205"
BuildableName = "SkeletonView.framework"
BlueprintName = "SkeletonView-iOS"
ReferencedContainer = "container:SkeletonView.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0930"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F5F899F11FABA607002E8FDA"
BuildableName = "SkeletonViewExample.app"
BlueprintName = "SkeletonViewExample"
ReferencedContainer = "container:SkeletonView.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F5F899F11FABA607002E8FDA"
BuildableName = "SkeletonViewExample.app"
BlueprintName = "SkeletonViewExample"
ReferencedContainer = "container:SkeletonView.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F5F899F11FABA607002E8FDA"
BuildableName = "SkeletonViewExample.app"
BlueprintName = "SkeletonViewExample"
ReferencedContainer = "container:SkeletonView.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F5F899F11FABA607002E8FDA"
BuildableName = "SkeletonViewExample.app"
BlueprintName = "SkeletonViewExample"
ReferencedContainer = "container:SkeletonView.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "42ABD05A210B548200BEEFF4"
BuildableName = "SkeletonViewExampleUICollectionView.app"
BlueprintName = "SkeletonViewExampleUICollectionView"
ReferencedContainer = "container:SkeletonView.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "42ABD05A210B548200BEEFF4"
BuildableName = "SkeletonViewExampleUICollectionView.app"
BlueprintName = "SkeletonViewExampleUICollectionView"
ReferencedContainer = "container:SkeletonView.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "42ABD05A210B548200BEEFF4"
BuildableName = "SkeletonViewExampleUICollectionView.app"
BlueprintName = "SkeletonViewExampleUICollectionView"
ReferencedContainer = "container:SkeletonView.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "42ABD05A210B548200BEEFF4"
BuildableName = "SkeletonViewExampleUICollectionView.app"
BlueprintName = "SkeletonViewExampleUICollectionView"
ReferencedContainer = "container:SkeletonView.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -15,6 +15,7 @@ public enum SkeletonAppearance {
public static var `default`: Appearance = SkeletonViewAppearance.shared
}
// codebeat:disable[TOO_MANY_IVARS]
class SkeletonViewAppearance: Appearance {
static var shared = SkeletonViewAppearance()
@@ -31,3 +32,4 @@ class SkeletonViewAppearance: Appearance {
var multilineCornerRadius: Int = 0
}
// codebeat:enable[TOO_MANY_IVARS]
@@ -0,0 +1,41 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
/// Object that facilitates the creation of skeleton layers,
/// based on the builder pattern
class SkeletonLayerBuilder {
var skeletonType: SkeletonType?
var colors: [UIColor] = []
var holder: UIView?
func setSkeletonType(_ type: SkeletonType) -> SkeletonLayerBuilder {
self.skeletonType = type
return self
}
func addColor(_ color: UIColor) -> SkeletonLayerBuilder {
return addColors([color])
}
func addColors(_ colors: [UIColor]) -> SkeletonLayerBuilder {
self.colors.append(contentsOf: colors)
return self
}
func setHolder(_ holder: UIView) -> SkeletonLayerBuilder {
self.holder = holder
return self
}
func build() -> SkeletonLayer? {
guard let type = skeletonType,
let holder = holder
else { return nil }
return SkeletonLayer(withType: type,
usingColors: colors,
andSkeletonHolder: holder)
}
}
@@ -0,0 +1,51 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
/// Object that facilitates the creation of skeleton layers for multiline
/// elements, based on the builder pattern
class SkeletonMultilineLayerBuilder {
var skeletonType: SkeletonType?
var index: Int?
var width: CGFloat?
var cornerRadius: Int?
func setSkeletonType(_ type: SkeletonType) -> SkeletonMultilineLayerBuilder {
self.skeletonType = type
return self
}
func setIndex(_ index: Int) -> SkeletonMultilineLayerBuilder {
self.index = index
return self
}
func setWidth(_ width: CGFloat) -> SkeletonMultilineLayerBuilder {
self.width = width
return self
}
func setCornerRadius(_ radius: Int) -> SkeletonMultilineLayerBuilder {
self.cornerRadius = radius
return self
}
func build() -> CALayer? {
guard let type = skeletonType,
let index = index,
let width = width,
let radius = cornerRadius
else { return nil }
let layer = type.layer
layer.anchorPoint = .zero
layer.name = CALayer.skeletonSubLayersName
layer.updateLayerFrame(for: index, width: width)
layer.cornerRadius = CGFloat(radius)
layer.masksToBounds = true
return layer
}
}
@@ -19,6 +19,7 @@ protocol CollectionSkeleton {
var estimatedNumberOfRows: Int { get }
func addDummyDataSource()
func updateDummyDataSource()
func removeDummyDataSource(reloadAfter: Bool)
func disableUserInteraction()
func enableUserInteraction()
@@ -13,6 +13,7 @@ public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
func numSections(in collectionSkeletonView: UICollectionView) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier
func collectionSkeletonView(_ skeletonView: UICollectionView, supplementaryViewIdentifierOfKind: String, at indexPath: IndexPath) -> ReusableCellIdentifier?
}
public extension SkeletonCollectionViewDataSource {
@@ -21,6 +22,12 @@ public extension SkeletonCollectionViewDataSource {
return skeletonView.estimatedNumberOfRows
}
func collectionSkeletonView(_ skeletonView: UICollectionView,
supplementaryViewIdentifierOfKind: String,
at indexPath: IndexPath) -> ReusableCellIdentifier? {
return nil
}
func numSections(in collectionSkeletonView: UICollectionView) -> Int { return 1 }
}
@@ -16,17 +16,17 @@ extension UICollectionView: CollectionSkeleton {
}
var skeletonDataSource: SkeletonCollectionDataSource? {
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
set {
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource, newValue, AssociationPolicy.retain.objc)
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDataSource)
self.dataSource = newValue
}
}
var skeletonDelegate: SkeletonCollectionDelegate? {
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
set {
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate, newValue, AssociationPolicy.retain.objc)
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDelegate)
self.delegate = newValue
}
}
@@ -41,6 +41,14 @@ extension UICollectionView: CollectionSkeleton {
reloadData()
}
func updateDummyDataSource() {
if (dataSource as? SkeletonCollectionDataSource) != nil {
reloadData()
} else {
addDummyDataSource()
}
}
func removeDummyDataSource(reloadAfter: Bool) {
guard let dataSource = self.dataSource as? SkeletonCollectionDataSource else { return }
self.skeletonDataSource = nil
@@ -38,6 +38,7 @@ extension SkeletonCollectionDataSource: UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = originalTableViewDataSource?.collectionSkeletonView(tableView, cellIdentifierForRowAt: indexPath) ?? ""
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
skeletonCellIfContainerSkeletonIsActive(container: tableView, cell: cell)
return cell
}
}
@@ -56,6 +57,31 @@ extension SkeletonCollectionDataSource: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cellIdentifier = originalCollectionViewDataSource?.collectionSkeletonView(collectionView, cellIdentifierForItemAt: indexPath) ?? ""
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath)
skeletonCellIfContainerSkeletonIsActive(container: collectionView, cell: cell)
return cell
}
func collectionView(_ collectionView: UICollectionView,
viewForSupplementaryElementOfKind kind: String,
at indexPath: IndexPath) -> UICollectionReusableView {
if let viewIdentifier = originalCollectionViewDataSource?.collectionSkeletonView(collectionView, supplementaryViewIdentifierOfKind: kind, at: indexPath) {
return collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: viewIdentifier, for: indexPath)
}
return UICollectionReusableView()
}
}
extension SkeletonCollectionDataSource {
private func skeletonCellIfContainerSkeletonIsActive(container: UIView, cell: UIView) {
guard container.isSkeletonActive,
let skeletonConfig = container.currentSkeletonConfig else {
return
}
cell.showSkeleton(skeletonConfig: skeletonConfig)
}
}
@@ -15,17 +15,17 @@ extension UITableView: CollectionSkeleton {
}
var skeletonDataSource: SkeletonCollectionDataSource? {
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
set {
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource, newValue, AssociationPolicy.retain.objc)
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDataSource)
self.dataSource = newValue
}
}
var skeletonDelegate: SkeletonCollectionDelegate? {
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
set {
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate, newValue, AssociationPolicy.retain.objc)
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDelegate)
self.delegate = newValue
}
}
@@ -34,11 +34,21 @@ extension UITableView: CollectionSkeleton {
guard let originalDataSource = self.dataSource as? SkeletonTableViewDataSource,
!(originalDataSource is SkeletonCollectionDataSource)
else { return }
let dataSource = SkeletonCollectionDataSource(tableViewDataSource: originalDataSource, rowHeight: calculateRowHeight())
let rowHeight = calculateRowHeight()
let dataSource = SkeletonCollectionDataSource(tableViewDataSource: originalDataSource,
rowHeight: rowHeight)
self.skeletonDataSource = dataSource
reloadData()
}
func updateDummyDataSource() {
if (dataSource as? SkeletonCollectionDataSource) != nil {
reloadData()
} else {
addDummyDataSource()
}
}
func removeDummyDataSource(reloadAfter: Bool) {
guard let dataSource = self.dataSource as? SkeletonCollectionDataSource else { return }
restoreRowHeight()
@@ -53,7 +63,7 @@ extension UITableView: CollectionSkeleton {
}
private func calculateRowHeight() -> CGFloat {
guard rowHeight == UITableViewAutomaticDimension else { return rowHeight }
guard rowHeight == UITableView.automaticDimension else { return rowHeight }
rowHeight = estimatedRowHeight
return estimatedRowHeight
}
@@ -15,6 +15,11 @@ extension UIView {
collection.disableUserInteraction()
}
func updateDummyDataSourceIfNeeded() {
guard let collection = self as? CollectionSkeleton else { return }
collection.updateDummyDataSource()
}
func removeDummyDataSourceIfNeeded(reloadAfter reload: Bool = true) {
guard let collection = self as? CollectionSkeleton else { return }
collection.removeDummyDataSource(reloadAfter: reload)
+41 -10
View File
@@ -40,18 +40,49 @@ extension CALayer {
func addMultilinesLayers(lines: Int, type: SkeletonType, lastLineFillPercent: Int, multilineCornerRadius: Int) {
let numberOfSublayers = calculateNumLines(maxLines: lines)
for index in 0..<numberOfSublayers {
var width = bounds.width
if index == numberOfSublayers-1 && numberOfSublayers != 1 {
width = width * CGFloat(lastLineFillPercent)/100;
let layerBuilder = SkeletonMultilineLayerBuilder()
.setSkeletonType(type)
.setCornerRadius(multilineCornerRadius)
(0..<numberOfSublayers).forEach { index in
var width = getLineWidth(index: index, numberOfSublayers: numberOfSublayers, lastLineFillPercent: lastLineFillPercent)
if index == numberOfSublayers - 1 && numberOfSublayers != 1 {
width = width * CGFloat(lastLineFillPercent) / 100;
}
if let layer = layerBuilder
.setIndex(index)
.setWidth(width)
.build() {
addSublayer(layer)
}
let layer = SkeletonLayerFactory().makeMultilineLayer(withType: type, for: index, width: width, multilineCornerRadius: multilineCornerRadius)
addSublayer(layer)
}
}
func updateMultilinesLayers(lastLineFillPercent: Int) {
let currentSkeletonSublayers = skeletonSublayers
let numberOfSublayers = currentSkeletonSublayers.count
for (index, layer) in currentSkeletonSublayers.enumerated() {
let width = getLineWidth(index: index, numberOfSublayers: numberOfSublayers, lastLineFillPercent: lastLineFillPercent)
layer.updateLayerFrame(for: index, width: width)
}
}
private func getLineWidth(index: Int, numberOfSublayers: Int, lastLineFillPercent: Int) -> CGFloat {
var width = bounds.width
if index == numberOfSublayers - 1 && numberOfSublayers != 1 {
width = width * CGFloat(lastLineFillPercent) / 100;
}
return width
}
func updateLayerFrame(for index: Int, width: CGFloat) {
let spaceRequiredForEachLine = SkeletonAppearance.default.multilineHeight + SkeletonAppearance.default.multilineSpacing
frame = CGRect(x: 0.0, y: CGFloat(index) * spaceRequiredForEachLine, width: width, height: SkeletonAppearance.default.multilineHeight)
}
private func calculateNumLines(maxLines: Int) -> Int {
let spaceRequitedForEachLine = SkeletonAppearance.default.multilineHeight + SkeletonAppearance.default.multilineSpacing
var numberOfSublayers = Int(round(CGFloat(bounds.height)/CGFloat(spaceRequitedForEachLine)))
@@ -68,7 +99,7 @@ public extension CALayer {
pulseAnimation.fromValue = backgroundColor
pulseAnimation.toValue = UIColor(cgColor: backgroundColor!).complementaryColor.cgColor
pulseAnimation.duration = 1
pulseAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
pulseAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
pulseAnimation.autoreverses = true
pulseAnimation.repeatCount = .infinity
return pulseAnimation
@@ -86,7 +117,7 @@ public extension CALayer {
let animGroup = CAAnimationGroup()
animGroup.animations = [startPointAnim, endPointAnim]
animGroup.duration = 1.5
animGroup.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
animGroup.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
animGroup.repeatCount = .infinity
return animGroup
+3 -9
View File
@@ -1,13 +1,8 @@
//
// UIColor+Skeleton.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 03/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
// codebeat:disable[TOO_MANY_IVARS]
extension UIColor {
convenience init(_ hex: UInt) {
@@ -20,9 +15,8 @@ extension UIColor {
}
func isLight() -> Bool {
// algorithm from: http://www.w3.org/WAI/ER/WD-AERT/#color-contrast
guard let components = cgColor.components,
components.count >= 3 else { return false }
components.count >= 3 else { return false }
let brightness = ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000
return !(brightness < 0.5)
}
@@ -72,4 +66,4 @@ public extension UIColor {
static var silver = UIColor(0xbdc3c7)
static var asbestos = UIColor(0x7f8c8d)
}
// codebeat:enable[TOO_MANY_IVARS]
+48
View File
@@ -0,0 +1,48 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
// codebeat:disable[TOO_MANY_IVARS]
enum ViewAssociatedKeys {
static var skeletonable = "skeletonable"
static var status = "status"
static var skeletonLayer = "layer"
static var flowDelegate = "flowDelegate"
static var isSkeletonAnimated = "isSkeletonAnimated"
static var viewState = "viewState"
static var currentSkeletonConfig = "currentSkeletonConfig"
}
// codebeat:enable[TOO_MANY_IVARS]
extension UIView {
enum Status {
case on
case off
}
var flowDelegate: SkeletonFlowDelegate? {
get { return ao_get(pkey: &ViewAssociatedKeys.flowDelegate) as? SkeletonFlowDelegate }
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.flowDelegate) }
}
var skeletonLayer: SkeletonLayer? {
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonLayer) as? SkeletonLayer }
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.skeletonLayer) }
}
var currentSkeletonConfig: SkeletonConfig? {
get { return ao_get(pkey: &ViewAssociatedKeys.currentSkeletonConfig) as? SkeletonConfig }
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.currentSkeletonConfig) }
}
var status: Status! {
get { return ao_get(pkey: &ViewAssociatedKeys.status) as? Status ?? .off }
set { ao_set(newValue ?? .off, pkey: &ViewAssociatedKeys.status) }
}
var skeletonIsAnimated: Bool! {
get { return ao_get(pkey: &ViewAssociatedKeys.isSkeletonAnimated) as? Bool ?? false }
set { ao_set(newValue ?? false, pkey: &ViewAssociatedKeys.isSkeletonAnimated) }
}
}
+2 -2
View File
@@ -32,12 +32,12 @@ extension UIView {
}
var maxWidthEstimated: CGFloat {
let constraintsWidth = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutAttribute.width })
let constraintsWidth = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutConstraint.Attribute.width })
return max(between: frame.size.width, andContantsOf: constraintsWidth)
}
var maxHeightEstimated: CGFloat {
let constraintsHeight = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutAttribute.height })
let constraintsHeight = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutConstraint.Attribute.height })
return max(between: frame.size.height, andContantsOf: constraintsHeight)
}
+2 -50
View File
@@ -1,22 +1,7 @@
//
// UIView+IBInspectable.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 01/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
private enum AssociatedKeys {
static var skeletonable = "skeletonable"
static var status = "status"
static var skeletonLayer = "layer"
static var flowDelegate = "flowDelegate"
static var isSkeletonAnimated = "isSkeletonAnimated"
static var viewState = "viewState"
}
public extension UIView {
@IBInspectable
@@ -28,43 +13,10 @@ public extension UIView {
var isSkeletonActive: Bool {
return status == .on || (subviewsSkeletonables.first(where: { $0.isSkeletonActive }) != nil)
}
}
extension UIView {
enum Status {
case on
case off
}
var flowDelegate: SkeletonFlowDelegate? {
get { return objc_getAssociatedObject(self, &AssociatedKeys.flowDelegate) as? SkeletonFlowDelegate }
set { objc_setAssociatedObject(self, &AssociatedKeys.flowDelegate, newValue, AssociationPolicy.retain.objc) }
}
var skeletonLayer: SkeletonLayer? {
get { return objc_getAssociatedObject(self, &AssociatedKeys.skeletonLayer) as? SkeletonLayer }
set { objc_setAssociatedObject(self, &AssociatedKeys.skeletonLayer, newValue, AssociationPolicy.retain.objc) }
}
var status: Status! {
get { return objc_getAssociatedObject(self, &AssociatedKeys.status) as? Status ?? .off }
set { objc_setAssociatedObject(self, &AssociatedKeys.status, newValue, AssociationPolicy.retain.objc) }
}
var viewState: RecoverableViewState? {
get { return objc_getAssociatedObject(self, &AssociatedKeys.viewState) as? RecoverableViewState }
set { objc_setAssociatedObject(self, &AssociatedKeys.viewState, newValue, AssociationPolicy.retain.objc) }
}
var skeletonIsAnimated: Bool! {
get { return objc_getAssociatedObject(self, &AssociatedKeys.isSkeletonAnimated) as? Bool ?? false }
set { objc_setAssociatedObject(self, &AssociatedKeys.isSkeletonAnimated, newValue, AssociationPolicy.retain.objc) }
}
fileprivate var skeletonable: Bool! {
get { return objc_getAssociatedObject(self, &AssociatedKeys.skeletonable) as? Bool ?? false }
set { objc_setAssociatedObject(self, &AssociatedKeys.skeletonable, newValue, AssociationPolicy.retain.objc) }
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonable) as? Bool ?? false }
set { ao_set(newValue ?? false, pkey: &ViewAssociatedKeys.skeletonable) }
}
}
@@ -11,8 +11,8 @@ import UIKit
extension UIView {
enum Constants {
static let becomeActiveNotification = NSNotification.Name.UIApplicationDidBecomeActive
static let enterForegroundNotification = NSNotification.Name.UIApplicationDidEnterBackground
static let becomeActiveNotification = UIApplication.didBecomeActiveNotification
static let enterForegroundNotification = UIApplication.didEnterBackgroundNotification
static let needAnimatedSkeletonKey = "needAnimateSkeleton"
}
+29 -6
View File
@@ -1,10 +1,4 @@
//
// AssociationPolicy.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 02/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import Foundation
@@ -21,3 +15,32 @@ enum AssociationPolicy: UInt {
return objc_AssociationPolicy(rawValue: rawValue)!
}
}
protocol AssociatedObjects: class { }
// transparent wrappers
extension AssociatedObjects {
/// wrapper around `objc_getAssociatedObject`
func ao_get(pkey: UnsafeRawPointer) -> Any? {
return objc_getAssociatedObject(self, pkey)
}
/// wrapper around `objc_setAssociatedObject`
func ao_setOptional(_ value: Any?, pkey: UnsafeRawPointer, policy: AssociationPolicy = .retainNonatomic) {
guard let value = value else { return }
objc_setAssociatedObject(self, pkey, value, policy.objc)
}
/// wrapper around `objc_setAssociatedObject`
func ao_set(_ value: Any, pkey: UnsafeRawPointer, policy: AssociationPolicy = .retainNonatomic) {
objc_setAssociatedObject(self, pkey, value, policy.objc)
}
/// wrapper around 'objc_removeAssociatedObjects'
func ao_removeAll() {
objc_removeAssociatedObjects(self)
}
}
extension NSObject: AssociatedObjects { }
@@ -1,82 +0,0 @@
//
// ContainsMultilineText.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 07/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
private enum AssociatedKeys {
static var lastLineFillingPercent = "lastLineFillingPercent"
static var multilineCornerRadius = "multilineCornerRadius"
}
protocol ContainsMultilineText {
var numLines: Int { get }
var lastLineFillingPercent: Int { get }
var multilineCornerRadius: Int { get }
}
extension ContainsMultilineText {
var numLines: Int { return 0 }
}
public extension UILabel {
@IBInspectable
var lastLineFillPercent: Int {
get { return lastLineFillingPercent }
set { lastLineFillingPercent = min(newValue, 100) }
}
@IBInspectable
var linesCornerRadius: Int {
get { return multilineCornerRadius }
set { multilineCornerRadius = min(newValue, 10) }
}
}
public extension UITextView {
@IBInspectable
var lastLineFillPercent: Int {
get { return lastLineFillingPercent }
set { lastLineFillingPercent = min(newValue, 100) }
}
@IBInspectable
var linesCornerRadius: Int {
get { return multilineCornerRadius }
set { multilineCornerRadius = min(newValue, 10) }
}
}
extension UILabel: ContainsMultilineText {
var numLines: Int {
return numberOfLines
}
var lastLineFillingPercent: Int {
get { return objc_getAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent) as? Int ?? SkeletonAppearance.default.multilineLastLineFillPercent }
set { objc_setAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent, newValue, AssociationPolicy.retain.objc) }
}
var multilineCornerRadius: Int {
get { return objc_getAssociatedObject(self, &AssociatedKeys.multilineCornerRadius) as? Int ?? SkeletonAppearance.default.multilineCornerRadius }
set { objc_setAssociatedObject(self, &AssociatedKeys.multilineCornerRadius, newValue, AssociationPolicy.retain.objc) }
}
}
extension UITextView: ContainsMultilineText {
var lastLineFillingPercent: Int {
get { return objc_getAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent) as? Int ?? SkeletonAppearance.default.multilineLastLineFillPercent }
set { objc_setAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent, newValue, AssociationPolicy.retain.objc) }
}
var multilineCornerRadius: Int {
get { return objc_getAssociatedObject(self, &AssociatedKeys.multilineCornerRadius) as? Int ?? SkeletonAppearance.default.multilineCornerRadius }
set { objc_setAssociatedObject(self, &AssociatedKeys.multilineCornerRadius, newValue, AssociationPolicy.retain.objc) }
}
}
@@ -0,0 +1,18 @@
// Copyright © 2017 SkeletonView. All rights reserved.
import UIKit
enum MultilineAssociatedKeys {
static var lastLineFillingPercent = "lastLineFillingPercent"
static var multilineCornerRadius = "multilineCornerRadius"
}
protocol ContainsMultilineText {
var numLines: Int { get }
var lastLineFillingPercent: Int { get }
var multilineCornerRadius: Int { get }
}
extension ContainsMultilineText {
var numLines: Int { return 0 }
}
@@ -0,0 +1,32 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
public extension UILabel {
@IBInspectable
var lastLineFillPercent: Int {
get { return lastLineFillingPercent }
set { lastLineFillingPercent = min(newValue, 100) }
}
@IBInspectable
var linesCornerRadius: Int {
get { return multilineCornerRadius }
set { multilineCornerRadius = min(newValue, 10) }
}
}
extension UILabel: ContainsMultilineText {
var numLines: Int {
return numberOfLines
}
var lastLineFillingPercent: Int {
get { return ao_get(pkey: &MultilineAssociatedKeys.lastLineFillingPercent) as? Int ?? SkeletonAppearance.default.multilineLastLineFillPercent }
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.lastLineFillingPercent) }
}
var multilineCornerRadius: Int {
get { return ao_get(pkey: &MultilineAssociatedKeys.multilineCornerRadius) as? Int ?? SkeletonAppearance.default.multilineCornerRadius }
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.multilineCornerRadius) }
}
}
@@ -0,0 +1,36 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
public extension UITextView {
@IBInspectable
var lastLineFillPercent: Int {
get { return lastLineFillingPercent }
set { lastLineFillingPercent = min(newValue, 100) }
}
@IBInspectable
var linesCornerRadius: Int {
get { return multilineCornerRadius }
set { multilineCornerRadius = min(newValue, 10) }
}
}
extension UITextView: ContainsMultilineText {
var lastLineFillingPercent: Int {
get {
let defaultValue = SkeletonAppearance.default.multilineLastLineFillPercent
return ao_get(pkey: &MultilineAssociatedKeys.lastLineFillingPercent) as? Int ?? defaultValue
}
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.lastLineFillingPercent) }
}
var multilineCornerRadius: Int {
get {
let defaultValue = SkeletonAppearance.default.multilineCornerRadius
return ao_get(pkey: &MultilineAssociatedKeys.multilineCornerRadius) as? Int ?? defaultValue
}
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.multilineCornerRadius) }
}
}
+5
View File
@@ -15,6 +15,11 @@ protocol Recoverable {
}
extension UIView: Recoverable {
var viewState: RecoverableViewState? {
get { return ao_get(pkey: &ViewAssociatedKeys.viewState) as? RecoverableViewState }
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.viewState) }
}
@objc func saveViewState() {
viewState = RecoverableViewState(view: self)
+4 -2
View File
@@ -21,7 +21,8 @@ public enum GradientDirection {
public func slidingAnimation(duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation {
return SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: self, duration: duration)
}
// codebeat:disable[ABC]
var startPoint: GradientAnimationPoint {
switch self {
case .leftRight:
@@ -55,6 +56,7 @@ public enum GradientDirection {
return ( from: CGPoint(x:2, y:2), to: CGPoint(x:0, y:0))
}
}
// codebeat:enable[ABC]
}
public class SkeletonAnimationBuilder {
@@ -76,7 +78,7 @@ public class SkeletonAnimationBuilder {
let animGroup = CAAnimationGroup()
animGroup.animations = [startPointAnim, endPointAnim]
animGroup.duration = duration
animGroup.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
animGroup.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
animGroup.repeatCount = .infinity
return animGroup
+31
View File
@@ -0,0 +1,31 @@
// Copyright © 2018 SkeletonView. All rights reserved.
import UIKit
/// Used to store all config needed to activate the skeleton layer.
struct SkeletonConfig {
/// Type of skeleton layer
let type: SkeletonType
/// Colors used in skeleton layer
let colors: [UIColor]
/// If type is gradient, which gradient direction
let gradientDirection: GradientDirection?
/// Specify if skeleton is animated or not
let animated: Bool
/// Used to execute a custom animation
let animation: SkeletonLayerAnimation?
init(
type: SkeletonType,
colors: [UIColor],
gradientDirection: GradientDirection? = nil,
animated: Bool = false,
animation: SkeletonLayerAnimation? = nil
) {
self.type = type
self.colors = colors
self.gradientDirection = gradientDirection
self.animated = animated
self.animation = animation
}
}
+16 -1
View File
@@ -5,9 +5,12 @@ import UIKit
protocol SkeletonFlowDelegate {
func willBeginShowingSkeletons(withRootView rootView: UIView)
func didShowSkeletons(withRootView rootView: UIView)
func willBeginUpdatingSkeletons(withRootView rootView: UIView)
func didUpdateSkeletons(withRootView rootView: UIView)
func willBeginLayingSkeletonsIfNeeded(withRootView: UIView)
func didLayoutSkeletonsIfNeeded(withRootView: UIView)
func willBeginHidingSkeletons(withRootView rootView: UIView)
func didHideSkeletons(withRootView rootView: UIView)
}
class SkeletonFlowHandler: SkeletonFlowDelegate {
@@ -20,6 +23,18 @@ class SkeletonFlowHandler: SkeletonFlowDelegate {
printSkeletonHierarchy(in: rootView)
}
func willBeginUpdatingSkeletons(withRootView rootView: UIView) {
}
func didUpdateSkeletons(withRootView rootView: UIView) {
}
func willBeginLayingSkeletonsIfNeeded(withRootView: UIView) {
}
func didLayoutSkeletonsIfNeeded(withRootView: UIView) {
}
func willBeginHidingSkeletons(withRootView rootView: UIView) {
rootView.removeAppNoticationsObserver()
}
+17 -20
View File
@@ -8,26 +8,6 @@
import UIKit
class SkeletonLayerFactory {
func makeSkeletonLayer(withType type: SkeletonType, usingColors colors: [UIColor], andHolder holder: UIView) -> SkeletonLayer {
return SkeletonLayer(withType: type, usingColors: colors, andSkeletonHolder: holder)
}
func makeMultilineLayer(withType type: SkeletonType, for index: Int, width: CGFloat, multilineCornerRadius: Int) -> CALayer {
let spaceRequiredForEachLine = SkeletonAppearance.default.multilineHeight + SkeletonAppearance.default.multilineSpacing
let layer = type.layer
layer.anchorPoint = .zero
layer.name = CALayer.skeletonSubLayersName
layer.frame = CGRect(x: 0.0, y: CGFloat(index) * spaceRequiredForEachLine, width: width, height: SkeletonAppearance.default.multilineHeight)
layer.cornerRadius = CGFloat(multilineCornerRadius)
layer.masksToBounds = true
return layer
}
}
public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation
public enum SkeletonType {
@@ -75,6 +55,18 @@ struct SkeletonLayer {
self.maskLayer.tint(withColors: colors)
}
func update(usingColors colors: [UIColor]) {
layoutIfNeeded()
self.maskLayer.tint(withColors: colors)
}
func layoutIfNeeded() {
if let bounds = self.holder?.maxBoundsEstimated {
self.maskLayer.bounds = bounds
}
updateMultilinesIfNeeded()
}
func removeLayer() {
maskLayer.removeFromSuperlayer()
}
@@ -83,6 +75,11 @@ struct SkeletonLayer {
guard let multiLineView = holder as? ContainsMultilineText else { return }
maskLayer.addMultilinesLayers(lines: multiLineView.numLines, type: type, lastLineFillPercent: multiLineView.lastLineFillingPercent, multilineCornerRadius: multiLineView.multilineCornerRadius)
}
func updateMultilinesIfNeeded() {
guard let multiLineView = holder as? ContainsMultilineText else { return }
maskLayer.updateMultilinesLayers(lastLineFillPercent: multiLineView.lastLineFillingPercent)
}
}
extension SkeletonLayer {
+133 -27
View File
@@ -5,19 +5,49 @@ import UIKit
public extension UIView {
func showSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor) {
showSkeleton(withType: .solid, usingColors: [color])
let config: SkeletonConfig = SkeletonConfig(type: .solid, colors: [color])
showSkeleton(skeletonConfig: config)
}
func showGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient) {
showSkeleton(withType: .gradient, usingColors: gradient.colors)
let config: SkeletonConfig = SkeletonConfig(type: .gradient, colors: gradient.colors)
showSkeleton(skeletonConfig: config)
}
func showAnimatedSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor, animation: SkeletonLayerAnimation? = nil) {
showSkeleton(withType: .solid, usingColors: [color], animated: true, animation: animation)
let config: SkeletonConfig = SkeletonConfig(type: .solid, colors: [color], animated: true, animation: animation)
showSkeleton(skeletonConfig: config)
}
func showAnimatedGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient, animation: SkeletonLayerAnimation? = nil) {
showSkeleton(withType: .gradient, usingColors: gradient.colors, animated: true, animation: animation)
let config: SkeletonConfig = SkeletonConfig(type: .gradient, colors: gradient.colors, animated: true, animation: animation)
showSkeleton(skeletonConfig: config)
}
func updateSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor) {
let config: SkeletonConfig = SkeletonConfig(type: .solid, colors: [color])
updateSkeleton(skeletonConfig: config)
}
func updateGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient) {
let config: SkeletonConfig = SkeletonConfig(type: .gradient, colors: gradient.colors)
updateSkeleton(skeletonConfig: config)
}
func updateAnimatedSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor, animation: SkeletonLayerAnimation? = nil) {
let config: SkeletonConfig = SkeletonConfig(type: .solid, colors: [color], animated: true, animation: animation)
updateSkeleton(skeletonConfig: config)
}
func updateAnimatedGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient, animation: SkeletonLayerAnimation? = nil) {
let config: SkeletonConfig = SkeletonConfig(type: .gradient, colors: gradient.colors, animated: true, animation: animation)
updateSkeleton(skeletonConfig: config)
}
func layoutSkeletonIfNeeded() {
guard let flowDelegate = flowDelegate else { return }
flowDelegate.willBeginLayingSkeletonsIfNeeded(withRootView: self)
recursiveLayoutSkeletonIfNeeded(root: self)
}
func hideSkeleton(reloadDataAfter reload: Bool = true) {
@@ -42,33 +72,88 @@ public extension UIView {
extension UIView {
func showSkeleton(withType type: SkeletonType = .solid, usingColors colors: [UIColor], animated: Bool = false, animation: SkeletonLayerAnimation? = nil) {
skeletonIsAnimated = animated
func showSkeleton(skeletonConfig config: SkeletonConfig) {
skeletonIsAnimated = config.animated
flowDelegate = SkeletonFlowHandler()
flowDelegate?.willBeginShowingSkeletons(withRootView: self)
recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation, root: self)
recursiveShowSkeleton(skeletonConfig: config, root: self)
}
fileprivate func recursiveShowSkeleton(withType type: SkeletonType, usingColors colors: [UIColor], animated: Bool, animation: SkeletonLayerAnimation?, root: UIView? = nil) {
addDummyDataSourceIfNeeded()
fileprivate func recursiveShowSkeleton(skeletonConfig config: SkeletonConfig, root: UIView? = nil) {
layoutIfNeeded()
currentSkeletonConfig = config
addDummyDataSourceIfNeeded()
subviewsSkeletonables.recursiveSearch(leafBlock: {
guard !isSkeletonActive else { return }
isUserInteractionEnabled = false
saveViewState()
(self as? PrepareForSkeleton)?.prepareViewForSkeleton()
addSkeletonLayer(withType: type, usingColors: colors, animated: animated, animation: animation)
}) { subview in
subview.recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation)
showSkeletonIfNotActive(skeletonConfig: config)
}){ subview in
subview.recursiveShowSkeleton(skeletonConfig: config)
}
if let root = root {
flowDelegate?.didShowSkeletons(withRootView: root)
}
}
fileprivate func showSkeletonIfNotActive(skeletonConfig config: SkeletonConfig) {
guard !self.isSkeletonActive else { return }
self.isUserInteractionEnabled = false
self.saveViewState()
(self as? PrepareForSkeleton)?.prepareViewForSkeleton()
self.addSkeletonLayer(skeletonConfig: config)
}
func updateSkeleton(skeletonConfig config: SkeletonConfig) {
guard let flowDelegate = flowDelegate else { return }
skeletonIsAnimated = config.animated
flowDelegate.willBeginUpdatingSkeletons(withRootView: self)
recursiveUpdateSkeleton(skeletonConfig: config, root: self)
}
fileprivate func recursiveUpdateSkeleton(skeletonConfig config: SkeletonConfig, root: UIView? = nil) {
layoutIfNeeded()
currentSkeletonConfig = config
updateDummyDataSourceIfNeeded()
subviewsSkeletonables.recursiveSearch(leafBlock: {
guard isSkeletonActive else { return }
if skeletonLayer?.type != config.type {
hideSkeleton()
}
if isSkeletonActive {
updateSkeletonLayer(skeletonConfig: config)
} else {
showSkeletonIfNotActive(skeletonConfig: config)
}
}) { subview in
subview.recursiveUpdateSkeleton(skeletonConfig: config)
}
if let root = root {
flowDelegate?.didUpdateSkeletons(withRootView: root)
}
}
fileprivate func recursiveLayoutSkeletonIfNeeded(root: UIView? = nil) {
layoutIfNeeded()
subviewsSkeletonables.recursiveSearch(leafBlock: {
layoutSkeletonLayerIfNeeded()
}) { subview in
subview.recursiveLayoutSkeletonIfNeeded()
}
if let root = root {
flowDelegate?.didLayoutSkeletonsIfNeeded(withRootView: root)
}
}
fileprivate func recursiveHideSkeleton(reloadDataAfter reload: Bool, root: UIView? = nil) {
removeDummyDataSourceIfNeeded()
removeDummyDataSourceIfNeeded(reloadAfter: reload)
isUserInteractionEnabled = true
subviewsSkeletonables.recursiveSearch(leafBlock: {
recoverViewState(forced: false)
@@ -76,6 +161,7 @@ extension UIView {
}) { subview in
subview.recursiveHideSkeleton(reloadDataAfter: reload)
}
if let root = root {
flowDelegate?.didHideSkeletons(withRootView: root)
}
@@ -98,20 +184,40 @@ extension UIView {
extension UIView {
func addSkeletonLayer(withType type: SkeletonType, usingColors colors: [UIColor], gradientDirection direction: GradientDirection? = nil, animated: Bool, animation: SkeletonLayerAnimation? = nil) {
self.skeletonLayer = SkeletonLayerFactory().makeSkeletonLayer(withType: type, usingColors: colors, andHolder: self)
layer.insertSublayer(skeletonLayer!.contentLayer, at: UInt32.max)
if animated { skeletonLayer!.start(animation) }
func addSkeletonLayer(skeletonConfig config: SkeletonConfig) {
guard let skeletonLayer = SkeletonLayerBuilder()
.setSkeletonType(config.type)
.addColors(config.colors)
.setHolder(self)
.build()
else { return }
self.skeletonLayer = skeletonLayer
layer.insertSublayer(skeletonLayer.contentLayer, at: UInt32.max)
if config.animated { skeletonLayer.start(config.animation) }
status = .on
}
func updateSkeletonLayer(skeletonConfig config: SkeletonConfig) {
guard let skeletonLayer = skeletonLayer else { return }
skeletonLayer.update(usingColors: config.colors)
if config.animated { skeletonLayer.start(config.animation) }
else { skeletonLayer.stopAnimation() }
}
func layoutSkeletonLayerIfNeeded() {
guard let skeletonLayer = skeletonLayer else { return }
skeletonLayer.layoutIfNeeded()
}
func removeSkeletonLayer() {
guard isSkeletonActive,
let layer = skeletonLayer else { return }
layer.stopAnimation()
layer.removeLayer()
skeletonLayer = nil
let skeletonLayer = skeletonLayer else { return }
skeletonLayer.stopAnimation()
skeletonLayer.removeLayer()
self.skeletonLayer = nil
status = .off
currentSkeletonConfig = nil
}
}
+13
View File
@@ -0,0 +1,13 @@
default_platform(:ios)
podspec_name = "SkeletonView.podspec"
lane :release_current do
version = version_get_podspec(path: @podspec_name)
if git_tag_exists(tag: version)
UI.user_error!("The tag #{version} already exists on the repo. To release a new version of the library bump the version on #{@podspec_name}")
end
pod_lib_lint
add_git_tag(tag: "#{version}")
push_git_tags
pod_push
end
+28
View File
@@ -0,0 +1,28 @@
fastlane documentation
================
# Installation
Make sure you have the latest version of the Xcode command line tools installed:
```
xcode-select --install
```
Install _fastlane_ using
```
[sudo] gem install fastlane -NV
```
or alternatively using `brew cask install fastlane`
# Available Actions
### release_current
```
fastlane release_current
```
----
This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).