Compare commits
184 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b93bae7b49 | |||
| 013cc025a1 | |||
| cc0cdf548e | |||
| 20d171f0db | |||
| ed563a29d7 | |||
| 38f9379aa5 | |||
| d94517939a | |||
| 65e86056cf | |||
| a0b01915fb | |||
| f16f4044d7 | |||
| ad28803221 | |||
| a6a988d169 | |||
| 3b17dbe7f2 | |||
| 06002ac2e2 | |||
| 060c66ecf9 | |||
| b3c25674ed | |||
| 7585d068f4 | |||
| 29c4ab8351 | |||
| 90b423c893 | |||
| 1184453782 | |||
| 33108b2a87 | |||
| e4cde53d73 | |||
| 7a35023c2d | |||
| d66c8c3f4d | |||
| fe195036cc | |||
| ac5c7b65f4 | |||
| 828a3a9655 | |||
| 8b9c1e387f | |||
| 88d7d7ea18 | |||
| d5639c6195 | |||
| c473f0c499 | |||
| a7c884771b | |||
| e291ab5da1 | |||
| e1a99a7ec1 | |||
| 0b5d6189b1 | |||
| 47a15e4c6d | |||
| 1f81365096 | |||
| 8e49eb305d | |||
| d09a5d75ee | |||
| 8ab04f8108 | |||
| f5d0620a57 | |||
| 8ddd4ccd27 | |||
| 57aa984a01 | |||
| 7a0c45a47e | |||
| 30fd5aa762 | |||
| 14b0248612 | |||
| 6b914ba1d5 | |||
| 3dac6c7f2f | |||
| 1a6ab34e7b | |||
| 601dc7a4e0 | |||
| ec3398bda0 | |||
| 5a75e00604 | |||
| 906c1845d3 | |||
| 8a2a512bbb | |||
| e6eb35e139 | |||
| 9c66a4b74c | |||
| 0d5c0a228b | |||
| 440e5ba022 | |||
| 26191ceb46 | |||
| 147ebd0365 | |||
| cd07ce7314 | |||
| 51ceb81f29 | |||
| 9d4e7688a3 | |||
| 6fc98f08d1 | |||
| 64cf3b07a2 | |||
| ba889f68c9 | |||
| e0134c3ee1 | |||
| f393954a96 | |||
| eff72e6a93 | |||
| f60982bb0e | |||
| 194e2ddaaf | |||
| 62401d502f | |||
| e54d458433 | |||
| 06085089fb | |||
| fd7ce7320e | |||
| 2538a62c03 | |||
| d0eeb5f42f | |||
| db1d8d7f4e | |||
| 43b64f8b30 | |||
| 5925d86d3f | |||
| 3a234048d4 | |||
| bea8fe7174 | |||
| b87a6e1d3c | |||
| c51f495410 | |||
| b279b8978a | |||
| 58dfa91ae6 | |||
| 42095e776b | |||
| 255479098f | |||
| 3a722b8a41 | |||
| 3ac931e15d | |||
| dfb538e233 | |||
| 27fab8f9e4 | |||
| 3ff87e0378 | |||
| ce83751152 | |||
| ecc90853e5 | |||
| 0799437dd7 | |||
| 89a098ea44 | |||
| 5a59fe1ed1 | |||
| c7bf9fdc10 | |||
| ec15de8ec7 | |||
| dd6d5b0e7d | |||
| d5165fe013 | |||
| 544f1ad66f | |||
| 744098d641 | |||
| 1fd952ced8 | |||
| 3840dc6cb5 | |||
| 23a77fd90f | |||
| 8d19f844f7 | |||
| 89858a4e25 | |||
| d32ea14495 | |||
| 815f17145c | |||
| 86cbaa2d72 | |||
| c206e48732 | |||
| fb835a438b | |||
| 9f73a137c6 | |||
| 83ecd7110d | |||
| 2dbc82ccc9 | |||
| 2e40eec3b3 | |||
| 3fe5629ca3 | |||
| bde2452bc9 | |||
| d030c80b05 | |||
| f082bfbcc3 | |||
| 0d8313cd7e | |||
| a338ffa9ae | |||
| 809dc4a9ba | |||
| b040f4771c | |||
| b938eddb87 | |||
| 01fe85b95c | |||
| f538673b53 | |||
| 898f7e4de4 | |||
| 570f059615 | |||
| 9a106d1af2 | |||
| 5544acc63e | |||
| 65299378c4 | |||
| d8f1b4c53b | |||
| d2d8c1f5db | |||
| ab385e5afb | |||
| 5d8cd9432e | |||
| fbaf2e7b4b | |||
| 97d83c7038 | |||
| ce83713240 | |||
| 56d3156f8e | |||
| aeb9dcf2c7 | |||
| 9914be0f5b | |||
| 7d0609098c | |||
| ec7a9973c5 | |||
| 06a9fb769e | |||
| d2797238f2 | |||
| b431aabddb | |||
| 0091ffc08b | |||
| e948e313a1 | |||
| a7ae5f0f9f | |||
| 85cce0cd7c | |||
| 3afe7286a2 | |||
| 3d8612ec0e | |||
| 6f1db7e303 | |||
| c1815642db | |||
| a6a168a919 | |||
| d0dbd1e004 | |||
| 4ade8f8797 | |||
| 264195bf16 | |||
| 11cc9a3ff7 | |||
| 37ac7d6e5d | |||
| a1e183a5e1 | |||
| 420785502b | |||
| eed18e5372 | |||
| 0a86ac4a9c | |||
| 264b0a70c2 | |||
| 01d68190ab | |||
| 2d57efd9cb | |||
| da51a6f673 | |||
| f6d4343cc6 | |||
| c6e20aa1a0 | |||
| 809544066f | |||
| 9406e3ef62 | |||
| 6d4c7d76c3 | |||
| f580fdaac2 | |||
| 4be93db383 | |||
| ae4ecfa760 | |||
| f60e5cd7ae | |||
| e097385de9 | |||
| 84d8971aa2 | |||
| 731509a46f | |||
| 7833c94f2e |
+1
-9
@@ -1,9 +1 @@
|
||||
# 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
|
||||
github: [juanpe]
|
||||
|
||||
+3
-8
@@ -1,15 +1,10 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 60
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilStale: 20
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- work in progress
|
||||
onlyLabels:
|
||||
- awaiting user input
|
||||
staleLabel: given up
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
pull_request:
|
||||
branches:
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: xcodebuild
|
||||
run: |
|
||||
xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-iOS -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=12'
|
||||
xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-tvOS -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV,OS=12'
|
||||
xcodebuild -project SkeletonView.xcodeproj -target SkeletonViewExample -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=12'
|
||||
@@ -0,0 +1,13 @@
|
||||
name: CI
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
danger:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Execute danger
|
||||
uses: danger/swift@2.0.3
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,7 +0,0 @@
|
||||
language: objective-c
|
||||
osx_image: xcode10.2
|
||||
script:
|
||||
- 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:
|
||||
+106
-46
@@ -1,146 +1,206 @@
|
||||
# Change Log
|
||||
All notable changes to this project will be documented in this file
|
||||
|
||||
## Next version
|
||||
### Next version
|
||||
|
||||
### Improvements
|
||||
### 📦 [1.8.8](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.8)
|
||||
|
||||
#### 🙌 New
|
||||
* [**304**](https://github.com/Juanpe/SkeletonView/pull/304): French README 🇫🇷 - [@OmarJalil](https://github.com/OmarJalil)
|
||||
|
||||
#### 🔬Improvements
|
||||
* [**311**](https://github.com/Juanpe/SkeletonView/pull/311): Bump json from 2.2.0 to 2.3.1 - [@dependabot](https://github.com/dependabot)
|
||||
|
||||
#### 🩹 Bug fixes
|
||||
* [**286**](https://github.com/Juanpe/SkeletonView/pull/286): Fix issue when WKWebView calls skeletonLayoutSubviews not on the main thread - [@paulanatoleclaudot-betclic](https://github.com/paulanatoleclaudot-betclic)
|
||||
* [**292**](https://github.com/Juanpe/SkeletonView/pull/292): Fix IBInspectable support when using Carthage - [@marisalaneous](https://github.com/marisalaneous)
|
||||
* [**308**](https://github.com/Juanpe/SkeletonView/pull/308): Fix example backgroundColor in DarkMode - [@toshi0383](https://github.com/toshi0383)
|
||||
* [**307**](https://github.com/Juanpe/SkeletonView/pull/307): Prevent incorrect skeletonLayer to be added when updating skeleton - [@wsalim1610](https://github.com/wsalim1610)
|
||||
|
||||
### 📦 [1.8.7](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.7)
|
||||
|
||||
#### 🔬Improvements
|
||||
* [**271**](https://github.com/Juanpe/SkeletonView/pull/271): Add corner radius for skeletonView as IBInspectable (CGFloat) default is 0.0 - [@paulanatoleclaudot-betclic](https://github.com/paulanatoleclaudot-betclic)
|
||||
|
||||
#### 🩹 Bug fixes
|
||||
* [**259**](https://github.com/Juanpe/SkeletonView/issues/259): Prevent isSkeletonActive to be called when isSkeletonable is false - [@wsalim1610](https://github.com/wsalim1610)
|
||||
* [**274**](https://github.com/Juanpe/SkeletonView/pull/274): Fix: hiding skeleton when header and footer views of section would not hide it - [@darkside999](https://github.com/darkside999)
|
||||
* [**273**](https://github.com/Juanpe/SkeletonView/pull/273): Fix: in vertical stack view with center alignment show incorrect position - [@koooootake](https://github.com/koooootake)
|
||||
|
||||
### 📦 [1.8.6](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.6)
|
||||
|
||||
#### 🔬Improvements
|
||||
* [**242**](https://github.com/Juanpe/SkeletonView/pull/242): Offscreen table view layout issue fixed - [@Cacodemon](https://github.com/Cacodemon)
|
||||
* [**261**](https://github.com/Juanpe/SkeletonView/pull/261): Fixes removing skeleton layers from table header footer sections - [@darkside999](https://github.com/darkside999)
|
||||
* [**263**](https://github.com/Juanpe/SkeletonView/pull/263): Feature/set cross dissolve transitions as default - [@Juanpe](https://github.com/Juanpe)
|
||||
* [**264**](https://github.com/Juanpe/SkeletonView/pull/264): not replace original datasource is running XCTests - [@Juanpe](https://github.com/Juanpe)
|
||||
* [**265**](https://github.com/Juanpe/SkeletonView/pull/265): call original traitCollectionDidChange method - [@Juanpe](https://github.com/Juanpe)
|
||||
|
||||
#### 🩹 Bug fixes
|
||||
* [**260**](https://github.com/Juanpe/SkeletonView/issues/260): Don't hide skeleton layers on TableViewHeaderFooterView
|
||||
* [**257**](https://github.com/Juanpe/SkeletonView/issues/257): Unit test problem when using SkeletonView
|
||||
|
||||
|
||||
### 📦 [1.8.3](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.3)
|
||||
|
||||
- Support for iOS 13 dark mode. (thanks @Wilsonator5000)
|
||||
|
||||
### 📦 [1.8.2](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.2)
|
||||
|
||||
#### 🙌 New
|
||||
- Add ability to customize line spacing per label. (thanks @gshahbazian)
|
||||
|
||||
### 📦 [LayoutSkeleton (1.8.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.8.1)
|
||||
|
||||
#### 🔬Improvements
|
||||
- Fix completion call in .none transition style while hide skeletons. (thanks @aadudyrev)
|
||||
|
||||
## [Transitions (1.8)](https://github.com/Juanpe/SkeletonView/releases/tag/1.8)
|
||||
#### 🙌 New
|
||||
- Swizzle `layoutSubviews` method.
|
||||
|
||||
### New
|
||||
#### 🔬Improvements
|
||||
- Fix completion call in .none transition style while hiding skeletons. (thanks @aadudyrev)
|
||||
- Swift format.
|
||||
|
||||
#### 🩹 Bug fixes
|
||||
- Update layout subviews when the original method is called.
|
||||
- Issues: [#88, #149]
|
||||
|
||||
### 📦 [Transitions (1.8)](https://github.com/Juanpe/SkeletonView/releases/tag/1.8)
|
||||
|
||||
#### 🙌 New
|
||||
|
||||
- Adding swift news to mentioned section (thanks @osterbergmarcus).
|
||||
- Create `SkeletonTransitionStyle`. Now, you can animate transition when you show or hide skeletons. (thanks @pontusjacobsson)
|
||||
|
||||
### Improvements
|
||||
#### 🔬Improvements
|
||||
- Refactor some methods.
|
||||
|
||||
### Bug fixes
|
||||
#### 🩹 Bug fixes
|
||||
- Solved issues.
|
||||
[#175](https://github.com/Juanpe/SkeletonView/issues/175) Swift Package Manager version format
|
||||
|
||||
## [Layout update (1.7)](https://github.com/Juanpe/SkeletonView/releases/tag/1.7)
|
||||
### 📦 [Layout update (1.7)](https://github.com/Juanpe/SkeletonView/releases/tag/1.7)
|
||||
|
||||
### New
|
||||
#### 🙌 New
|
||||
|
||||
- Allow updating skeleton layout to recalculate skeleton bounds: `layoutSkeletonIfNeeded`. See the examples to know how to use it. (thanks @eduardbosch)
|
||||
|
||||
### Improvements
|
||||
#### 🔬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)
|
||||
### 📦 [Debug (1.4)](https://github.com/Juanpe/SkeletonView/releases/tag/1.4)
|
||||
|
||||
### New
|
||||
#### 🙌 New
|
||||
|
||||
- Create `skeletonDescription` print a skeleton representation of the view.
|
||||
- Create `SKELETON_DEBUG` environment variable, in order to print the view hierarchy when the skeleton appears.
|
||||
|
||||
### Improvements
|
||||
#### 🔬Improvements
|
||||
- Add two new methods to `SkeletonFlowDelegate` protocol. Now you can know when the skeleton did show and when it did hide.
|
||||
- `Recursive` protocol
|
||||
|
||||
### Bug fixes
|
||||
#### 🩹 Bug fixes
|
||||
- Solved issue [#86](https://github.com/Juanpe/SkeletonView/issues/86) (thanks @reececomo)
|
||||
|
||||
## [Custom defaults (1.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.3)
|
||||
### 📦 [Custom defaults (1.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.3)
|
||||
|
||||
### New
|
||||
#### 🙌 New
|
||||
|
||||
- Default values customizables. Now you can set the default values of Skeleton appearance.(thanks @reececomo)
|
||||
- issues: [[#50](https://github.com/Juanpe/SkeletonView/issues/50), [#83](https://github.com/Juanpe/SkeletonView/issues/83)]
|
||||
|
||||
### Bug fixes
|
||||
#### 🩹 Bug fixes
|
||||
- Solved issue [#41](https://github.com/Juanpe/SkeletonView/issues/41). Now, Skeleton works if UICollectionView cell's Nib is registered in code. (thanks @kjoneandrei)
|
||||
|
||||
## [Typo (1.2.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.3)
|
||||
### 📦 [Typo (1.2.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.3)
|
||||
|
||||
### Fixes
|
||||
#### Fixes
|
||||
|
||||
- Fix typo in `SkeletonTableViewDataSource` protocol
|
||||
|
||||
### Improvements
|
||||
#### 🔬Improvements
|
||||
|
||||
- Now it takes in account the `UIStackView` to calculate the `SkeletonLayer` bounds (thanks @giantramen)
|
||||
|
||||
## [New face (1.2.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.2)
|
||||
### 📦 [New face (1.2.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.2)
|
||||
|
||||
### New
|
||||
#### 🙌 New
|
||||
|
||||
- Rebranding
|
||||
|
||||
### Bug fixes
|
||||
#### 🩹 Bug fixes
|
||||
- Solved issue [#23](https://github.com/Juanpe/SkeletonView/issues/23). Problem with UIStackView. (thanks @giantramen)
|
||||
|
||||
## [State (1.2.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.1)
|
||||
### 📦 [State (1.2.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.1)
|
||||
|
||||
### New
|
||||
#### 🙌 New
|
||||
|
||||
- You can set the corner radius multiline elements (thanks @B4V4-G)
|
||||
- Save view state when skeleton appears and recovery when it is hidden (@juanpe)
|
||||
|
||||
### Bug fixes
|
||||
#### 🩹 Bug fixes
|
||||
- Solved issue [#51](https://github.com/Juanpe/SkeletonView/issues/51). Support inspectable properties when using Carthage. (thanks @eduardbosch)
|
||||
|
||||
## [On TV (1.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2)
|
||||
### 📦 [On TV (1.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2)
|
||||
|
||||
### New
|
||||
#### 🙌 New
|
||||
- Now ```SkeletonView``` is **tvOS** compatible! 🎉. (thanks @mihai8804858)
|
||||
|
||||
### Bug fixes
|
||||
#### 🩹 Bug fixes
|
||||
- Solved issue [#46](https://github.com/Juanpe/SkeletonView/issues/46). It crashes the application when tap on it, didSelect called and crash.
|
||||
|
||||
|
||||
## [Hotfix (1.1.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.1.1)
|
||||
### 📦 [Hotfix (1.1.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.1.1)
|
||||
|
||||
### Bug fixes
|
||||
#### 🩹 Bug fixes
|
||||
- Now yes, solved issue [#39](https://github.com/Juanpe/SkeletonView/issues/39)
|
||||
|
||||
## [Needed (1.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.1)
|
||||
### 📦 [Needed (1.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.1)
|
||||
|
||||
### New
|
||||
#### 🙌 New
|
||||
- Now ```SkeletonView```supports **UICollectionViews**! 🎉. (thanks @Renatdz)
|
||||
|
||||
### Bug fixes
|
||||
#### 🩹 Bug fixes
|
||||
- Solved issue [#39](https://github.com/Juanpe/SkeletonView/issues/39). Gradient animation did not work when app becomes active.
|
||||
|
||||
|
||||
## [Resizable (1.0.5)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.5)
|
||||
### 📦 [Resizable (1.0.5)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.5)
|
||||
|
||||
### New
|
||||
#### 🙌 New
|
||||
- Now you can use table views with resizable cells.
|
||||
|
||||
### Bug fixes
|
||||
#### 🩹 Bug fixes
|
||||
- Solved issues.
|
||||
[#17](https://github.com/Juanpe/SkeletonView/issues/17),
|
||||
[#30](https://github.com/Juanpe/SkeletonView/issues/30),
|
||||
[#34](https://github.com/Juanpe/SkeletonView/issues/34).
|
||||
|
||||
## [Filled or not (1.0.4)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.4)
|
||||
### 📦 [Filled or not (1.0.4)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.4)
|
||||
|
||||
### New
|
||||
#### 🙌 New
|
||||
- You can set the filling percent of the last line in multiline elements (thanks @jontelang!)
|
||||
|
||||
### Bug fixes
|
||||
#### 🩹 Bug fixes
|
||||
- Solved issue [#14](https://github.com/Juanpe/SkeletonView/issues/14). You could edit text views with skeleton active.
|
||||
|
||||
## [In all directions (1.0.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.3)
|
||||
### 📦 [In all directions (1.0.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.3)
|
||||
|
||||
### New
|
||||
#### 🙌 New
|
||||
- Create ```SkeletonAnimationBuilder```, to facilitate the creation of layer animations.
|
||||
```GradientDirection``` enum.
|
||||
|
||||
## [Retro (1.0.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.2)
|
||||
### 📦 [Retro (1.0.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.2)
|
||||
|
||||
### New
|
||||
#### 🙌 New
|
||||
- Change some private keywords, to be Swift 3 compatible
|
||||
|
||||
## [Early bird bug (1.0.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.2)
|
||||
### 📦 [Early bird bug (1.0.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0.2)
|
||||
|
||||
### Bug fixes
|
||||
#### 🩹 Bug fixes
|
||||
- It was not removing the skeleton layer
|
||||
|
||||
## [Starter (1.0)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0)
|
||||
### 📦 [Starter (1.0)](https://github.com/Juanpe/SkeletonView/releases/tag/1.0)
|
||||
|
||||
- First release
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import Danger
|
||||
|
||||
let danger = Danger()
|
||||
let github = danger.github
|
||||
|
||||
// Changelog entries are required for changes to library files.
|
||||
let allSourceFiles = danger.git.modifiedFiles + danger.git.createdFiles
|
||||
let noChangelogEntry = !allSourceFiles.contains("CHANGELOG.md")
|
||||
let sourceChanges = allSourceFiles.contains { $0.hasPrefix("Sources") }
|
||||
let isNotTrivial = !danger.github.pullRequest.title.contains("#trivial")
|
||||
|
||||
if isNotTrivial && noChangelogEntry && sourceChanges {
|
||||
warn("Any changes to library code should be reflected in the Changelog.")
|
||||
}
|
||||
|
||||
// Make it more obvious that a PR is a work in progress and shouldn't be merged yet
|
||||
if danger.github.pullRequest.title.contains("WIP") {
|
||||
warn("PR is classed as Work in Progress")
|
||||
}
|
||||
|
||||
// Warn, asking to update all README files if only English README are updated
|
||||
let enReameModified = danger.git.modifiedFiles.contains { $0.contains("README.md") }
|
||||
let zhReameModified = danger.git.modifiedFiles.contains { $0.contains("README_zh.md") }
|
||||
let koReameModified = danger.git.modifiedFiles.contains { $0.contains("README_ko.md") }
|
||||
let ptBrReameModified = danger.git.modifiedFiles.contains { $0.contains("README_pt-br.md") }
|
||||
let otherLanguagesReadmeHaveBeenModified = zhReameModified && koReameModified && ptBrReameModified
|
||||
|
||||
if (enReameModified && !otherLanguagesReadmeHaveBeenModified) {
|
||||
warn("Consider **also** updating the README for other languages.")
|
||||
}
|
||||
|
||||
// Warn when there is a big PR
|
||||
if (danger.github.pullRequest.additions ?? 0) > 500 {
|
||||
warn("Big PR, try to keep changes smaller if you can")
|
||||
}
|
||||
|
||||
// Added (or removed) library files need to be added (or removed) from the
|
||||
// Xcode project to avoid breaking things.
|
||||
let addedSwiftLibraryFiles = danger.git.createdFiles.contains { $0.fileType == .swift && $0.hasPrefix("Sources") }
|
||||
let deletedSwiftLibraryFiles = danger.git.deletedFiles.contains { $0.fileType == .swift && $0.hasPrefix("Sources") }
|
||||
let modifiedXcodeProject = danger.git.modifiedFiles.contains { $0.contains(".xcodeproj") }
|
||||
if (addedSwiftLibraryFiles || deletedSwiftLibraryFiles) && !modifiedXcodeProject {
|
||||
fail("Added or removed files require the Xcode project to be updated.")
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" 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>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="irH-dz-xqL">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -19,7 +17,7 @@
|
||||
<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"/>
|
||||
<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"/>
|
||||
@@ -66,7 +64,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="HKL-L0-T2w">
|
||||
<rect key="frame" x="0.0" y="263" width="375" height="244"/>
|
||||
<rect key="frame" x="0.0" y="243" 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"/>
|
||||
@@ -83,7 +81,7 @@
|
||||
<rect key="frame" x="0.0" y="507" width="375" height="160"/>
|
||||
<subviews>
|
||||
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="fMR-vj-7de">
|
||||
<rect key="frame" x="20" y="22.5" width="135" height="29"/>
|
||||
<rect key="frame" x="20" y="21" width="145" height="32"/>
|
||||
<segments>
|
||||
<segment title="Solid"/>
|
||||
<segment title="Gradient"/>
|
||||
@@ -135,14 +133,14 @@
|
||||
<action selector="showOrHideSkeleton:" destination="irH-dz-xqL" eventType="touchUpInside" id="lHc-k2-OgV"/>
|
||||
</connections>
|
||||
</button>
|
||||
<stepper opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" maximumValue="5" stepValue="0.25" translatesAutoresizingMaskIntoConstraints="NO" id="3mz-9M-e7Q">
|
||||
<rect key="frame" x="263" y="126" width="94" height="29"/>
|
||||
<stepper opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.25" maximumValue="5" stepValue="0.25" translatesAutoresizingMaskIntoConstraints="NO" id="3mz-9M-e7Q">
|
||||
<rect key="frame" x="263" y="123" width="94" height="32"/>
|
||||
<connections>
|
||||
<action selector="transitionDurationStepperAction:" destination="irH-dz-xqL" eventType="valueChanged" id="Ll0-Pr-d0V"/>
|
||||
</connections>
|
||||
</stepper>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Fade Duration: 0 sec" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5gN-Jz-44y">
|
||||
<rect key="frame" x="113.5" y="131.5" width="141.5" height="18"/>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Fade Duration: 0.25 sec" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5gN-Jz-44y">
|
||||
<rect key="frame" x="92.5" y="130" width="162.5" height="18"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@@ -188,6 +186,9 @@
|
||||
<constraint firstItem="HKL-L0-T2w" firstAttribute="leading" secondItem="2Gq-Y8-1TU" secondAttribute="leading" id="iIq-cx-paX"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="2Gq-Y8-1TU"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="avatarImage" destination="Ql9-Jy-aWM" id="VoL-by-ygR"/>
|
||||
|
||||
@@ -45,7 +45,7 @@ class ViewController: UIViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.isSkeletonable = true
|
||||
transitionDurationStepper.value = 0.25
|
||||
collectionView.prepareSkeleton(completion: { done in
|
||||
self.view.showAnimatedSkeleton()
|
||||
})
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Va7-1y-Tel">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Va7-1y-Tel">
|
||||
<device id="retina5_9" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15510"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -13,19 +13,19 @@
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="SkeletonViewExample" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="F9K-jU-100" userLabel="ContainerView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="243"/>
|
||||
<rect key="frame" x="0.0" y="44" width="375" height="243"/>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="e9V-mk-xH0">
|
||||
<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="gF5-G1-lKI"/>
|
||||
</constraints>
|
||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </string>
|
||||
<color key="textColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
@@ -50,7 +50,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="nMj-pU-5wJ" firstAttribute="centerX" secondItem="F9K-jU-100" secondAttribute="centerX" id="9X4-2r-AKx"/>
|
||||
<constraint firstItem="e9V-mk-xH0" firstAttribute="leading" secondItem="F9K-jU-100" secondAttribute="leading" constant="45" id="HvQ-HY-zYU"/>
|
||||
@@ -65,8 +65,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="UCB-SP-lQk">
|
||||
<rect key="frame" x="0.0" y="243" width="375" height="215"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<rect key="frame" x="0.0" y="287" width="375" height="282"/>
|
||||
<color key="separatorColor" red="0.1061807256" green="0.84678786989999999" blue="0.031482450150000001" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="CellIdentifier" rowHeight="120" id="2dN-Bd-tdy" customClass="Cell" customModule="SkeletonViewExample" customModuleProvider="target">
|
||||
@@ -87,12 +86,12 @@
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VhU-1t-AaI" userLabel="Label">
|
||||
<rect key="frame" x="118" y="29" width="237" height="20.5"/>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VhU-1t-AaI" userLabel="Label">
|
||||
<rect key="frame" x="118" y="29" width="237" height="18"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="lessThanOrEqual" constant="71" id="HRL-cI-ieC"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
@@ -108,6 +107,7 @@
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="oiE-tt-nc2" firstAttribute="leading" secondItem="7IN-F3-Mr6" secondAttribute="leadingMargin" id="1be-ak-AH1"/>
|
||||
<constraint firstAttribute="bottom" secondItem="oiE-tt-nc2" secondAttribute="bottom" constant="20" id="CKt-oA-eBI"/>
|
||||
<constraint firstItem="oiE-tt-nc2" firstAttribute="top" secondItem="7IN-F3-Mr6" secondAttribute="topMargin" constant="7" id="EKn-ST-LDX"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="VhU-1t-AaI" secondAttribute="trailing" constant="5" id="I7C-Bq-mfK"/>
|
||||
<constraint firstItem="VhU-1t-AaI" firstAttribute="leading" secondItem="oiE-tt-nc2" secondAttribute="trailing" constant="21" id="Ojr-Kz-1k6"/>
|
||||
@@ -131,10 +131,11 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="BYZ-38-t0r" id="Hxi-nC-gbY"/>
|
||||
<outlet property="delegate" destination="BYZ-38-t0r" id="Z10-Nx-iGb"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XgY-1a-UGc">
|
||||
<rect key="frame" x="0.0" y="458" width="375" height="160"/>
|
||||
<rect key="frame" x="0.0" y="569" width="375" height="160"/>
|
||||
<subviews>
|
||||
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="xOL-Sq-r4i">
|
||||
<rect key="frame" x="20" y="23" width="145" height="32"/>
|
||||
@@ -159,7 +160,7 @@
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Color" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7CF-rV-pK2">
|
||||
<rect key="frame" x="20" y="73.5" width="90" height="21"/>
|
||||
<rect key="frame" x="20" y="73.666666666666629" width="90" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@@ -189,20 +190,20 @@
|
||||
<action selector="showOrHideSkeleton:" destination="BYZ-38-t0r" eventType="touchUpInside" id="Ma1-WX-Dzy"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Fade Duration: 0 sec" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mrw-PM-jJJ">
|
||||
<rect key="frame" x="113.5" y="130" width="141.5" height="18"/>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Fade Duration: 0.25 sec" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mrw-PM-jJJ">
|
||||
<rect key="frame" x="92.333333333333329" y="130" width="162.66666666666669" height="18"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stepper opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" maximumValue="5" stepValue="0.25" translatesAutoresizingMaskIntoConstraints="NO" id="l4N-LL-ZrJ">
|
||||
<stepper opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.25" maximumValue="5" stepValue="0.25" translatesAutoresizingMaskIntoConstraints="NO" id="l4N-LL-ZrJ">
|
||||
<rect key="frame" x="263" y="123" width="94" height="32"/>
|
||||
<connections>
|
||||
<action selector="transitionDurationStepperAction:" destination="BYZ-38-t0r" eventType="valueChanged" id="jPN-df-fNs"/>
|
||||
</connections>
|
||||
</stepper>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="l4N-LL-ZrJ" firstAttribute="leading" secondItem="mrw-PM-jJJ" secondAttribute="trailing" constant="8" id="5iU-dO-qVc"/>
|
||||
<constraint firstItem="mrw-PM-jJJ" firstAttribute="centerY" secondItem="l4N-LL-ZrJ" secondAttribute="centerY" id="9OM-mx-4Jo"/>
|
||||
@@ -227,7 +228,7 @@
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="F9K-jU-100" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="1es-nY-bd3"/>
|
||||
<constraint firstItem="F9K-jU-100" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="A3E-iv-1qp"/>
|
||||
@@ -242,7 +243,7 @@
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="NO"/>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<tabBarItem key="tabBarItem" title="Item" id="wQY-ap-3n3"/>
|
||||
@@ -260,16 +261,16 @@
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-2682" y="340"/>
|
||||
<point key="canvasLocation" x="-2682.4000000000001" y="339.90147783251234"/>
|
||||
</scene>
|
||||
<!--Item-->
|
||||
<scene sceneID="Cfc-AT-AS1">
|
||||
<objects>
|
||||
<viewController id="dv8-ph-Ehg" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Jwx-gI-Qod">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<viewLayoutGuide key="safeArea" id="Ao1-hk-zrH"/>
|
||||
</view>
|
||||
<tabBarItem key="tabBarItem" title="Item" id="iKp-9S-aib"/>
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
let colors = [(UIColor.turquoise,"turquoise"), (UIColor.emerald,"emerald"), (UIColor.peterRiver,"peterRiver"), (UIColor.amethyst,"amethyst"),(UIColor.wetAsphalt,"wetAsphalt"), (UIColor.nephritis,"nephritis"), (UIColor.belizeHole,"belizeHole"), (UIColor.wisteria,"wisteria"), (UIColor.midnightBlue,"midnightBlue"), (UIColor.sunFlower,"sunFlower"), (UIColor.carrot,"carrot"), (UIColor.alizarin,"alizarin"),(UIColor.clouds,"clouds"), (UIColor.concrete,"concrete"), (UIColor.flatOrange,"flatOrange"), (UIColor.pumpkin,"pumpkin"), (UIColor.pomegranate,"pomegranate"), (UIColor.silver,"silver"), (UIColor.asbestos,"asbestos")]
|
||||
let colors = [(UIColor.skeletonDefault,"skeletonDefault"),(UIColor.turquoise,"turquoise"), (UIColor.emerald,"emerald"), (UIColor.peterRiver,"peterRiver"), (UIColor.amethyst,"amethyst"),(UIColor.wetAsphalt,"wetAsphalt"), (UIColor.nephritis,"nephritis"), (UIColor.belizeHole,"belizeHole"), (UIColor.wisteria,"wisteria"), (UIColor.midnightBlue,"midnightBlue"), (UIColor.sunFlower,"sunFlower"), (UIColor.carrot,"carrot"), (UIColor.alizarin,"alizarin"),(UIColor.clouds,"clouds"), (UIColor.concrete,"concrete"), (UIColor.flatOrange,"flatOrange"), (UIColor.pumpkin,"pumpkin"), (UIColor.pomegranate,"pomegranate"), (UIColor.silver,"silver"), (UIColor.asbestos,"asbestos")]
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright © 2020 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
class HeaderFooterSection: UITableViewHeaderFooterView {
|
||||
|
||||
lazy var titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
|
||||
label.text = " "
|
||||
label.isSkeletonable = true
|
||||
label.linesCornerRadius = 5
|
||||
|
||||
return label
|
||||
}()
|
||||
|
||||
override init(reuseIdentifier: String?) {
|
||||
super.init(reuseIdentifier: reuseIdentifier)
|
||||
|
||||
isSkeletonable = true
|
||||
|
||||
contentView.addSubview(titleLabel)
|
||||
|
||||
titleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
|
||||
titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
|
||||
titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
|
||||
titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10)
|
||||
])
|
||||
|
||||
backgroundView = UIView()
|
||||
if #available(iOS 13.0, *) {
|
||||
backgroundView?.backgroundColor = .systemBackground
|
||||
} else {
|
||||
backgroundView?.backgroundColor = .white
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,13 @@ class ViewController: UIViewController {
|
||||
@IBOutlet weak var tableview: UITableView! {
|
||||
didSet {
|
||||
tableview.rowHeight = UITableView.automaticDimension
|
||||
tableview.sectionHeaderHeight = UITableView.automaticDimension
|
||||
tableview.sectionFooterHeight = UITableView.automaticDimension
|
||||
tableview.estimatedRowHeight = 120.0
|
||||
tableview.estimatedSectionFooterHeight = 20.0
|
||||
tableview.estimatedSectionHeaderHeight = 20.0
|
||||
tableview.register(HeaderFooterSection.self, forHeaderFooterViewReuseIdentifier: "HeaderIdentifier")
|
||||
tableview.register(HeaderFooterSection.self, forHeaderFooterViewReuseIdentifier: "FooterIdentifier")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +51,7 @@ class ViewController: UIViewController {
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
tableview.isSkeletonable = true
|
||||
transitionDurationStepper.value = 0.25
|
||||
view.showAnimatedSkeleton()
|
||||
}
|
||||
|
||||
@@ -178,3 +185,26 @@ extension ViewController: SkeletonTableViewDataSource {
|
||||
}
|
||||
}
|
||||
|
||||
extension ViewController: SkeletonTableViewDelegate {
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier? {
|
||||
return "HeaderIdentifier"
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
let header = tableView
|
||||
.dequeueReusableHeaderFooterView(withIdentifier: "HeaderIdentifier") as! HeaderFooterSection
|
||||
header.titleLabel.text = "header => \(section)"
|
||||
return header
|
||||
}
|
||||
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier? {
|
||||
return "FooterIdentifier"
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
|
||||
let footer = tableView
|
||||
.dequeueReusableHeaderFooterView(withIdentifier: "FooterIdentifier") as! HeaderFooterSection
|
||||
footer.titleLabel.text = "footer => \(section)"
|
||||
return footer
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -58,7 +58,7 @@ GEM
|
||||
dotenv (2.7.4)
|
||||
emoji_regex (1.0.1)
|
||||
escape (0.0.4)
|
||||
excon (0.65.0)
|
||||
excon (0.71.0)
|
||||
faraday (0.15.4)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-cookie_jar (0.0.6)
|
||||
@@ -137,7 +137,7 @@ GEM
|
||||
httpclient (2.8.3)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
json (2.2.0)
|
||||
json (2.3.1)
|
||||
jwt (2.1.0)
|
||||
memoist (0.16.0)
|
||||
mime-types (3.2.2)
|
||||
@@ -163,7 +163,7 @@ GEM
|
||||
retriable (3.1.2)
|
||||
rouge (2.0.7)
|
||||
ruby-macho (1.4.0)
|
||||
rubyzip (1.2.3)
|
||||
rubyzip (1.3.0)
|
||||
security (0.1.3)
|
||||
signet (0.11.0)
|
||||
addressable (~> 2.3)
|
||||
|
||||
@@ -1,43 +1,30 @@
|
||||

|
||||
|
||||
<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 href="https://github.com/Juanpe/SkeletonView/workflows/build">
|
||||
<img src="https://github.com/Juanpe/SkeletonView/workflows/build/badge.svg">
|
||||
</a>
|
||||
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
|
||||
<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>
|
||||
<img src="http://img.shields.io/badge/dependency%20manager-swiftpm%2Bcocoapods%2Bcarthage-green" />
|
||||
<img src="https://img.shields.io/badge/platforms-ios%2Btvos-green" />
|
||||
<a href="https://badge.bow-swift.io/recipe?name=SkeletonView&description=An%20elegant%20way%20to%20show%20users%20that%20something%20is%20happening%20and%20also%20prepare%20them%20to%20which%20contents%20he%20is%20waiting&url=https://github.com/juanpe/skeletonview&owner=Juanpe&avatar=https://avatars0.githubusercontent.com/u/1409041?v=4&tag=1.8.7"><img src="https://raw.githubusercontent.com/bow-swift/bow-art/master/badges/nef-playgrounds-badge.svg" alt="SkeletonView Playground" style="height:20px"></a>
|
||||
<br/>
|
||||
<a href="https://twitter.com/JuanpeCatalan">
|
||||
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
|
||||
</a>
|
||||
<br/>
|
||||
<a href="https://gitter.im/SkeletonView/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge">
|
||||
<img src="https://badges.gitter.im/SkeletonView/community.svg?style=flat" />
|
||||
</a>
|
||||
<a href="https://twitter.com/intent/tweet?text=Wow%20This%20library%20is%20awesome:&url=https%3A%2F%2Fgithub.com%2FJuanpe%2FSkeletonView">
|
||||
<img src="https://img.shields.io/twitter/url/https/github.com/Juanpe/SkeletonView.svg?style=social" alt="License" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
🌎 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)
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
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.
|
||||
**🌎 README is available in other languages: [🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) [🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) [🇰🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_ko.md) [🇫🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_fr.md)**
|
||||
|
||||
```SkeletonView``` has been conceived to address this need, an elegant way to show users that something is happening and also prepare them to which contents he is waiting.
|
||||
Today almost all apps have async processes, such as API requests, long running processes, etc. While the processes are working, usually developers place a loading view to show users that something is going on.
|
||||
|
||||
```SkeletonView``` has been conceived to address this need, an elegant way to show users that something is happening and also prepare them for which contents are waiting.
|
||||
|
||||
Enjoy it! 🙂
|
||||
|
||||
@@ -59,7 +46,7 @@ Enjoy it! 🙂
|
||||
* [Documentation](#-documentation)
|
||||
* [Supported OS & SDK Versions](#-supported-os--sdk-versions)
|
||||
* [Next steps](#-next-steps)
|
||||
* [Contributing](#-contributing)
|
||||
* [Contributing](#%EF%B8%8F-contributing)
|
||||
* [Mentions](#-mentions)
|
||||
* [Author](#-author)
|
||||
* [License](#-license)
|
||||
@@ -77,7 +64,9 @@ Enjoy it! 🙂
|
||||
|
||||
## 🎬 Guides
|
||||
|
||||
[<img src="Assets/thumb_getting_started.png">](https://youtu.be/75kgOhWsPNA)
|
||||
| [](https://youtu.be/75kgOhWsPNA)|[](https://youtu.be/MVCiM_VdxVA)|[](https://youtu.be/Qq3Evspeea8)|[](https://youtu.be/ZOoPtBwDRT0)
|
||||
|:---: | :---: |:---: | :---:
|
||||
|[**SkeletonView Guides - Getting started**](https://youtu.be/75kgOhWsPNA)|[**How to Create Loading View with Skeleton View in Swift 5.2**](https://youtu.be/MVCiM_VdxVA) <br/> by iKh4ever Studio|[**Create Skeleton Loading View in App (Swift 5) - Xcode 11, 2020**](https://youtu.be/Qq3Evspeea8) <br/> by iOS Academy| [**Add An Elegant Loading Animation in Swift***](https://youtu.be/ZOoPtBwDRT0) <br/> by Gary Tokman
|
||||
|
||||
## 📲 Installation
|
||||
|
||||
@@ -202,7 +191,7 @@ You can change the skeleton configuration at any time like its colour, animation
|
||||
|
||||
### 🌿 Collections
|
||||
|
||||
Now, ```SkeletonView``` is compatible with ```UITableView``` and ```UICollectionView```.
|
||||
```SkeletonView``` is compatible with ```UITableView``` and ```UICollectionView```.
|
||||
|
||||
#### UITableView
|
||||
|
||||
@@ -213,6 +202,8 @@ public protocol SkeletonTableViewDataSource: UITableViewDataSource {
|
||||
func numSections(in collectionSkeletonView: UITableView) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier?
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier?
|
||||
}
|
||||
```
|
||||
As you can see, this protocol inherits from ```UITableViewDataSource```, so you can replace this protocol with the skeleton protocol.
|
||||
@@ -230,6 +221,16 @@ func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection s
|
||||
// It calculates how many cells need to populate whole tableview
|
||||
```
|
||||
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier?
|
||||
// Default: nil
|
||||
```
|
||||
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier?
|
||||
// Default: nil
|
||||
```
|
||||
|
||||
There is only one method you need to implement to let Skeleton know the cell identifier. This method doesn't have default implementation:
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
@@ -326,9 +327,9 @@ Besides, ```SkeletonView``` features 20 flat colors 🤙🏼
|
||||
|
||||
Default values:
|
||||
- **tintColor**: UIColor
|
||||
- *default: .clouds*
|
||||
- *default: `.skeletonDefault` (same as `.clouds` but adaptive to dark mode)*
|
||||
- **gradient**: SkeletonGradient
|
||||
- *default: SkeletonGradient(baseColor: .clouds)*
|
||||
- *default: `SkeletonGradient(baseColor: .skeletonDefault)`*
|
||||
- **multilineHeight**: CGFloat
|
||||
- *default: 15*
|
||||
- **multilineSpacing**: CGFloat
|
||||
@@ -337,6 +338,8 @@ Default values:
|
||||
- *default: 70*
|
||||
- **multilineCornerRadius**: Int
|
||||
- *default: 0*
|
||||
- **skeletonCornerRadius**: CGFloat (IBInspectable) (Make your skeleton view with corner)
|
||||
- *default: 0*
|
||||
|
||||
To get these default values you can use `SkeletonAppearance.default`. Using this property you can set the values as well:
|
||||
```Swift
|
||||
@@ -344,6 +347,12 @@ SkeletonAppearance.default.multilineHeight = 20
|
||||
SkeletonAppearance.default.tintColor = .green
|
||||
```
|
||||
|
||||
You can also specifiy these line appearance properties on a per-label basis:
|
||||
- **lastLineFillPercent**: Int
|
||||
- **linesCornerRadius**: Int
|
||||
- **skeletonLineSpacing**: CGFloat
|
||||
- **skeletonPaddingInsets**: UIEdgeInsets
|
||||
|
||||
|
||||
### 🤓 Custom animations
|
||||
|
||||
@@ -410,6 +419,8 @@ view.hideSkeleton(transition: .crossDissolve(0.25)) //Hide skeleton cross di
|
||||
|
||||
```
|
||||
|
||||
The default value is `crossDissolve(0.25)`
|
||||
|
||||
**Preview**
|
||||
|
||||
<table>
|
||||
|
||||
+562
@@ -0,0 +1,562 @@
|
||||

|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/Juanpe/SkeletonView/workflows/build">
|
||||
<img src="https://github.com/Juanpe/SkeletonView/workflows/build/badge.svg">
|
||||
</a>
|
||||
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
|
||||
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
|
||||
<img src="http://img.shields.io/badge/dependency%20manager-swiftpm%2Bcocoapods%2Bcarthage-green" />
|
||||
<img src="https://img.shields.io/badge/platforms-ios%2Btvos-green" />
|
||||
<a href="https://badge.bow-swift.io/recipe?name=SkeletonView&description=An%20elegant%20way%20to%20show%20users%20that%20something%20is%20happening%20and%20also%20prepare%20them%20to%20which%20contents%20he%20is%20waiting&url=https://github.com/juanpe/skeletonview&owner=Juanpe&avatar=https://avatars0.githubusercontent.com/u/1409041?v=4&tag=1.8.7"><img src="https://raw.githubusercontent.com/bow-swift/bow-art/master/badges/nef-playgrounds-badge.svg" alt="SkeletonView Playground" style="height:20px"></a>
|
||||
<br/>
|
||||
<a href="https://twitter.com/JuanpeCatalan">
|
||||
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
|
||||
</a>
|
||||
<a href="https://gitter.im/SkeletonView/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge">
|
||||
<img src="https://badges.gitter.im/SkeletonView/community.svg?style=flat" />
|
||||
</a>
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">
|
||||
<img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="PayPal" />
|
||||
<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="Licence" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
🌎 Traductions : </br>
|
||||
[Original](https://github.com/Juanpe/SkeletonView) </br>
|
||||
[🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) [@WhatsXie](https://twitter.com/WhatsXie) </br>
|
||||
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) [@brunomunizaf](https://twitter.com/brunomuniz_af) </br>
|
||||
[🇰🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_ko.md) [@techinpark](https://twitter.com/techinpark) </br>
|
||||
[🇫🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_fr.md) [@OmarJalil](https://github.com/OmarJalil)
|
||||
|
||||
Aujourd'hui, presque toutes les applications ont des processus asynchrones, tels que les requêtes Api, les processus de longue durée, etc. Et pendant que les processus fonctionnent, les développeurs affichent généralement une vue de chargement pour montrer aux utilisateurs que quelque chose se passe.
|
||||
|
||||
"SkeletonView" a été créé pour répondre à ce besoin, une manière élégante de montrer aux utilisateurs que quelque chose se passe et aussi de les préparer aux contenus qu'ils attendent.
|
||||
|
||||
Profitez-en! 🙂
|
||||
|
||||
* [Caractéristiques](#-caractéristiques)
|
||||
* [Guides](#-guides)
|
||||
* [Installation](#-installation)
|
||||
* [Cocoapods](#utilisation-de-cocoapods)
|
||||
* [Carthage](#utilisation-de-carthage)
|
||||
* [SPM](#utilisation-du-gestionnaire-de-paquets-Swift)
|
||||
* [Comment utiliser](#-mode-d'emploi)
|
||||
* [Collections](#-collections)
|
||||
* [Texte Multiligne](#-texte-multiligne)
|
||||
* [Couleurs personnalisées](#-couleurs-personnalisées)
|
||||
* [Présentation](#-présentation)
|
||||
* [Animations personnalisées](#-animations-personnalisées)
|
||||
* [Transitions](#-transitions)
|
||||
* [Hiérarchie](#-hiérarchie)
|
||||
* [Débugger](#-débugger)
|
||||
* [Documentation](#-documentation)
|
||||
* [Versions OS & SDK supportées](#-versions-os-et-sdk-supportées)
|
||||
* [Prochaines étapes](#-prochaines-étapes)
|
||||
* [Contribuer](#-contribuer)
|
||||
* [Mentions](#-mentions)
|
||||
* [Auteur](#-auteur)
|
||||
* [Licence](#-licence)
|
||||
|
||||
## 🌟 Caractéristiques
|
||||
|
||||
- [x] Facile à utiliser
|
||||
- [x] Tous les UIViews sont squelettisables
|
||||
- [x] Entièrement personnalisable
|
||||
- [x] Universel (iPhone et iPad)
|
||||
- [x] Interface Builder friendly
|
||||
- [x] Syntaxe Swift simple
|
||||
- [x] Base de code légère et lisible
|
||||
|
||||
## 🎬 Guides
|
||||
|
||||
[<img src="Assets/thumb_getting_started.png">](https://youtu.be/75kgOhWsPNA)
|
||||
|
||||
## 📲 Installation
|
||||
|
||||
#### Utilisation de [CocoaPods](https://cocoapods.org)
|
||||
|
||||
Editez votre "podfile" et spécifiez la dépendance:
|
||||
|
||||
```ruby
|
||||
pod "SkeletonView"
|
||||
```
|
||||
|
||||
#### Utilisation de [Carthage](https://github.com/carthage)
|
||||
|
||||
Modifiez votre "Cartfile" et spécifiez la dépendance:
|
||||
|
||||
```bash
|
||||
github "Juanpe/SkeletonView"
|
||||
```
|
||||
|
||||
#### Utilisation du [gestionnaire de paquets Swift](https://github.com/apple/swift-package-manager)
|
||||
|
||||
Une fois que vous avez configuré votre paquet Swift, ajouter `SkeletonView` comme dépendance est aussi facile que de l'ajouter à la valeur des `dépendances` de votre `Package.swift`.
|
||||
|
||||
```swift
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/Juanpe/SkeletonView.git", from: "1.7.0")
|
||||
]
|
||||
```
|
||||
|
||||
## 🐒 Mode d'emploi
|
||||
|
||||
Seulement **3** étapes nécessaires pour utiliser `SkeletonView` :
|
||||
|
||||
**1.** Importer SkeletonView au bon endroit.
|
||||
```swift
|
||||
import SkeletonView
|
||||
```
|
||||
|
||||
**2.** Maintenant, définissez les vues qui seront `squelettisables`. Vous y arrivez de deux façons :
|
||||
|
||||
**En utilisant du code:**
|
||||
```swift
|
||||
avatarImageView.isSkeletonable = true
|
||||
```
|
||||
**Utilisation des IB/Storyboards:**
|
||||
|
||||

|
||||
|
||||
**Une fois que vous avez défini les vues, vous pouvez montrer le **squelette**. Pour le faire, vous avez quatre choix :
|
||||
|
||||
```swift
|
||||
(1) view.showSkeleton() // Solide
|
||||
(2) view.showGradientSkeleton() // Gradient
|
||||
(3) view.showAnimatedSkeleton() // Solide animé
|
||||
(4) view.showAnimatedGradientSkeleton() // Gradient animé
|
||||
```
|
||||
|
||||
**Preview**
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="25%">
|
||||
<center>Solide</center>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<center>Gradient</center>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<center>Animé Solide</center>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<center>Animé Gradient</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>
|
||||
|
||||
> **IMPORTANT!**
|
||||
>>```SkeletonView``` est récursif, donc si vous voulez montrer le squelette dans toutes les vues squelettables, il vous suffit d'appeler la méthode `show` dans la vue principale du conteneur. Par exemple, avec UIViewControllers
|
||||
|
||||
### Extra
|
||||
|
||||
#### Mise en page des vues squelettes
|
||||
|
||||
Il arrive que le squelette ne corresponde pas à votre mise en page parce que les limites de la vue parent ont changé. ~Par exemple, la rotation de l'appareil.~
|
||||
|
||||
Vous pouvez relayer les vues du squelette de cette manière :
|
||||
|
||||
```swift
|
||||
override func viewDidLayoutSubviews() {
|
||||
view.layoutSkeletonIfNeeded()
|
||||
}
|
||||
```
|
||||
|
||||
⚠️⚠️ Vous ne devriez pas appeler cette méthode. À partir de la *version 1.8.1*, vous n'avez pas besoin d'appeler cette méthode, la bibliothèque le fait automatiquement. Vous pouvez donc utiliser cette méthode *seulement* dans les cas où vous devez mettre à jour manuellement la présentation du squelette.
|
||||
|
||||
#### Mise à jour de la configuration du squelette
|
||||
|
||||
Vous pouvez modifier la configuration du squelette à tout moment comme sa couleur, son animation, etc :
|
||||
|
||||
```swift
|
||||
(1) view.updateSkeleton() // Solide
|
||||
(2) view.updateGradientSkeleton() // Gradient
|
||||
(3) view.updateAnimatedSkeleton() // Solid animated
|
||||
(4) view.updateAnimatedGradientSkeleton() // Gradient animé
|
||||
```
|
||||
|
||||
### 🌿 Collections
|
||||
|
||||
Maintenant, `SkeletonView` est compatible avec `UITableView` et `UICollectionView`.
|
||||
|
||||
#### UITableView
|
||||
|
||||
Si vous voulez montrer le squelette dans un `UITableView`, vous devez vous conformer au protocole `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
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier?
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier?
|
||||
}
|
||||
```
|
||||
|
||||
Comme vous pouvez le voir, ce protocole hérite de `UITableViewDataSource`, vous pouvez donc remplacer ce protocole par le protocole squelette.
|
||||
|
||||
Ce protocole a une implémentation par défaut:
|
||||
|
||||
``` swift
|
||||
func numSections(in collectionSkeletonView: UITableView) -> Int
|
||||
// Par défaut: 1
|
||||
```
|
||||
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
|
||||
// Par défaut:
|
||||
// Il calcule le nombre de cellules nécessaires pour remplir l'ensemble du tableau
|
||||
```
|
||||
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier?
|
||||
// Par défaut: nil
|
||||
```
|
||||
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier?
|
||||
// Par défaut: nil
|
||||
```
|
||||
|
||||
Il n'y a qu'une seule méthode à mettre en œuvre pour faire connaître au Squelette l'identifiant de la cellule. Cette méthode n'a pas d'implémentation par défaut :
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView : UITableView, cellIdentifierForRowAt indexPath : IndexPath) -> ReusableCellIdentifier
|
||||
```
|
||||
|
||||
**Exemple**
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView : UITableView, cellIdentifierForRowAt indexPath : IndexPath) -> ReusableCellIdentifier {
|
||||
return "CellIdentifier".
|
||||
}
|
||||
```
|
||||
|
||||
> **IMPORTANT!**
|
||||
> Si vous utilisez des cellules redimensionnables (`tableView.rowHeight = UITableViewAutomaticDimension` ), il est obligatoire de définir la `estimatedRowHeight`.
|
||||
|
||||
👩🏼 **Comment préciser quels éléments sont squelettisables ?
|
||||
|
||||
Voici une illustration qui montre comment vous devez spécifier quels éléments sont squelettisables lorsque vous utilisez une `UITableView` :
|
||||
|
||||

|
||||
|
||||
Comme vous pouvez le voir, nous devons faire `skeletonable` la tableview, la cellule et les éléments de l'interface visuelle, mais nous n'avons pas besoin de faire `skeletonable` le `contentView`.
|
||||
|
||||
#### UICollectionView
|
||||
|
||||
Pour ```UICollectionView```, vous devez conformer le protocole `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
|
||||
}
|
||||
```
|
||||
|
||||
Le reste du processus ressemble à une `UITableView`.
|
||||
|
||||
### 📰 Texte multiligne
|
||||
|
||||

|
||||
|
||||
Lorsqu'on utilise des éléments avec du texte, `SkeletonView` dessine des lignes pour simuler le texte.
|
||||
En outre, vous pouvez décider combien de lignes vous voulez. Si `numberOfLines` est réglé à zéro, il calculera le combien de lignes sont nécessaires pour remplir tout le squelette et il sera dessiné. Au contraire, si vous le réglez sur un, deux ou tout autre nombre supérieur à zéro, il ne dessinera que ce nombre de lignes.
|
||||
|
||||
##### 🎛 Personnaliser
|
||||
|
||||
Vous pouvez définir certaines propriétés pour les éléments multilignes.
|
||||
|
||||
| Propriété | Valeurs | Par défaut | Aperçu
|
||||
| ------- | ------- |------- | -------
|
||||
| **Pourcentage de remplissage** de la dernière ligne. | `0...100` | `70%` | 
|
||||
| **Corner radius** des lignes. (**NEW**) | `0...10` | `0` | 
|
||||
|
||||
|
||||
Pour modifier le pourcentage ou le rayon **à l'aide du code**, définissez les propriétés :
|
||||
``` swift
|
||||
descriptionTextView.lastLineFillPercent = 50
|
||||
descriptionTextView.linesCornerRadius = 5
|
||||
```
|
||||
|
||||
Ou, si vous préférez, utilisez l'**IB/Storyboard** :
|
||||
|
||||

|
||||
|
||||
### 🎨 Couleurs personnalisées
|
||||
|
||||
Vous pouvez décider la couleur du squelette. Il vous suffit de passer comme paramètre la couleur ou le gradient que vous souhaitez.
|
||||
|
||||
**Utiliser des couleurs solides**
|
||||
``` swift
|
||||
view.showSkeleton(usingColor : UIColor.gray) // Solide
|
||||
// ou
|
||||
view.showSkeleton(usingColor : UIColor(red : 25.0, green : 30.0, blue : 255.0, alpha : 1.0))
|
||||
```
|
||||
**Utilisation des gradients**
|
||||
``` swift
|
||||
let gradient = SkeletonGradient(baseColor : UIColor.midnightBlue)
|
||||
view.showGradientSkeleton(usingGradient : gradient) // Gradient
|
||||
```
|
||||
|
||||
En outre, `SkeletonView` dispose de 20 couleurs unies 🤙🏼
|
||||
|
||||
`UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...`
|
||||
|
||||

|
||||
###### Image tirée du site web [https://flatuicolors.com](https://flatuicolors.com)
|
||||
|
||||
### 🦋 Présentation
|
||||
|
||||
**NOUVEAU** Les squelettes ont une apparence par défaut. Ainsi, lorsque vous ne spécifiez pas la couleur, le gradient ou les propriétés multilignes, `SkeletonView` utilise les valeurs par défaut.
|
||||
|
||||
Valeurs par défaut :
|
||||
- **tintColor** : UIColor
|
||||
- *défaut : `.skeletonDefault` (comme `.clouds` mais adaptable au thème sombre)*
|
||||
- **gradient** : SkeletonGradient
|
||||
- *défaut : `SkeletonGradient(baseColor : .skeletonDefault)`*
|
||||
- **multilineHeight** : CGFloat
|
||||
- *défaut : 15*
|
||||
- **multilineSpacing** : CGFloat
|
||||
- *défaut : 10*
|
||||
- **multilineLastLineFillPercent** : Int
|
||||
- *défaut : 70*
|
||||
- **multilineCornerRadius** : Int
|
||||
- *défaut : 0*
|
||||
- **skeletonCornerRadius** : CGFloat (IBInspectable) (Faites votre vue squelette avec des coins)
|
||||
- *défaut : 0*
|
||||
|
||||
Pour obtenir ces valeurs par défaut, vous pouvez utiliser `SkeletonAppearance.default`. En utilisant cette propriété, vous pouvez également définir les valeurs :
|
||||
``` swift
|
||||
SkeletonAppearance.default.multilineHeight = 20
|
||||
SkeletonAppearance.default.tintColor = .green
|
||||
```
|
||||
|
||||
### 🤓 Animations personnalisées
|
||||
|
||||
`SkeletonView` a deux animations intégrées, *pulse* pour les squelettes solides et *sliding* pour les gradients.
|
||||
|
||||
De plus, si vous voulez faire votre propre animation de squelette, c'est très facile.
|
||||
|
||||
Skeleton fournit la fonction `showAnimatedSkeleton` qui possède une fermeture `SkeletonLayerAnimation` où vous pouvez définir votre animation personnalisée.
|
||||
|
||||
``` swift
|
||||
public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation
|
||||
```
|
||||
|
||||
Vous pouvez appeler la fonction comme ceci :
|
||||
|
||||
```swift
|
||||
view.showAnimatedSkeleton { (layer) -> CAAnimation in
|
||||
let animation = CAAnimation()
|
||||
// Personnalisez ici votre animation
|
||||
|
||||
return animation
|
||||
}
|
||||
```
|
||||
|
||||
`SkeletonAnimationBuilder` est disponible. C'est un constructeur pour faire `SkeletonLayerAnimation`.
|
||||
|
||||
Aujourd'hui, vous pouvez créer des **animations glissantes** pour les gradients, en décidant de la **direction** et en fixant la **durée** de l'animation (par défaut = 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` est une `enum`, avec ces cas :
|
||||
|
||||
| Direction | Aperçu
|
||||
|------- | -------
|
||||
| .leftRight | 
|
||||
| .rightLeft | 
|
||||
| .topBottom | 
|
||||
| .bottomTop | 
|
||||
| .topLeftBottomRight | 
|
||||
| .bottomRightTopLeft | 
|
||||
|
||||
> **😉 TRICK!**
|
||||
Il existe une autre façon de créer des animations de glissement, en utilisant simplement ce raccourci :
|
||||
>>```let animation = GradientDirection.leftToRight.slidingAnimation()```
|
||||
|
||||
### 🏄 Transitions
|
||||
|
||||
`SkeletonView` a des transitions intégrées pour **montrer** ou **cacher** les squelettes d'une manière *lisse* 🤙
|
||||
|
||||
Pour utiliser la transition, il suffit d'ajouter le paramètre "transition" à votre fonction `showSkeleton()` ou `hideSkeleton()` avec le temps de transition, comme ceci :
|
||||
|
||||
```swift
|
||||
view.showSkeleton(transition : .crossDissolve(0.25)) //Montrer la transition de dissolution croisée du squelette avec un temps de fondu de 0,25 seconde
|
||||
view.hideSkeleton(transition : .crossDissolve(0.25)) //Cachez la transition de dissolution croisée du squelette avec un temps de fondu de 0,25 seconde
|
||||
```
|
||||
|
||||
La valeur par défaut est `crossDissolve(0.25)`.
|
||||
|
||||
**Preview**
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%">
|
||||
<center>None</center>
|
||||
</td>
|
||||
<td width="50%">
|
||||
<center>Cross dissolve</center>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%">
|
||||
<img src="Assets/skeleton_transition_nofade.gif"></img>
|
||||
</td>
|
||||
<td width="50%">
|
||||
<img src="Assets/skeleton_transition_fade.gif"></img>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### 👨👧👦 Hiérarchie
|
||||
|
||||
Puisque `SkeletonView` est récursif, et que nous voulons que le squelette soit très efficace, nous voulons arrêter la récursivité dès que possible. Pour cette raison, vous devez définir la vue du conteneur comme `Skeletonable`, parce que Skeleton arrêtera de chercher des sous-vues `squelettisables` dès qu'une vue n'est pas `Skeletonable`, cassant alors la récursivité.
|
||||
|
||||
Parce qu'une image vaut mille mots :
|
||||
|
||||
Dans cet exemple, nous avons un `UIViewController` avec une `ContainerView` et une `UITableView`. Lorsque la vue est prête, nous montrons le squelette en utilisant cette méthode :
|
||||
```
|
||||
view.showSkeleton()
|
||||
```
|
||||
|
||||
> ```isSkeletonable```= ☠️
|
||||
|
||||
| Configuration | Résultat|
|
||||
|:-------:|:-------:|
|
||||
|<img src="Assets/no_skeletonable.jpg" width="350"/> | <img src="Assets/no_skeletonables_result.png" width="350"/>|
|
||||
|<img src="Assets/container_no_skeletonable.jpg" width="350"/> | <img src="Assets/no_skeletonables_result.png" width="350"/>|
|
||||
|<img src="Assets/container_skeletonable.jpg" width="350"/> | <img src="Assets/container_skeletonable_result.png" width="350"/>|
|
||||
|<img src="Assets/all_skeletonables.jpg" width="350"/>| <img src="Assets/all_skeletonables_result.png" width="350"/>|
|
||||
|<img src="Assets/tableview_no_skeletonable.jpg" width="350"/> | <img src="Assets/tableview_no_skeletonable_result.png" height="350"/>|
|
||||
|<img src="Assets/tableview_skeletonable.jpg" width="350"/> | <img src="Assets/tableview_skeletonable_result.png" height="350"/>|
|
||||
|
||||
### 🔬 Débugger
|
||||
|
||||
**NOUVEAU** Afin de faciliter les tâches de debuggage lorsque quelque chose ne fonctionne pas bien. `SkeletonView` a de nouveaux outils.
|
||||
|
||||
Tout d'abord, `UIView` a une nouvelle propriété disponible avec son squelette d'information :
|
||||
```swift
|
||||
var skeletonDescription : String
|
||||
```
|
||||
La représentation du squelette ressemble à ceci :
|
||||
|
||||

|
||||
|
||||
En outre, vous pouvez activer le nouveau mode **debug**. Il suffit d'ajouter la variable d'environnement `SKELETON_DEBUG` et de l'activer.
|
||||
|
||||

|
||||
|
||||
Ensuite, lorsque le squelette apparaît, vous pouvez voir la hiérarchie des vues dans la console Xcode.
|
||||
|
||||
<details>
|
||||
<summary>Ouvrez pour voir un exemple de sortie</summary>
|
||||
<img src="Assets/hierarchy_output.png" />
|
||||
</details>
|
||||
|
||||
### 📚 Documentation
|
||||
Bientôt disponible...😅
|
||||
|
||||
### 📋 Versions OS et SDK supportées
|
||||
|
||||
* iOS 9.0+
|
||||
* tvOS 9.0+
|
||||
* Swift 5
|
||||
|
||||
## 📬 Prochaines étapes
|
||||
|
||||
* [x] Fixer le pourcentage de remplissage de la dernière ligne dans les éléments multilignes
|
||||
* [x] Ajout d'autres animations en dégradé
|
||||
* [x] Cellules redimensionnables prises en charge
|
||||
* [x] Compatible avec CollectionView
|
||||
* [x] Compatible avec tvOS
|
||||
* [x] Ajouter l'état de recouvrement
|
||||
* [x] Apparence personnalisée par défaut
|
||||
* [x] Mode de debuggage
|
||||
* [x] Ajouter des animations lorsqu'il montre/cache les squelettes
|
||||
* [ ] Compatible avec les collections personnalisées
|
||||
* [ ] Compatible avec MacOS et WatchOS
|
||||
|
||||
## ❤️ Contribuer
|
||||
Il s'agit d'un projet open source, alors n'hésitez pas à y contribuer. Comment ?
|
||||
- Ouvrez un [numéro](https://github.com/Juanpe/SkeletonView/issues/new).
|
||||
- Envoyez vos commentaires via [email](mailto://juanpecatalan.com).
|
||||
- Proposez vos propres correctifs, suggestions et ouvrez une `pull request` avec les changements.
|
||||
|
||||
Voir [tous les contributeurs](https://github.com/Juanpe/SkeletonView/graphs/contributors)
|
||||
|
||||
###### Projet généré avec [SwiftPlate](https://github.com/JohnSundell/SwiftPlate)
|
||||
|
||||
## 📢 Mentions
|
||||
|
||||
- [iOS Dev Weekly #327](https://iosdevweekly.com/issues/327#start)
|
||||
- [Hacking with Swift Articles] (https://www.hackingwithswift.com/articles/40/skeletonview-makes-loading-content-beautiful)
|
||||
- [Top 10 articles Swift de novembre] (https://medium.mybridge.co/swift-top-10-articles-for-the-past-month-v-nov-2017-dfed7861cd65)
|
||||
- [30 bibliothèques incroyables pour iOS Swift (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)
|
||||
- [Bulletin d'information sur les développements Swift #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)
|
||||
- [Bulletin d'information Génial iOS #74](https://ios.libhunt.com/newsletter/74)
|
||||
- [Swift News #36](https://www.youtube.com/watch?v=mAGpsQiy6so)
|
||||
- [Meilleurs articles sur iOS, nouveaux outils et plus](https://medium.com/flawless-app-stories/best-ios-articles-new-tools-more-fcbe673e10d)
|
||||
|
||||
## 👨🏻💻 Auteur
|
||||
[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>
|
||||
|
||||
## 👮🏻 Licence
|
||||
|
||||
```
|
||||
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.
|
||||
```
|
||||
+34
-37
@@ -1,42 +1,39 @@
|
||||

|
||||
|
||||
<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 href="https://github.com/Juanpe/SkeletonView/workflows/build">
|
||||
<img src="https://github.com/Juanpe/SkeletonView/workflows/build/badge.svg">
|
||||
</a>
|
||||
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
|
||||
<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>
|
||||
<img src="http://img.shields.io/badge/dependency%20manager-swiftpm%2Bcocoapods%2Bcarthage-green" />
|
||||
<img src="https://img.shields.io/badge/platforms-ios%2Btvos-green" />
|
||||
<a href="https://badge.bow-swift.io/recipe?name=SkeletonView&description=An%20elegant%20way%20to%20show%20users%20that%20something%20is%20happening%20and%20also%20prepare%20them%20to%20which%20contents%20he%20is%20waiting&url=https://github.com/juanpe/skeletonview&owner=Juanpe&avatar=https://avatars0.githubusercontent.com/u/1409041?v=4&tag=1.8.7"><img src="https://raw.githubusercontent.com/bow-swift/bow-art/master/badges/nef-playgrounds-badge.svg" alt="SkeletonView Playground" style="height:20px"></a>
|
||||
<br/>
|
||||
<a href="https://twitter.com/JuanpeCatalan">
|
||||
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
|
||||
</a>
|
||||
<br/>
|
||||
<a href="https://gitter.im/SkeletonView/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge">
|
||||
<img src="https://badges.gitter.im/SkeletonView/community.svg?style=flat" />
|
||||
</a>
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">
|
||||
<img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="PayPal" />
|
||||
<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)
|
||||
[Original](https://github.com/Juanpe/SkeletonView) </br>
|
||||
[🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) [@WhatsXie](https://twitter.com/WhatsXie) </br>
|
||||
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) [@brunomunizaf](https://twitter.com/brunomuniz_af) </br>
|
||||
[🇰🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_ko.md) [@techinpark](https://twitter.com/techinpark) </br>
|
||||
[🇫🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_fr.md) [@OmarJalil](https://github.com/OmarJalil)
|
||||
|
||||
오늘날 거의 대부분의 앱들은 비동기 방식의 API 호출을 사용하는 프로세스를 가지고 있습니다.
|
||||
프로세스가 작동하는동안 개발자들은 작업이 실행되고 있다는것을 사용자들에게 보여주기 위해서 로딩 뷰를 배치합니다.
|
||||
|
||||
오늘날 거의 대부분의 앱들은 비동기 방식의 API 호출을 사용하는 프로세스를 가지고 있습니다.
|
||||
프로세스가 작동하는동안 개발자들은 작업이 실행되고 있다는것을 사용자들에게 보여주기 위해서 로딩 뷰를 배치합니다.
|
||||
|
||||
```SkeletonView```는 이러한 필요에 의해 고안되었고, 사용자들에게 무엇인가 로딩이 되고 있다는것을 보여주면서 기다리는 콘텐츠에 대해서도 미리 준비할 수 있게 해주는 우아하게 표현할수 있는 방법입니다
|
||||
```SkeletonView```는 이러한 필요에 의해 고안되었고, 사용자들에게 무엇인가 로딩이 되고 있다는것을 보여주면서 기다리는 콘텐츠에 대해서도 미리 준비할 수 있게 해주는 우아하게 표현할수 있는 방법입니다
|
||||
|
||||
맘껏 누리세요 🙂
|
||||
|
||||
@@ -65,9 +62,9 @@
|
||||
|
||||
## 🌟 기능
|
||||
|
||||
- [x] 사용이 쉽습니다
|
||||
- [x] 모든 `UIView`에서 사용가능합니다
|
||||
- [x] 전체 커스터마이징이 가능합니다
|
||||
- [x] 사용이 쉽습니다
|
||||
- [x] 모든 `UIView`에서 사용가능합니다
|
||||
- [x] 전체 커스터마이징이 가능합니다
|
||||
- [x] 공통으로 이용가능합니다 (iPhone & iPad)
|
||||
- [x] `Interface Builder` 에서 사용 가능합니다.
|
||||
- [x] 간단한 스위프트 문법
|
||||
@@ -171,7 +168,7 @@ avatarImageView.isSkeletonable = true
|
||||
</table>
|
||||
|
||||
> **중요!**
|
||||
>>```SkeletonView``` 는 재귀적으로 되어있습니다, 만약 모든 뷰에 대해서 skeleton을 호출하고 싶다면, 메인 컨테이너 뷰에서 show `method`를 호출하여야 합니다. 예를 들자면 UIViewControllers가 있습니다.
|
||||
>>```SkeletonView``` 는 재귀적으로 되어있습니다, 만약 모든 뷰에 대해서 skeleton을 호출하고 싶다면, 메인 컨테이너 뷰에서 show `method`를 호출하여야 합니다. 예를 들자면 UIViewControllers가 있습니다.
|
||||
|
||||
|
||||
|
||||
@@ -181,7 +178,7 @@ avatarImageView.isSkeletonable = true
|
||||
|
||||
#### UITableView
|
||||
|
||||
만약 ```UITableView```에서 skeleton을 호출하고 싶다면, ```SkeletonTableViewDataSource``` protocol 을 구현하여야 합니다.
|
||||
만약 ```UITableView```에서 skeleton을 호출하고 싶다면, ```SkeletonTableViewDataSource``` protocol 을 구현하여야 합니다.
|
||||
|
||||
``` swift
|
||||
public protocol SkeletonTableViewDataSource: UITableViewDataSource {
|
||||
@@ -191,9 +188,9 @@ public protocol SkeletonTableViewDataSource: UITableViewDataSource {
|
||||
}
|
||||
```
|
||||
|
||||
해당 프로토클은 보시다시피 ```UITableViewDataSource```를 상속받아 구현하였으므로, skeleton의 protocol과 대체 가능합니다.
|
||||
해당 프로토클은 보시다시피 ```UITableViewDataSource```를 상속받아 구현하였으므로, skeleton의 protocol과 대체 가능합니다.
|
||||
|
||||
프로토콜의 기본 구현은 다음과 같습니다:
|
||||
프로토콜의 기본 구현은 다음과 같습니다:
|
||||
|
||||
``` swift
|
||||
func numSections(in collectionSkeletonView: UITableView) -> Int
|
||||
@@ -203,10 +200,10 @@ func numSections(in collectionSkeletonView: UITableView) -> Int
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
|
||||
// Default:
|
||||
// 전체 테이블 뷰를 채우는데 필요한 셀 수를 계산합니다
|
||||
// 전체 테이블 뷰를 채우는데 필요한 셀 수를 계산합니다
|
||||
```
|
||||
|
||||
해당 메소드는 당신이 구현하여야할 cell identifier을 아는 경우에만 사용합니다, 해당 메소드는 기본으로 구현하지 않아도됩니다 :
|
||||
해당 메소드는 당신이 구현하여야할 cell identifier을 아는 경우에만 사용합니다, 해당 메소드는 기본으로 구현하지 않아도됩니다 :
|
||||
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
@@ -243,7 +240,7 @@ public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
|
||||
}
|
||||
```
|
||||
|
||||
```UITableView``` 와 사용방법은 같습니다.
|
||||
```UITableView``` 와 사용방법은 같습니다.
|
||||
|
||||
### 📰 Multiline text
|
||||
|
||||
@@ -277,7 +274,7 @@ descriptionTextView.linesCornerRadius = 5
|
||||
|
||||
### 🎨 Custom colors
|
||||
|
||||
당신은 skeleton의 색상을 지정 할 수 있습니다. 간단하게 원하는 색상을 파라미터로 넘겨주시면 됩니다.
|
||||
당신은 skeleton의 색상을 지정 할 수 있습니다. 간단하게 원하는 색상을 파라미터로 넘겨주시면 됩니다.
|
||||
|
||||
**단색 이용방법**
|
||||
``` swift
|
||||
@@ -296,11 +293,11 @@ view.showGradientSkeleton(usingGradient: gradient) // Gradient
|
||||
```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...```
|
||||
|
||||

|
||||
###### 위 이미지는 [https://flatuicolors.com](https://flatuicolors.com) 사이트에서 발췌했습니다.
|
||||
###### 위 이미지는 [https://flatuicolors.com](https://flatuicolors.com) 사이트에서 발췌했습니다.
|
||||
|
||||
### 🦋 Appearance
|
||||
|
||||
**새로운 사항** skeleton 은 기본설정 값이 정해져 있습니다. 만약 커스텀 컬러를 사용할 필요가 없다면, `SkeletonView` 에 지정 되어있는 기본설정을 사용하시면 됩니다.
|
||||
**새로운 사항** skeleton 은 기본설정 값이 정해져 있습니다. 만약 커스텀 컬러를 사용할 필요가 없다면, `SkeletonView` 에 지정 되어있는 기본설정을 사용하시면 됩니다.
|
||||
|
||||
기본 설정값:
|
||||
- **tintColor**: UIColor
|
||||
@@ -376,7 +373,7 @@ view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
|
||||
|
||||
### 👨👧👦 계층 구조
|
||||
|
||||
```SkeletonView```는 재귀적입니다 , 그리고 우리는 skeleton이 효율적으로 작동하기를 원하기 때문에, 가능한 빨리 재귀작업을 중단하기를 원합니다. 이러한 이유때문에 반드시 컨테이너 뷰를 `Skeletonable` 로 설정해야 합니다, `skeletonable` 되지 않는 뷰를 만나는 순간 재귀 작업을 중단하기 떄문입니다.
|
||||
```SkeletonView```는 재귀적입니다 , 그리고 우리는 skeleton이 효율적으로 작동하기를 원하기 때문에, 가능한 빨리 재귀작업을 중단하기를 원합니다. 이러한 이유때문에 반드시 컨테이너 뷰를 `Skeletonable` 로 설정해야 합니다, `skeletonable` 되지 않는 뷰를 만나는 순간 재귀 작업을 중단하기 떄문입니다.
|
||||
|
||||
아래의 이미지를 참고하세요 이미지는 한눈에 이해되실겁니다:
|
||||
|
||||
@@ -429,7 +426,7 @@ skeleton은 이렇게 생겼습니다:
|
||||
|
||||
* [x] 멀티라인 에서의 마지막 라인의 채우기 비율 설정
|
||||
* [x] 더많은 그라디언트 애니메이션
|
||||
* [x] resizable cells 지원
|
||||
* [x] resizable cells 지원
|
||||
* [x] CollectionView 호환
|
||||
* [x] tvOS 호환
|
||||
* [x] recovery state 추가
|
||||
|
||||
+14
-15
@@ -1,27 +1,23 @@
|
||||

|
||||
|
||||
<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 href="https://github.com/Juanpe/SkeletonView/workflows/build">
|
||||
<img src="https://github.com/Juanpe/SkeletonView/workflows/build/badge.svg">
|
||||
</a>
|
||||
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
|
||||
<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>
|
||||
<img src="http://img.shields.io/badge/dependency%20manager-swiftpm%2Bcocoapods%2Bcarthage-green" />
|
||||
<img src="https://img.shields.io/badge/platforms-ios%2Btvos-green" />
|
||||
<a href="https://badge.bow-swift.io/recipe?name=SkeletonView&description=An%20elegant%20way%20to%20show%20users%20that%20something%20is%20happening%20and%20also%20prepare%20them%20to%20which%20contents%20he%20is%20waiting&url=https://github.com/juanpe/skeletonview&owner=Juanpe&avatar=https://avatars0.githubusercontent.com/u/1409041?v=4&tag=1.8.7"><img src="https://raw.githubusercontent.com/bow-swift/bow-art/master/badges/nef-playgrounds-badge.svg" alt="SkeletonView Playground" style="height:20px"></a>
|
||||
<br/>
|
||||
<a href="https://twitter.com/JuanpeCatalan">
|
||||
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
|
||||
</a>
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">
|
||||
<img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Paypal" />
|
||||
<a href="https://gitter.im/SkeletonView/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge">
|
||||
<img src="https://badges.gitter.im/SkeletonView/community.svg?style=flat" />
|
||||
</a>
|
||||
<br/>
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">
|
||||
<img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="PayPal" />
|
||||
<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>
|
||||
@@ -29,7 +25,10 @@
|
||||
|
||||
🌎 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>
|
||||
[🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) [@WhatsXie](https://twitter.com/WhatsXie) </br>
|
||||
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) [@brunomunizaf](https://twitter.com/brunomuniz_af) </br>
|
||||
[🇰🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_ko.md) [@techinpark](https://twitter.com/techinpark) </br>
|
||||
[🇫🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_fr.md) [@OmarJalil](https://github.com/OmarJalil)
|
||||
|
||||
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.
|
||||
|
||||
|
||||
+15
-17
@@ -1,36 +1,34 @@
|
||||

|
||||
|
||||
<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 href="https://github.com/Juanpe/SkeletonView/workflows/build">
|
||||
<img src="https://github.com/Juanpe/SkeletonView/workflows/build/badge.svg">
|
||||
</a>
|
||||
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
|
||||
<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>
|
||||
<img src="http://img.shields.io/badge/dependency%20manager-swiftpm%2Bcocoapods%2Bcarthage-green" />
|
||||
<img src="https://img.shields.io/badge/platforms-ios%2Btvos-green" />
|
||||
<a href="https://badge.bow-swift.io/recipe?name=SkeletonView&description=An%20elegant%20way%20to%20show%20users%20that%20something%20is%20happening%20and%20also%20prepare%20them%20to%20which%20contents%20he%20is%20waiting&url=https://github.com/juanpe/skeletonview&owner=Juanpe&avatar=https://avatars0.githubusercontent.com/u/1409041?v=4&tag=1.8.7"><img src="https://raw.githubusercontent.com/bow-swift/bow-art/master/badges/nef-playgrounds-badge.svg" alt="SkeletonView Playground" style="height:20px"></a>
|
||||
<br/>
|
||||
<a href="https://twitter.com/JuanpeCatalan">
|
||||
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
|
||||
</a>
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">
|
||||
<img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Paypal" />
|
||||
<a href="https://gitter.im/SkeletonView/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge">
|
||||
<img src="https://badges.gitter.im/SkeletonView/community.svg?style=flat" />
|
||||
</a>
|
||||
<br/>
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">
|
||||
<img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="PayPal" />
|
||||
<a href="https://twitter.com/intent/tweet?text=Wow%20This%20library%20is%20awesome:&url=https%3A%2F%2Fgithub.com%2FJuanpe%2FSkeletonView">
|
||||
<img src="https://img.shields.io/twitter/url/https/github.com/Juanpe/SkeletonView.svg?style=social" alt="License" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
🌎 翻译: [ [原版的](https://github.com/Juanpe/SkeletonView) ] </br>
|
||||
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) [@brunomunizaf](https://twitter.com/brunomuniz_af)
|
||||
|
||||
[Original](https://github.com/Juanpe/SkeletonView) </br>
|
||||
[🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) [@WhatsXie](https://twitter.com/WhatsXie) </br>
|
||||
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) [@brunomunizaf](https://twitter.com/brunomuniz_af) </br>
|
||||
[🇰🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_ko.md) [@techinpark](https://twitter.com/techinpark) </br>
|
||||
[🇫🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_fr.md) [@OmarJalil](https://github.com/OmarJalil)
|
||||
|
||||
今天,几乎所有的应用程序都有异步流程,例如:Api请求、长时间运行的流程等。虽然流程正在运行,但通常开发人员会设置一个加载视图来向用户显示正在发生的事情。
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "SkeletonView"
|
||||
s.version = "1.8.2"
|
||||
s.version = "1.8.8"
|
||||
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.
|
||||
|
||||
@@ -30,6 +30,11 @@
|
||||
17DD0E1F207FB32100C56334 /* RecursiveProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */; };
|
||||
1E6C67A2230E76CC0019D87B /* SkeletonTransitionStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4CE587C22EEE65200333067 /* SkeletonTransitionStyle.swift */; };
|
||||
1E6C67A3230E76CE0019D87B /* UIView+Transitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4CE587B22EEE63100333067 /* UIView+Transitions.swift */; };
|
||||
1EE42E1F23FF25CC00BF665A /* ProcessInfo+XCTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE42E1E23FF25CC00BF665A /* ProcessInfo+XCTest.swift */; };
|
||||
1EE42E2023FF25CC00BF665A /* ProcessInfo+XCTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE42E1E23FF25CC00BF665A /* ProcessInfo+XCTest.swift */; };
|
||||
3467F55F2473601F0086C027 /* UITextView+Multiline.swift in Headers */ = {isa = PBXBuildFile; fileRef = 872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
3467F560247360270086C027 /* UIView+IBInspectable.swift in Headers */ = {isa = PBXBuildFile; fileRef = F5804771230ECD0000066D02 /* UIView+IBInspectable.swift */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
3467F5612473602D0086C027 /* UILabel+Multiline.swift in Headers */ = {isa = PBXBuildFile; fileRef = 872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
42ABD063210B548200BEEFF4 /* SkeletonView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* SkeletonView.framework */; };
|
||||
42ABD069210B548200BEEFF4 /* SkeletonView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* SkeletonView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
42ABD078210B54E200BEEFF4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD070210B54E100BEEFF4 /* AppDelegate.swift */; };
|
||||
@@ -38,6 +43,10 @@
|
||||
42ABD07B210B54E200BEEFF4 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD073210B54E100BEEFF4 /* ViewController.swift */; };
|
||||
42ABD07C210B54E200BEEFF4 /* Base.lproj in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD074210B54E100BEEFF4 /* Base.lproj */; };
|
||||
42ABD07F210B54E200BEEFF4 /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */; };
|
||||
5600784423FD293D00669AD6 /* UITableView+VisibleSections.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5600784323FD293D00669AD6 /* UITableView+VisibleSections.swift */; };
|
||||
5600784523FD293D00669AD6 /* UITableView+VisibleSections.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5600784323FD293D00669AD6 /* UITableView+VisibleSections.swift */; };
|
||||
5600784923FD33AD00669AD6 /* HeaderFooterSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5600784623FD2BC300669AD6 /* HeaderFooterSection.swift */; };
|
||||
5600784A23FD33AE00669AD6 /* HeaderFooterSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5600784623FD2BC300669AD6 /* HeaderFooterSection.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 */; };
|
||||
@@ -152,6 +161,7 @@
|
||||
17DD0E00207FB27400C56334 /* SkeletonView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SkeletonView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
17DD0E1A207FB2C200C56334 /* SkeletonView-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SkeletonView-tvOS.plist"; sourceTree = "<group>"; };
|
||||
17DD0E1B207FB2C200C56334 /* SkeletonView-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SkeletonView-iOS.plist"; sourceTree = "<group>"; };
|
||||
1EE42E1E23FF25CC00BF665A /* ProcessInfo+XCTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+XCTest.swift"; sourceTree = "<group>"; };
|
||||
42ABD06D210B548200BEEFF4 /* SkeletonViewExampleUICollectionView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SkeletonViewExampleUICollectionView.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
42ABD070210B54E100BEEFF4 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
42ABD071210B54E100BEEFF4 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
|
||||
@@ -161,6 +171,8 @@
|
||||
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; };
|
||||
5600784323FD293D00669AD6 /* UITableView+VisibleSections.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+VisibleSections.swift"; sourceTree = "<group>"; };
|
||||
5600784623FD2BC300669AD6 /* HeaderFooterSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderFooterSection.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
@@ -295,6 +307,7 @@
|
||||
F5F899F41FABA607002E8FDA /* AppDelegate.swift */,
|
||||
88DEA97D1FCDBD1F006C80EF /* Constants.swift */,
|
||||
F5F899F61FABA607002E8FDA /* ViewController.swift */,
|
||||
5600784623FD2BC300669AD6 /* HeaderFooterSection.swift */,
|
||||
F5F622441FACA338007C062A /* Cell.swift */,
|
||||
F5F899F81FABA607002E8FDA /* Main.storyboard */,
|
||||
F5F899FB1FABA607002E8FDA /* Assets.xcassets */,
|
||||
@@ -430,6 +443,8 @@
|
||||
F5307E2D1FB0E5E400EE67C5 /* UIView+Frame.swift */,
|
||||
F5804771230ECD0000066D02 /* UIView+IBInspectable.swift */,
|
||||
F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */,
|
||||
5600784323FD293D00669AD6 /* UITableView+VisibleSections.swift */,
|
||||
1EE42E1E23FF25CC00BF665A /* ProcessInfo+XCTest.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@@ -494,6 +509,9 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3467F5612473602D0086C027 /* UILabel+Multiline.swift in Headers */,
|
||||
3467F55F2473601F0086C027 /* UITextView+Multiline.swift in Headers */,
|
||||
3467F560247360270086C027 /* UIView+IBInspectable.swift in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -683,6 +701,7 @@
|
||||
F51ED28320973CBB008B2434 /* SkeletonCollectionDelegate.swift in Sources */,
|
||||
F58A6E6F20A8C66300612494 /* Recoverable.swift in Sources */,
|
||||
17DD0E18207FB28F00C56334 /* SkeletonView.swift in Sources */,
|
||||
1EE42E2023FF25CC00BF665A /* ProcessInfo+XCTest.swift in Sources */,
|
||||
17DD0E19207FB28F00C56334 /* SkeletonFlow.swift in Sources */,
|
||||
17DD0E09207FB28900C56334 /* SkeletonCollectionDataSource.swift in Sources */,
|
||||
17DD0E0C207FB28900C56334 /* UICollectionView+CollectionSkeleton.swift in Sources */,
|
||||
@@ -692,6 +711,7 @@
|
||||
F5D3FB0C209DCAA300003FCF /* SubviewsSkeletonables.swift in Sources */,
|
||||
17DD0E14207FB28F00C56334 /* SkeletonAnimationBuilder.swift in Sources */,
|
||||
17DD0E11207FB28C00C56334 /* UIView+Frame.swift in Sources */,
|
||||
5600784523FD293D00669AD6 /* UITableView+VisibleSections.swift in Sources */,
|
||||
17DD0E15207FB28F00C56334 /* SkeletonAppearance.swift in Sources */,
|
||||
872D5A6021C24F8E0037D763 /* UITextView+Multiline.swift in Sources */,
|
||||
17DD0E10207FB28C00C56334 /* UIColor+Skeleton.swift in Sources */,
|
||||
@@ -713,6 +733,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
42ABD07B210B54E200BEEFF4 /* ViewController.swift in Sources */,
|
||||
5600784A23FD33AE00669AD6 /* HeaderFooterSection.swift in Sources */,
|
||||
42ABD078210B54E200BEEFF4 /* AppDelegate.swift in Sources */,
|
||||
42ABD07F210B54E200BEEFF4 /* CollectionViewCell.swift in Sources */,
|
||||
8785E3A3211C9CE800CC9DFD /* Constants.swift in Sources */,
|
||||
@@ -740,6 +761,7 @@
|
||||
F5F899ED1FAB9F04002E8FDA /* CollectionSkeletonProtocol.swift in Sources */,
|
||||
F58A6E6E20A8C66300612494 /* Recoverable.swift in Sources */,
|
||||
F5307E2C1FAF6BC900EE67C5 /* SkeletonGradient.swift in Sources */,
|
||||
1EE42E1F23FF25CC00BF665A /* ProcessInfo+XCTest.swift in Sources */,
|
||||
F587FB86202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift in Sources */,
|
||||
F5F899EB1FAB9DA3002E8FDA /* SkeletonCollectionDataSource.swift in Sources */,
|
||||
F5F622411FAC6E31007C062A /* UIColor+Skeleton.swift in Sources */,
|
||||
@@ -749,6 +771,7 @@
|
||||
F5D3FB0B209DCAA300003FCF /* SubviewsSkeletonables.swift in Sources */,
|
||||
F5F899D21FAB9630002E8FDA /* AssociationPolicy.swift in Sources */,
|
||||
F54CF5E62024CEB000330B0D /* UICollectionView+CollectionSkeleton.swift in Sources */,
|
||||
5600784423FD293D00669AD6 /* UITableView+VisibleSections.swift in Sources */,
|
||||
F56B94461FAE20AF0095662F /* PrepareForSkeletonProtocol.swift in Sources */,
|
||||
F5307E391FB1078E00EE67C5 /* SkeletonCollectionViewProtocols.swift in Sources */,
|
||||
872D5A5F21C24F8E0037D763 /* UITextView+Multiline.swift in Sources */,
|
||||
@@ -770,6 +793,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F5F899F71FABA607002E8FDA /* ViewController.swift in Sources */,
|
||||
5600784923FD33AD00669AD6 /* HeaderFooterSection.swift in Sources */,
|
||||
F5F899F51FABA607002E8FDA /* AppDelegate.swift in Sources */,
|
||||
F5F622451FACA338007C062A /* Cell.swift in Sources */,
|
||||
88DEA97F1FCDBD78006C80EF /* Constants.swift in Sources */,
|
||||
@@ -1100,7 +1124,7 @@
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
@@ -1109,6 +1133,7 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@@ -1125,7 +1150,7 @@
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
@@ -1134,6 +1159,7 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
|
||||
@@ -9,6 +9,7 @@ public protocol Appearance {
|
||||
var multilineSpacing: CGFloat { get set }
|
||||
var multilineLastLineFillPercent: Int { get set }
|
||||
var multilineCornerRadius: Int { get set }
|
||||
var renderSingleLineAsView: Bool { get set }
|
||||
}
|
||||
|
||||
public enum SkeletonAppearance {
|
||||
@@ -19,9 +20,9 @@ public enum SkeletonAppearance {
|
||||
class SkeletonViewAppearance: Appearance {
|
||||
static var shared = SkeletonViewAppearance()
|
||||
|
||||
var tintColor: UIColor = .clouds
|
||||
var tintColor: UIColor = .skeletonDefault
|
||||
|
||||
var gradient: SkeletonGradient = SkeletonGradient(baseColor: .clouds)
|
||||
var gradient: SkeletonGradient = SkeletonGradient(baseColor: .skeletonDefault)
|
||||
|
||||
var multilineHeight: CGFloat = 15
|
||||
|
||||
@@ -30,5 +31,7 @@ class SkeletonViewAppearance: Appearance {
|
||||
var multilineLastLineFillPercent: Int = 70
|
||||
|
||||
var multilineCornerRadius: Int = 0
|
||||
|
||||
var renderSingleLineAsView: Bool = false
|
||||
}
|
||||
// codebeat:enable[TOO_MANY_IVARS]
|
||||
|
||||
@@ -7,8 +7,11 @@ import UIKit
|
||||
class SkeletonMultilineLayerBuilder {
|
||||
var skeletonType: SkeletonType?
|
||||
var index: Int?
|
||||
var height: CGFloat?
|
||||
var width: CGFloat?
|
||||
var cornerRadius: Int?
|
||||
var multilineSpacing: CGFloat = SkeletonAppearance.default.multilineSpacing
|
||||
var paddingInsets: UIEdgeInsets = .zero
|
||||
|
||||
func setSkeletonType(_ type: SkeletonType) -> SkeletonMultilineLayerBuilder {
|
||||
self.skeletonType = type
|
||||
@@ -20,6 +23,11 @@ class SkeletonMultilineLayerBuilder {
|
||||
return self
|
||||
}
|
||||
|
||||
func setHeight(_ height: CGFloat) -> SkeletonMultilineLayerBuilder {
|
||||
self.height = height
|
||||
return self
|
||||
}
|
||||
|
||||
func setWidth(_ width: CGFloat) -> SkeletonMultilineLayerBuilder {
|
||||
self.width = width
|
||||
return self
|
||||
@@ -30,17 +38,28 @@ class SkeletonMultilineLayerBuilder {
|
||||
return self
|
||||
}
|
||||
|
||||
func setMultilineSpacing(_ spacing: CGFloat) -> SkeletonMultilineLayerBuilder {
|
||||
self.multilineSpacing = spacing
|
||||
return self
|
||||
}
|
||||
|
||||
func setPadding(_ insets: UIEdgeInsets) -> SkeletonMultilineLayerBuilder {
|
||||
self.paddingInsets = insets
|
||||
return self
|
||||
}
|
||||
|
||||
func build() -> CALayer? {
|
||||
guard let type = skeletonType,
|
||||
let index = index,
|
||||
let width = width,
|
||||
let height = height,
|
||||
let radius = cornerRadius
|
||||
else { return nil }
|
||||
|
||||
let layer = type.layer
|
||||
layer.anchorPoint = .zero
|
||||
layer.name = CALayer.skeletonSubLayersName
|
||||
layer.updateLayerFrame(for: index, width: width)
|
||||
layer.updateLayerFrame(for: index, size: CGSize(width: width, height: height), multilineSpacing: self.multilineSpacing, paddingInsets: paddingInsets)
|
||||
|
||||
layer.cornerRadius = CGFloat(radius)
|
||||
layer.masksToBounds = true
|
||||
|
||||
@@ -14,12 +14,14 @@ class SkeletonCollectionDataSource: NSObject {
|
||||
weak var originalTableViewDataSource: SkeletonTableViewDataSource?
|
||||
weak var originalCollectionViewDataSource: SkeletonCollectionViewDataSource?
|
||||
var rowHeight: CGFloat = 0.0
|
||||
var originalRowHeight: CGFloat = 0.0
|
||||
|
||||
convenience init(tableViewDataSource: SkeletonTableViewDataSource? = nil, collectionViewDataSource: SkeletonCollectionViewDataSource? = nil, rowHeight: CGFloat = 0.0) {
|
||||
convenience init(tableViewDataSource: SkeletonTableViewDataSource? = nil, collectionViewDataSource: SkeletonCollectionViewDataSource? = nil, rowHeight: CGFloat = 0.0, originalRowHeight: CGFloat = 0.0) {
|
||||
self.init()
|
||||
self.originalTableViewDataSource = tableViewDataSource
|
||||
self.originalCollectionViewDataSource = collectionViewDataSource
|
||||
self.rowHeight = rowHeight
|
||||
self.originalRowHeight = originalRowHeight
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +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)
|
||||
skeletonViewIfContainerSkeletonIsActive(container: tableView, view: cell)
|
||||
return cell
|
||||
}
|
||||
}
|
||||
@@ -54,7 +56,7 @@ 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)
|
||||
skeletonViewIfContainerSkeletonIsActive(container: collectionView, view: cell)
|
||||
return cell
|
||||
}
|
||||
|
||||
@@ -63,8 +65,9 @@ extension SkeletonCollectionDataSource: UICollectionViewDataSource {
|
||||
at indexPath: IndexPath) -> UICollectionReusableView {
|
||||
|
||||
if let viewIdentifier = originalCollectionViewDataSource?.collectionSkeletonView(collectionView, supplementaryViewIdentifierOfKind: kind, at: indexPath) {
|
||||
|
||||
return collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: viewIdentifier, for: indexPath)
|
||||
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: viewIdentifier, for: indexPath)
|
||||
skeletonViewIfContainerSkeletonIsActive(container: collectionView, view: view)
|
||||
return view
|
||||
}
|
||||
|
||||
return UICollectionReusableView()
|
||||
@@ -73,12 +76,12 @@ extension SkeletonCollectionDataSource: UICollectionViewDataSource {
|
||||
}
|
||||
|
||||
extension SkeletonCollectionDataSource {
|
||||
private func skeletonCellIfContainerSkeletonIsActive(container: UIView, cell: UIView) {
|
||||
private func skeletonViewIfContainerSkeletonIsActive(container: UIView, view: UIView) {
|
||||
guard container.isSkeletonActive,
|
||||
let skeletonConfig = container.currentSkeletonConfig else {
|
||||
return
|
||||
}
|
||||
|
||||
cell.showSkeleton(skeletonConfig: skeletonConfig)
|
||||
view.showSkeleton(skeletonConfig: skeletonConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,56 @@ class SkeletonCollectionDelegate: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
extension SkeletonCollectionDelegate: UITableViewDelegate { }
|
||||
// MARK: - UITableViewDelegate
|
||||
extension SkeletonCollectionDelegate: UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
if let viewIdentifier = originalTableViewDelegate?.collectionSkeletonView(tableView, identifierForHeaderInSection: section),
|
||||
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: viewIdentifier) {
|
||||
|
||||
// MARK: - UICollectionViewDataSource
|
||||
skeletonViewIfContainerSkeletonIsActive(container: tableView, view: header)
|
||||
return header
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
|
||||
if let viewIdentifier = originalTableViewDelegate?.collectionSkeletonView(tableView, identifierForFooterInSection: section),
|
||||
let footer = tableView.dequeueReusableHeaderFooterView(withIdentifier: viewIdentifier) {
|
||||
|
||||
skeletonViewIfContainerSkeletonIsActive(container: tableView, view: footer)
|
||||
return footer
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didEndDisplayingHeaderView view: UIView, forSection section: Int) {
|
||||
view.hideSkeleton()
|
||||
originalTableViewDelegate?.tableView?(tableView, didEndDisplayingHeaderView: view, forSection: section)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didEndDisplayingFooterView view: UIView, forSection section: Int) {
|
||||
view.hideSkeleton()
|
||||
originalTableViewDelegate?.tableView?(tableView, didEndDisplayingFooterView: view, forSection: section)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
cell.hideSkeleton()
|
||||
originalTableViewDelegate?.tableView?(tableView, didEndDisplaying: cell, forRowAt: indexPath)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UICollectionViewDelegate
|
||||
extension SkeletonCollectionDelegate: UICollectionViewDelegate { }
|
||||
|
||||
extension SkeletonCollectionDelegate {
|
||||
private func skeletonViewIfContainerSkeletonIsActive(container: UIView, view: UIView) {
|
||||
guard container.isSkeletonActive,
|
||||
let skeletonConfig = container.currentSkeletonConfig else {
|
||||
return
|
||||
}
|
||||
|
||||
view.showSkeleton(skeletonConfig: skeletonConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,4 +29,17 @@ public extension SkeletonTableViewDataSource {
|
||||
}
|
||||
}
|
||||
|
||||
public protocol SkeletonTableViewDelegate: UITableViewDelegate { }
|
||||
public protocol SkeletonTableViewDelegate: UITableViewDelegate {
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier?
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier?
|
||||
}
|
||||
|
||||
public extension SkeletonTableViewDelegate {
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
public typealias ReusableHeaderFooterIdentifier = String
|
||||
|
||||
extension UITableView: CollectionSkeleton {
|
||||
var estimatedNumberOfRows: Int {
|
||||
return Int(ceil(frame.height/rowHeight))
|
||||
@@ -33,10 +35,20 @@ extension UITableView: CollectionSkeleton {
|
||||
guard let originalDataSource = self.dataSource as? SkeletonTableViewDataSource,
|
||||
!(originalDataSource is SkeletonCollectionDataSource)
|
||||
else { return }
|
||||
let rowHeight = calculateRowHeight()
|
||||
let calculatedRowHeight = calculateRowHeight()
|
||||
let dataSource = SkeletonCollectionDataSource(tableViewDataSource: originalDataSource,
|
||||
rowHeight: rowHeight)
|
||||
rowHeight: rowHeight,
|
||||
originalRowHeight: self.rowHeight)
|
||||
rowHeight = calculatedRowHeight
|
||||
self.skeletonDataSource = dataSource
|
||||
|
||||
if let originalDelegate = self.delegate as? SkeletonTableViewDelegate,
|
||||
!(originalDelegate is SkeletonCollectionDelegate)
|
||||
{
|
||||
let delegate = SkeletonCollectionDelegate(tableViewDelegate: originalDelegate)
|
||||
self.skeletonDelegate = delegate
|
||||
}
|
||||
|
||||
reloadData()
|
||||
}
|
||||
|
||||
@@ -53,17 +65,22 @@ extension UITableView: CollectionSkeleton {
|
||||
restoreRowHeight()
|
||||
self.skeletonDataSource = nil
|
||||
self.dataSource = dataSource.originalTableViewDataSource
|
||||
|
||||
if let delegate = self.delegate as? SkeletonCollectionDelegate {
|
||||
self.skeletonDelegate = nil
|
||||
self.delegate = delegate.originalTableViewDelegate
|
||||
}
|
||||
|
||||
if reloadAfter { self.reloadData() }
|
||||
}
|
||||
|
||||
private func restoreRowHeight() {
|
||||
guard let dataSource = self.dataSource as? SkeletonCollectionDataSource else { return }
|
||||
rowHeight = dataSource.rowHeight
|
||||
rowHeight = dataSource.originalRowHeight
|
||||
}
|
||||
|
||||
private func calculateRowHeight() -> CGFloat {
|
||||
guard rowHeight == UITableView.automaticDimension else { return rowHeight }
|
||||
rowHeight = estimatedRowHeight
|
||||
return estimatedRowHeight
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,19 +10,22 @@ import UIKit
|
||||
|
||||
extension UIView {
|
||||
func addDummyDataSourceIfNeeded() {
|
||||
guard let collection = self as? CollectionSkeleton else { return }
|
||||
guard let collection = self as? CollectionSkeleton,
|
||||
!ProcessInfo.isRunningXCTest else { return }
|
||||
status = .on
|
||||
collection.addDummyDataSource()
|
||||
collection.disableUserInteraction()
|
||||
}
|
||||
|
||||
func updateDummyDataSourceIfNeeded() {
|
||||
guard let collection = self as? CollectionSkeleton else { return }
|
||||
guard let collection = self as? CollectionSkeleton,
|
||||
!ProcessInfo.isRunningXCTest else { return }
|
||||
collection.updateDummyDataSource()
|
||||
}
|
||||
|
||||
func removeDummyDataSourceIfNeeded(reloadAfter reload: Bool = true) {
|
||||
guard let collection = self as? CollectionSkeleton else { return }
|
||||
guard let collection = self as? CollectionSkeleton,
|
||||
!ProcessInfo.isRunningXCTest else { return }
|
||||
status = .off
|
||||
collection.removeDummyDataSource(reloadAfter: reload)
|
||||
collection.enableUserInteraction()
|
||||
|
||||
@@ -28,6 +28,16 @@ extension CAGradientLayer {
|
||||
}
|
||||
}
|
||||
|
||||
struct SkeletonMultilinesLayerConfig {
|
||||
var lines: Int
|
||||
var lineHeight: CGFloat? = nil
|
||||
var type: SkeletonType
|
||||
var lastLineFillPercent: Int
|
||||
var multilineCornerRadius: Int
|
||||
var multilineSpacing: CGFloat
|
||||
var paddingInsets: UIEdgeInsets
|
||||
}
|
||||
|
||||
|
||||
// MARK: Skeleton sublayers
|
||||
extension CALayer {
|
||||
@@ -37,15 +47,22 @@ extension CALayer {
|
||||
return sublayers?.filter { $0.name == CALayer.skeletonSubLayersName } ?? [CALayer]()
|
||||
}
|
||||
|
||||
func addMultilinesLayers(lines: Int, type: SkeletonType, lastLineFillPercent: Int, multilineCornerRadius: Int) {
|
||||
let numberOfSublayers = calculateNumLines(maxLines: lines)
|
||||
func addMultilinesLayers(for config: SkeletonMultilinesLayerConfig) {
|
||||
let numberOfSublayers = config.lines == 1 ? 1 : calculateNumLines(for: config)
|
||||
var height = config.lineHeight ?? SkeletonAppearance.default.multilineHeight
|
||||
if numberOfSublayers == 1 {
|
||||
height = bounds.height
|
||||
}
|
||||
|
||||
let layerBuilder = SkeletonMultilineLayerBuilder()
|
||||
.setSkeletonType(type)
|
||||
.setCornerRadius(multilineCornerRadius)
|
||||
|
||||
.setSkeletonType(config.type)
|
||||
.setCornerRadius(config.multilineCornerRadius)
|
||||
.setMultilineSpacing(config.multilineSpacing)
|
||||
.setPadding(config.paddingInsets)
|
||||
.setHeight(height)
|
||||
|
||||
(0..<numberOfSublayers).forEach { index in
|
||||
let width = calculatedWidthForLine(at: index, totalLines: numberOfSublayers, lastLineFillPercent: lastLineFillPercent)
|
||||
let width = calculatedWidthForLine(at: index, totalLines: numberOfSublayers, lastLineFillPercent: config.lastLineFillPercent, paddingInsets: config.paddingInsets)
|
||||
if let layer = layerBuilder
|
||||
.setIndex(index)
|
||||
.setWidth(width)
|
||||
@@ -54,33 +71,41 @@ extension CALayer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateMultilinesLayers(lastLineFillPercent: Int) {
|
||||
|
||||
func updateMultilinesLayers(for config: SkeletonMultilinesLayerConfig) {
|
||||
let currentSkeletonSublayers = skeletonSublayers
|
||||
let numberOfSublayers = currentSkeletonSublayers.count
|
||||
let lastLineFillPercent = config.lastLineFillPercent
|
||||
let paddingInsets = config.paddingInsets
|
||||
let multilineSpacing = config.multilineSpacing
|
||||
var height = config.lineHeight ?? SkeletonAppearance.default.multilineHeight
|
||||
if numberOfSublayers == 1 {
|
||||
height = bounds.height
|
||||
}
|
||||
|
||||
for (index, layer) in currentSkeletonSublayers.enumerated() {
|
||||
let width = calculatedWidthForLine(at: index, totalLines: numberOfSublayers, lastLineFillPercent: lastLineFillPercent)
|
||||
layer.updateLayerFrame(for: index, width: width)
|
||||
let width = calculatedWidthForLine(at: index, totalLines: numberOfSublayers, lastLineFillPercent: lastLineFillPercent, paddingInsets: paddingInsets)
|
||||
layer.updateLayerFrame(for: index, size: CGSize(width: width, height: height), multilineSpacing: multilineSpacing, paddingInsets: paddingInsets)
|
||||
}
|
||||
}
|
||||
|
||||
private func calculatedWidthForLine(at index: Int, totalLines: Int, lastLineFillPercent: Int) -> CGFloat {
|
||||
var width = bounds.width
|
||||
private func calculatedWidthForLine(at index: Int, totalLines: Int, lastLineFillPercent: Int, paddingInsets: UIEdgeInsets) -> CGFloat {
|
||||
var width = bounds.width - paddingInsets.left - paddingInsets.right
|
||||
if index == totalLines - 1 && totalLines != 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)
|
||||
func updateLayerFrame(for index: Int, size: CGSize, multilineSpacing: CGFloat, paddingInsets: UIEdgeInsets) {
|
||||
let spaceRequiredForEachLine = SkeletonAppearance.default.multilineHeight + multilineSpacing
|
||||
frame = CGRect(x: paddingInsets.left, y: CGFloat(index) * spaceRequiredForEachLine + paddingInsets.top, width: size.width, height: size.height)
|
||||
}
|
||||
|
||||
private func calculateNumLines(maxLines: Int) -> Int {
|
||||
let requiredSpaceForEachLine = SkeletonAppearance.default.multilineHeight + SkeletonAppearance.default.multilineSpacing
|
||||
var numberOfSublayers = Int(round(CGFloat(bounds.height)/CGFloat(requiredSpaceForEachLine)))
|
||||
if maxLines != 0, maxLines <= numberOfSublayers { numberOfSublayers = maxLines }
|
||||
private func calculateNumLines(for config: SkeletonMultilinesLayerConfig) -> Int {
|
||||
let requiredSpaceForEachLine = (config.lineHeight ?? SkeletonAppearance.default.multilineHeight) + config.multilineSpacing
|
||||
var numberOfSublayers = Int(round(CGFloat(bounds.height - config.paddingInsets.top - config.paddingInsets.bottom)/CGFloat(requiredSpaceForEachLine)))
|
||||
if config.lines != 0, config.lines <= numberOfSublayers { numberOfSublayers = config.lines }
|
||||
return numberOfSublayers
|
||||
}
|
||||
}
|
||||
@@ -139,15 +164,15 @@ public extension CALayer {
|
||||
}
|
||||
|
||||
extension CALayer {
|
||||
func setOpacity(from: Int, to: Int, duration: TimeInterval, completion: (() -> Void)?) {
|
||||
func setOpacity(from: Int, to: Int, duration: TimeInterval, completion: (() -> Void)?) {
|
||||
DispatchQueue.main.async { CATransaction.begin() }
|
||||
let animation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
|
||||
animation.fromValue = from
|
||||
animation.toValue = to
|
||||
animation.duration = duration
|
||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
|
||||
let animation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
|
||||
animation.fromValue = from
|
||||
animation.toValue = to
|
||||
animation.duration = duration
|
||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
|
||||
DispatchQueue.main.async { CATransaction.setCompletionBlock(completion) }
|
||||
add(animation, forKey: "setOpacityAnimation")
|
||||
add(animation, forKey: "setOpacityAnimation")
|
||||
DispatchQueue.main.async { CATransaction.commit() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright © 2020 SkeletonView. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension ProcessInfo {
|
||||
enum Constants {
|
||||
static let testConfigurationFilePathKey = "XCTestConfigurationFilePath"
|
||||
}
|
||||
|
||||
static var isRunningXCTest: Bool {
|
||||
return processInfo.environment[Constants.testConfigurationFilePathKey] != nil
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,13 @@ extension UIColor {
|
||||
}
|
||||
|
||||
public var complementaryColor: UIColor {
|
||||
return isLight() ? darker : lighter
|
||||
if #available(iOS 13, tvOS 13, *) {
|
||||
return UIColor { traitCollection in
|
||||
return self.isLight() ? self.darker : self.lighter
|
||||
}
|
||||
} else {
|
||||
return isLight() ? darker : lighter
|
||||
}
|
||||
}
|
||||
|
||||
public var lighter: UIColor {
|
||||
@@ -58,11 +64,25 @@ public extension UIColor {
|
||||
static var carrot = UIColor(0xe67e22)
|
||||
static var alizarin = UIColor(0xe74c3c)
|
||||
static var clouds = UIColor(0xecf0f1)
|
||||
static var darkClouds = UIColor(0x1c2325)
|
||||
static var concrete = UIColor(0x95a5a6)
|
||||
static var flatOrange = UIColor(0xf39c12)
|
||||
static var pumpkin = UIColor(0xd35400)
|
||||
static var pomegranate = UIColor(0xc0392b)
|
||||
static var silver = UIColor(0xbdc3c7)
|
||||
static var asbestos = UIColor(0x7f8c8d)
|
||||
|
||||
static var skeletonDefault: UIColor {
|
||||
if #available(iOS 13, tvOS 13, *) {
|
||||
return UIColor { traitCollection in
|
||||
switch traitCollection.userInterfaceStyle {
|
||||
case .dark: return .darkClouds
|
||||
default: return .clouds
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .clouds
|
||||
}
|
||||
}
|
||||
}
|
||||
// codebeat:enable[TOO_MANY_IVARS]
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright © 2020 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UITableView {
|
||||
var indexesOfVisibleSections: [Int] {
|
||||
(0..<numberOfSections).reduce([]) {
|
||||
let headerRect: CGRect?
|
||||
|
||||
if self.style == .plain {
|
||||
headerRect = rect(forSection: $1)
|
||||
} else {
|
||||
headerRect = rectForHeader(inSection: $1)
|
||||
}
|
||||
|
||||
if headerRect != nil {
|
||||
let visiblePartOfTableView: CGRect = CGRect(
|
||||
x: contentOffset.x,
|
||||
y: contentOffset.y,
|
||||
width: bounds.size.width,
|
||||
height: bounds.size.height
|
||||
)
|
||||
|
||||
if (visiblePartOfTableView.intersects(headerRect!)) {
|
||||
return $0 + [$1]
|
||||
}
|
||||
}
|
||||
|
||||
return $0
|
||||
}
|
||||
}
|
||||
|
||||
var visibleSectionHeaders: [UITableViewHeaderFooterView] {
|
||||
indexesOfVisibleSections.compactMap { headerView(forSection: $0) }
|
||||
}
|
||||
|
||||
var visibleSectionFooters: [UITableViewHeaderFooterView] {
|
||||
indexesOfVisibleSections.compactMap { footerView(forSection: $0) }
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ enum ViewAssociatedKeys {
|
||||
static var labelViewState = "labelViewState"
|
||||
static var imageViewState = "imageViewState"
|
||||
static var currentSkeletonConfig = "currentSkeletonConfig"
|
||||
static var skeletonCornerRadius = "skeletonCornerRadius"
|
||||
}
|
||||
// codebeat:enable[TOO_MANY_IVARS]
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@ extension UIView {
|
||||
if let parentStackView = (superview as? UIStackView) {
|
||||
var origin: CGPoint = .zero
|
||||
switch parentStackView.alignment {
|
||||
case .center:
|
||||
origin.x = maxWidthEstimated / 2
|
||||
case .trailing:
|
||||
origin.x = maxWidthEstimated
|
||||
default:
|
||||
|
||||
@@ -8,6 +8,12 @@ public extension UIView {
|
||||
get { return skeletonable }
|
||||
set { skeletonable = newValue }
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var skeletonCornerRadius: Float {
|
||||
get { return skeletonableCornerRadius }
|
||||
set { skeletonableCornerRadius = newValue }
|
||||
}
|
||||
|
||||
var isSkeletonActive: Bool {
|
||||
return status == .on || (subviewsSkeletonables.first(where: { $0.isSkeletonActive }) != nil)
|
||||
@@ -17,5 +23,10 @@ public extension UIView {
|
||||
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonable) as? Bool ?? false }
|
||||
set { ao_set(newValue ?? false, pkey: &ViewAssociatedKeys.skeletonable) }
|
||||
}
|
||||
|
||||
private var skeletonableCornerRadius: Float! {
|
||||
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonCornerRadius) as? Float ?? 0.0 }
|
||||
set { ao_set(newValue ?? 0.0, pkey: &ViewAssociatedKeys.skeletonCornerRadius) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,17 @@ import UIKit
|
||||
enum MultilineAssociatedKeys {
|
||||
static var lastLineFillingPercent = "lastLineFillingPercent"
|
||||
static var multilineCornerRadius = "multilineCornerRadius"
|
||||
static var multilineSpacing = "multilineSpacing"
|
||||
static var paddingInsets = "paddingInsets"
|
||||
}
|
||||
|
||||
protocol ContainsMultilineText {
|
||||
var multilineTextFont: UIFont? { get }
|
||||
var numLines: Int { get }
|
||||
var lastLineFillingPercent: Int { get }
|
||||
var multilineCornerRadius: Int { get }
|
||||
var multilineSpacing: CGFloat { get }
|
||||
var paddingInsets: UIEdgeInsets { get }
|
||||
}
|
||||
|
||||
extension ContainsMultilineText {
|
||||
|
||||
@@ -13,9 +13,23 @@ public extension UILabel {
|
||||
get { return multilineCornerRadius }
|
||||
set { multilineCornerRadius = min(newValue, 10) }
|
||||
}
|
||||
@IBInspectable
|
||||
var skeletonLineSpacing: CGFloat {
|
||||
get { return multilineSpacing }
|
||||
set { multilineSpacing = min(newValue, 10) }
|
||||
}
|
||||
@IBInspectable
|
||||
var skeletonPaddingInsets: UIEdgeInsets {
|
||||
get { return paddingInsets }
|
||||
set { paddingInsets = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
extension UILabel: ContainsMultilineText {
|
||||
var multilineTextFont: UIFont? {
|
||||
return font
|
||||
}
|
||||
|
||||
var numLines: Int {
|
||||
return numberOfLines
|
||||
}
|
||||
@@ -29,4 +43,14 @@ extension UILabel: ContainsMultilineText {
|
||||
get { return ao_get(pkey: &MultilineAssociatedKeys.multilineCornerRadius) as? Int ?? SkeletonAppearance.default.multilineCornerRadius }
|
||||
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.multilineCornerRadius) }
|
||||
}
|
||||
|
||||
var multilineSpacing: CGFloat {
|
||||
get { return ao_get(pkey: &MultilineAssociatedKeys.multilineSpacing) as? CGFloat ?? SkeletonAppearance.default.multilineSpacing }
|
||||
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.multilineSpacing) }
|
||||
}
|
||||
|
||||
var paddingInsets: UIEdgeInsets {
|
||||
get { return ao_get(pkey: &MultilineAssociatedKeys.paddingInsets) as? UIEdgeInsets ?? .zero }
|
||||
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.paddingInsets) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,25 @@ public extension UITextView {
|
||||
get { return multilineCornerRadius }
|
||||
set { multilineCornerRadius = min(newValue, 10) }
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var skeletonLineSpacing: CGFloat {
|
||||
get { return multilineSpacing }
|
||||
set { multilineSpacing = min(newValue, 10) }
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var skeletonPaddingInsets: UIEdgeInsets {
|
||||
get { return paddingInsets }
|
||||
set { paddingInsets = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
extension UITextView: ContainsMultilineText {
|
||||
var multilineTextFont: UIFont? {
|
||||
return font
|
||||
}
|
||||
|
||||
var lastLineFillingPercent: Int {
|
||||
get {
|
||||
let defaultValue = SkeletonAppearance.default.multilineLastLineFillPercent
|
||||
@@ -32,4 +48,14 @@ extension UITextView: ContainsMultilineText {
|
||||
}
|
||||
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.multilineCornerRadius) }
|
||||
}
|
||||
|
||||
var multilineSpacing: CGFloat {
|
||||
get { return ao_get(pkey: &MultilineAssociatedKeys.multilineSpacing) as? CGFloat ?? SkeletonAppearance.default.multilineSpacing }
|
||||
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.multilineSpacing) }
|
||||
}
|
||||
|
||||
var paddingInsets: UIEdgeInsets {
|
||||
get { return ao_get(pkey: &MultilineAssociatedKeys.paddingInsets) as? UIEdgeInsets ?? .zero }
|
||||
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.paddingInsets) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ extension UILabel{
|
||||
startTransition { [weak self] in
|
||||
self?.textColor = self?.labelState?.textColor
|
||||
self?.text = self?.labelState?.text
|
||||
self?.isUserInteractionEnabled = self?.labelState?.isUserInteractionsEnabled ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,6 +74,7 @@ extension UITextView{
|
||||
startTransition { [weak self] in
|
||||
self?.textColor = self?.textState?.textColor
|
||||
self?.text = self?.textState?.text
|
||||
self?.isUserInteractionEnabled = self?.textState?.isUserInteractionsEnabled ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,15 +23,18 @@ struct RecoverableViewState {
|
||||
struct RecoverableTextViewState {
|
||||
var text: String?
|
||||
var textColor: UIColor?
|
||||
var isUserInteractionsEnabled: Bool
|
||||
|
||||
init(view: UILabel) {
|
||||
self.textColor = view.textColor
|
||||
self.text = view.text
|
||||
self.isUserInteractionsEnabled = view.isUserInteractionEnabled
|
||||
}
|
||||
|
||||
init(view: UITextView) {
|
||||
self.textColor = view.textColor
|
||||
self.text = view.text
|
||||
self.isUserInteractionsEnabled = view.isUserInteractionEnabled
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ struct SkeletonConfig {
|
||||
gradientDirection: GradientDirection? = nil,
|
||||
animated: Bool = false,
|
||||
animation: SkeletonLayerAnimation? = nil,
|
||||
transition: SkeletonTransitionStyle = .none
|
||||
transition: SkeletonTransitionStyle = .crossDissolve(0.25)
|
||||
) {
|
||||
self.type = type
|
||||
self.colors = colors
|
||||
|
||||
@@ -15,17 +15,21 @@ protocol SkeletonFlowDelegate {
|
||||
|
||||
class SkeletonFlowHandler: SkeletonFlowDelegate {
|
||||
func willBeginShowingSkeletons(rootView: UIView) {
|
||||
NotificationCenter.default.post(name: .willBeginShowingSkeletons, object: rootView, userInfo: nil)
|
||||
rootView.addAppNotificationsObservers()
|
||||
}
|
||||
|
||||
func didShowSkeletons(rootView: UIView) {
|
||||
printSkeletonHierarchy(in: rootView)
|
||||
NotificationCenter.default.post(name: .didShowSkeletons, object: rootView, userInfo: nil)
|
||||
}
|
||||
|
||||
|
||||
func willBeginUpdatingSkeletons(rootView: UIView) {
|
||||
NotificationCenter.default.post(name: .willBeginUpdatingSkeletons, object: rootView, userInfo: nil)
|
||||
}
|
||||
|
||||
func didUpdateSkeletons(rootView: UIView) {
|
||||
NotificationCenter.default.post(name: .didUpdateSkeletons, object: rootView, userInfo: nil)
|
||||
}
|
||||
|
||||
func willBeginLayingSkeletonsIfNeeded(rootView: UIView) {
|
||||
@@ -35,10 +39,21 @@ class SkeletonFlowHandler: SkeletonFlowDelegate {
|
||||
}
|
||||
|
||||
func willBeginHidingSkeletons(rootView: UIView) {
|
||||
NotificationCenter.default.post(name: .willBeginHidingSkeletons, object: rootView, userInfo: nil)
|
||||
rootView.removeAppNoticationsObserver()
|
||||
}
|
||||
|
||||
func didHideSkeletons(rootView: UIView) {
|
||||
rootView.flowDelegate = nil
|
||||
NotificationCenter.default.post(name: .didHideSkeletons, object: rootView, userInfo: nil)
|
||||
}
|
||||
}
|
||||
|
||||
public extension Notification.Name {
|
||||
static let willBeginShowingSkeletons = Notification.Name("willBeginShowingSkeletons")
|
||||
static let didShowSkeletons = Notification.Name("didShowSkeletons")
|
||||
static let willBeginUpdatingSkeletons = Notification.Name("willBeginUpdatingSkeletons")
|
||||
static let didUpdateSkeletons = Notification.Name("didUpdateSkeletons")
|
||||
static let willBeginHidingSkeletons = Notification.Name("willBeginHidingSkeletons")
|
||||
static let didHideSkeletons = Notification.Name("didHideSkeletons")
|
||||
}
|
||||
|
||||
+40
-21
@@ -50,7 +50,8 @@ struct SkeletonLayer {
|
||||
self.maskLayer = type.layer
|
||||
self.maskLayer.anchorPoint = .zero
|
||||
self.maskLayer.bounds = holder.maxBoundsEstimated
|
||||
addMultilinesIfNeeded()
|
||||
self.maskLayer.cornerRadius = CGFloat(holder.skeletonCornerRadius)
|
||||
addTextLinesIfNeeded()
|
||||
self.maskLayer.tint(withColors: colors)
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ struct SkeletonLayer {
|
||||
if let bounds = holder?.maxBoundsEstimated {
|
||||
maskLayer.bounds = bounds
|
||||
}
|
||||
updateMultilinesIfNeeded()
|
||||
updateLinesIfNeeded()
|
||||
}
|
||||
|
||||
func removeLayer(transition: SkeletonTransitionStyle, completion: (() -> Void)? = nil) {
|
||||
@@ -72,29 +73,47 @@ struct SkeletonLayer {
|
||||
maskLayer.removeFromSuperlayer()
|
||||
completion?()
|
||||
case .crossDissolve(let duration):
|
||||
maskLayer.setOpacity(from: 1, to: 0, duration: duration) {
|
||||
self.maskLayer.removeFromSuperlayer()
|
||||
completion?()
|
||||
}
|
||||
maskLayer.setOpacity(from: 1, to: 0, duration: duration) {
|
||||
self.maskLayer.removeFromSuperlayer()
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a multiLineViewHolder only if the current holder implements the `ContainsMultilineText` protocol,
|
||||
/// and actually displays multiple lines of text.
|
||||
private var multiLineViewHolder: ContainsMultilineText? {
|
||||
guard let multiLineView = holder as? ContainsMultilineText,
|
||||
multiLineView.numLines != 1 else { return nil }
|
||||
return multiLineView
|
||||
}
|
||||
/// If there is more than one line, or custom preferences have been set for a single line, draw custom layers
|
||||
func addTextLinesIfNeeded() {
|
||||
guard let textView = holderAsTextView else { return }
|
||||
|
||||
let config = SkeletonMultilinesLayerConfig(lines: textView.numLines,
|
||||
lineHeight: textView.multilineTextFont?.lineHeight,
|
||||
type: type,
|
||||
lastLineFillPercent: textView.lastLineFillingPercent,
|
||||
multilineCornerRadius: textView.multilineCornerRadius,
|
||||
multilineSpacing: textView.multilineSpacing,
|
||||
paddingInsets: textView.paddingInsets)
|
||||
|
||||
func addMultilinesIfNeeded() {
|
||||
guard let multiLineView = multiLineViewHolder else { return }
|
||||
maskLayer.addMultilinesLayers(lines: multiLineView.numLines, type: type, lastLineFillPercent: multiLineView.lastLineFillingPercent, multilineCornerRadius: multiLineView.multilineCornerRadius)
|
||||
maskLayer.addMultilinesLayers(for: config)
|
||||
}
|
||||
|
||||
func updateMultilinesIfNeeded() {
|
||||
guard let multiLineView = multiLineViewHolder else { return }
|
||||
maskLayer.updateMultilinesLayers(lastLineFillPercent: multiLineView.lastLineFillingPercent)
|
||||
|
||||
func updateLinesIfNeeded() {
|
||||
guard let textView = holderAsTextView else { return }
|
||||
let config = SkeletonMultilinesLayerConfig(lines: textView.numLines,
|
||||
lineHeight: textView.multilineTextFont?.lineHeight,
|
||||
type: type,
|
||||
lastLineFillPercent: textView.lastLineFillingPercent,
|
||||
multilineCornerRadius: textView.multilineCornerRadius,
|
||||
multilineSpacing: textView.multilineSpacing,
|
||||
paddingInsets: textView.paddingInsets)
|
||||
|
||||
maskLayer.updateMultilinesLayers(for: config)
|
||||
}
|
||||
|
||||
var holderAsTextView: ContainsMultilineText? {
|
||||
guard let textView = holder as? ContainsMultilineText,
|
||||
(textView.numLines == 0 || textView.numLines > 1 || textView.numLines == 1 && !SkeletonAppearance.default.renderSingleLineAsView) else {
|
||||
return nil
|
||||
}
|
||||
return textView
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +122,7 @@ extension SkeletonLayer {
|
||||
let animation = anim ?? type.layerAnimation
|
||||
contentLayer.playAnimation(animation, key: "skeletonAnimation", completion: completion)
|
||||
}
|
||||
|
||||
|
||||
func stopAnimation() {
|
||||
contentLayer.stopAnimation(forKey: "skeletonAnimation")
|
||||
}
|
||||
|
||||
+36
-15
@@ -7,8 +7,8 @@ public extension UIView {
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - color: The color of the skeleton. Defaults to `SkeletonAppearance.default.tintColor`.
|
||||
/// - transition: The style of the transition when the skeleton appears. Defaults to `.none`.
|
||||
func showSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor, transition: SkeletonTransitionStyle = .none) {
|
||||
/// - transition: The style of the transition when the skeleton appears. Defaults to `.crossDissolve(0.25)`.
|
||||
func showSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor, transition: SkeletonTransitionStyle = .crossDissolve(0.25)) {
|
||||
let config = SkeletonConfig(type: .solid, colors: [color], transition: transition)
|
||||
showSkeleton(skeletonConfig: config)
|
||||
}
|
||||
@@ -17,8 +17,8 @@ public extension UIView {
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - gradient: The gradient of the skeleton. Defaults to `SkeletonAppearance.default.gradient`.
|
||||
/// - transition: The style of the transition when the skeleton appears. Defaults to `.none`.
|
||||
func showGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient, transition: SkeletonTransitionStyle = .none) {
|
||||
/// - transition: The style of the transition when the skeleton appears. Defaults to `.crossDissolve(0.25)`.
|
||||
func showGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient, transition: SkeletonTransitionStyle = .crossDissolve(0.25)) {
|
||||
let config = SkeletonConfig(type: .gradient, colors: gradient.colors, transition: transition)
|
||||
showSkeleton(skeletonConfig: config)
|
||||
}
|
||||
@@ -30,8 +30,8 @@ public extension UIView {
|
||||
/// - Parameters:
|
||||
/// - color: The color of skeleton. Defaults to `SkeletonAppearance.default.tintColor`.
|
||||
/// - animation: The animation of the skeleton. Defaults to `nil`.
|
||||
/// - transition: The style of the transition when the skeleton appears. Defaults to `.none`.
|
||||
func showAnimatedSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor, animation: SkeletonLayerAnimation? = nil, transition: SkeletonTransitionStyle = .none) {
|
||||
/// - transition: The style of the transition when the skeleton appears. Defaults to `.crossDissolve(0.25)`.
|
||||
func showAnimatedSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor, animation: SkeletonLayerAnimation? = nil, transition: SkeletonTransitionStyle = .crossDissolve(0.25)) {
|
||||
let config = SkeletonConfig(type: .solid, colors: [color], animated: true, animation: animation, transition: transition)
|
||||
showSkeleton(skeletonConfig: config)
|
||||
}
|
||||
@@ -43,8 +43,8 @@ public extension UIView {
|
||||
/// - Parameters:
|
||||
/// - gradient: The gradient of the skeleton. Defaults to `SkeletonAppearance.default.gradient`.
|
||||
/// - animation: The animation of the skeleton. Defaults to `nil`.
|
||||
/// - transition: The style of the transition when the skeleton appears. Defaults to `.none`.
|
||||
func showAnimatedGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient, animation: SkeletonLayerAnimation? = nil, transition: SkeletonTransitionStyle = .none) {
|
||||
/// - transition: The style of the transition when the skeleton appears. Defaults to `.crossDissolve(0.25)`.
|
||||
func showAnimatedGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient, animation: SkeletonLayerAnimation? = nil, transition: SkeletonTransitionStyle = .crossDissolve(0.25)) {
|
||||
let config = SkeletonConfig(type: .gradient, colors: gradient.colors, animated: true, animation: animation, transition: transition)
|
||||
showSkeleton(skeletonConfig: config)
|
||||
}
|
||||
@@ -74,7 +74,7 @@ public extension UIView {
|
||||
recursiveLayoutSkeletonIfNeeded(root: self)
|
||||
}
|
||||
|
||||
func hideSkeleton(reloadDataAfter reload: Bool = true, transition: SkeletonTransitionStyle = .none) {
|
||||
func hideSkeleton(reloadDataAfter reload: Bool = true, transition: SkeletonTransitionStyle = .crossDissolve(0.25)) {
|
||||
flowDelegate?.willBeginHidingSkeletons(rootView: self)
|
||||
recursiveHideSkeleton(reloadDataAfter: reload, transition: transition, root: self)
|
||||
}
|
||||
@@ -94,9 +94,17 @@ public extension UIView {
|
||||
|
||||
extension UIView {
|
||||
@objc func skeletonLayoutSubviews() {
|
||||
guard Thread.isMainThread else { return }
|
||||
skeletonLayoutSubviews()
|
||||
guard isSkeletonActive else { return }
|
||||
layoutSkeletonIfNeeded()
|
||||
}
|
||||
|
||||
@objc func skeletonTraitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
skeletonTraitCollectionDidChange(previousTraitCollection)
|
||||
guard isSkeletonable, isSkeletonActive, let config = currentSkeletonConfig else { return }
|
||||
updateSkeleton(skeletonConfig: config)
|
||||
}
|
||||
|
||||
func showSkeleton(skeletonConfig config: SkeletonConfig) {
|
||||
isSkeletonAnimated = config.animated
|
||||
@@ -106,9 +114,10 @@ extension UIView {
|
||||
}
|
||||
|
||||
private func recursiveShowSkeleton(skeletonConfig config: SkeletonConfig, root: UIView? = nil) {
|
||||
guard !isSkeletonActive else { return }
|
||||
guard isSkeletonable && !isSkeletonActive else { return }
|
||||
currentSkeletonConfig = config
|
||||
swizzleLayoutSubviews()
|
||||
swizzleTraitCollectionDidChange()
|
||||
addDummyDataSourceIfNeeded()
|
||||
subviewsSkeletonables.recursiveSearch(leafBlock: {
|
||||
showSkeletonIfNotActive(skeletonConfig: config)
|
||||
@@ -123,8 +132,8 @@ extension UIView {
|
||||
|
||||
private func showSkeletonIfNotActive(skeletonConfig config: SkeletonConfig) {
|
||||
guard !isSkeletonActive else { return }
|
||||
isUserInteractionEnabled = false
|
||||
saveViewState()
|
||||
isUserInteractionEnabled = false
|
||||
prepareViewForSkeleton()
|
||||
addSkeletonLayer(skeletonConfig: config)
|
||||
}
|
||||
@@ -140,7 +149,8 @@ extension UIView {
|
||||
currentSkeletonConfig = config
|
||||
updateDummyDataSourceIfNeeded()
|
||||
subviewsSkeletonables.recursiveSearch(leafBlock: {
|
||||
if skeletonLayer?.type != config.type {
|
||||
if let skeletonLayer = skeletonLayer,
|
||||
skeletonLayer.type != config.type {
|
||||
removeSkeletonLayer()
|
||||
addSkeletonLayer(skeletonConfig: config)
|
||||
} else {
|
||||
@@ -173,16 +183,16 @@ extension UIView {
|
||||
|
||||
private func recursiveHideSkeleton(reloadDataAfter reload: Bool, transition: SkeletonTransitionStyle, root: UIView? = nil) {
|
||||
guard isSkeletonActive else { return }
|
||||
removeDummyDataSourceIfNeeded(reloadAfter: reload)
|
||||
currentSkeletonConfig?.transition = transition
|
||||
isUserInteractionEnabled = true
|
||||
removeDummyDataSourceIfNeeded(reloadAfter: reload)
|
||||
subviewsSkeletonables.recursiveSearch(leafBlock: {
|
||||
recoverViewState(forced: false)
|
||||
removeSkeletonLayer()
|
||||
}) { subview in
|
||||
subview.recursiveHideSkeleton(reloadDataAfter: reload, transition: transition)
|
||||
}
|
||||
|
||||
|
||||
if let root = root {
|
||||
flowDelegate?.didHideSkeletons(rootView: root)
|
||||
}
|
||||
@@ -208,7 +218,7 @@ extension UIView {
|
||||
|
||||
private func swizzleLayoutSubviews() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
||||
DispatchQueue.once(token: "UIView.SkeletonView.swizzle") {
|
||||
DispatchQueue.once(token: "UIView.SkeletonView.swizzleLayoutSubviews") {
|
||||
swizzle(selector: #selector(UIView.layoutSubviews),
|
||||
with: #selector(UIView.skeletonLayoutSubviews),
|
||||
inClass: UIView.self,
|
||||
@@ -217,6 +227,17 @@ extension UIView {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func swizzleTraitCollectionDidChange() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
||||
DispatchQueue.once(token: "UIView.SkeletonView.swizzleTraitCollectionDidChange") {
|
||||
swizzle(selector: #selector(UIView.traitCollectionDidChange(_:)),
|
||||
with: #selector(UIView.skeletonTraitCollectionDidChange(_:)),
|
||||
inClass: UIView.self,
|
||||
usingClass: UIView.self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
@@ -14,7 +14,7 @@ extension UIView {
|
||||
|
||||
extension UITableView {
|
||||
override var subviewsToSkeleton: [UIView] {
|
||||
return visibleCells
|
||||
return visibleCells + visibleSectionHeaders + visibleSectionFooters
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,12 @@ extension UITableViewCell {
|
||||
}
|
||||
}
|
||||
|
||||
extension UITableViewHeaderFooterView {
|
||||
override var subviewsToSkeleton: [UIView] {
|
||||
return contentView.subviews
|
||||
}
|
||||
}
|
||||
|
||||
extension UICollectionView {
|
||||
override var subviewsToSkeleton: [UIView] {
|
||||
return subviews
|
||||
|
||||
Reference in New Issue
Block a user