Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| db19eae968 | |||
| 1d2256b4e6 | |||
| 4e5c8627dd | |||
| 09e1fe31e0 | |||
| 303ae5aeb8 | |||
| 4649c7e61f | |||
| 83e7e48caa | |||
| c03af15070 | |||
| ed82f74ba8 | |||
| 01aba0b3ff | |||
| 6352030012 | |||
| 4b06c30b9d | |||
| 3dcde46fe9 | |||
| c9de28eb37 | |||
| 915e838994 | |||
| 74b44264ec | |||
| a9ad72aa0c | |||
| d535529be8 | |||
| a6bff05ca4 | |||
| bc98cd600e | |||
| 8f1cfc0045 | |||
| 432ec6caa2 | |||
| e18aedbbf8 | |||
| 4dc67ac3d7 | |||
| aa29cd3d6b | |||
| a2def55880 | |||
| 97f5e3e2e1 | |||
| 420cc7a669 | |||
| b9601f56c3 | |||
| fbca1092c3 | |||
| eb90faf413 | |||
| f0cc7125ef | |||
| f5b493d6f0 | |||
| 48bc217413 | |||
| 516fcecce2 | |||
| 9052d5d806 | |||
| 12dff52238 | |||
| f0b4575de6 | |||
| 7be8ddd70a | |||
| a9628c0de8 | |||
| 339191f016 | |||
| 7022942d92 | |||
| 03cf1bfea8 | |||
| 9f46e7a647 | |||
| 674cd1aa9f | |||
| 36a863b557 | |||
| 3b83b85249 | |||
| afbb10d785 | |||
| 99ccd1e640 | |||
| e6834d1296 | |||
| 89cb6f4116 | |||
| b74592e6be | |||
| 5f79708f03 | |||
| a83e1a00bf | |||
| 61e98b26ad | |||
| 0ec754dfc5 | |||
| 78b4b7b31c | |||
| e3ddc4fe98 | |||
| 13485e61f7 | |||
| 8f5d409304 | |||
| bc3258b8a2 | |||
| fd4683f555 | |||
| 97ebbcc917 | |||
| a02922dba6 | |||
| 4ac1abe50e | |||
| 25e313850e | |||
| 88914f7816 | |||
| 14ab00d13a | |||
| 90b993a16b | |||
| 413867718e | |||
| e43b3f3750 | |||
| b9b368b7e8 | |||
| fc22d58f89 | |||
| 8988b22784 | |||
| 3cb1602e11 | |||
| 649672182e | |||
| 3c16416dbb | |||
| b90e97592a | |||
| 9fbaaa02f5 | |||
| 3f7f2ec95c | |||
| 26da7e3fc0 | |||
| 804390e98a | |||
| 3bf613da0d | |||
| 0eff052314 | |||
| af91af3716 | |||
| 2fe2323cc1 | |||
| 024171a993 | |||
| 726817cfe5 | |||
| 9684817956 | |||
| 3a847ece3d | |||
| a0fb735c3e | |||
| c2720898ac | |||
| 9e395c7747 | |||
| b8aa587af6 | |||
| 349f7eb48e | |||
| 4f874a84f0 | |||
| a6de5342a4 | |||
| 404da95cbe | |||
| ebe7f3e463 | |||
| ad5cf2a21f | |||
| 7afe87160a | |||
| 7c7be7c80c | |||
| ff42bd02e2 | |||
| 0136c8576c | |||
| 76c526385d | |||
| 2b633e5ab6 | |||
| 5c19df67df | |||
| b1778542d7 | |||
| ad5c87eea2 | |||
| 09813e530c | |||
| 23489159dd | |||
| c06ae2d72c | |||
| 02b56f2707 | |||
| fe40f4b6bf | |||
| 8b4f0b38b1 | |||
| 44ce71e6b2 | |||
| d4ea1e78c2 | |||
| 00dc7e7b5d | |||
| 3f947c08ca | |||
| 5d8a30ba82 | |||
| aa08656fdd | |||
| 15b1a33ade | |||
| d66adce904 | |||
| 06ddf69bd4 | |||
| bd0640a4c4 | |||
| 53a276c66b | |||
| be7fc21d03 | |||
| 3ac704eb07 | |||
| 987d12cbc5 | |||
| a37948b6c1 | |||
| 2b15498ef3 | |||
| bcde3e2ed6 | |||
| 719a5f507b | |||
| 340f792880 | |||
| f516eab8e2 | |||
| 3506ffbfb5 | |||
| 2a895dfe35 | |||
| 261ff5a451 | |||
| e0fbf88912 |
@@ -1,5 +1,7 @@
|
||||
⚠️ Please fill out this template when filing an issue.
|
||||
|
||||
#### 🙏🏼 *Please check if it already exists other issue related with yours.*
|
||||
|
||||
### What did you do?
|
||||
|
||||
*Please replace this with what you did.*
|
||||
|
||||
@@ -46,7 +46,7 @@ playground.xcworkspace
|
||||
# you should judge for yourself, the pros and cons are mentioned at:
|
||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||
#
|
||||
# Pods/
|
||||
Pods/
|
||||
|
||||
# Carthage
|
||||
#
|
||||
|
||||
@@ -1 +1 @@
|
||||
4.1
|
||||
5.0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
language: objective-c
|
||||
osx_image: xcode9.3
|
||||
osx_image: xcode10
|
||||
script:
|
||||
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-iOS -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=11'
|
||||
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-tvOS -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV,OS=11'
|
||||
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonViewExample -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=11'
|
||||
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-iOS -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=12'
|
||||
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-tvOS -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV,OS=12'
|
||||
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonViewExample -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=12'
|
||||
after_success:
|
||||
|
||||
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 222 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 388 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 267 KiB |
|
After Width: | Height: | Size: 29 KiB |
@@ -1,6 +1,73 @@
|
||||
# Change Log
|
||||
All notable changes to this project will be documented in this file
|
||||
|
||||
## Next version
|
||||
|
||||
### New
|
||||
- Adding swift news to mentioned section (thanks @osterbergmarcus)
|
||||
|
||||
## [Debug (1.4)](https://github.com/Juanpe/SkeletonView/releases/tag/1.4)
|
||||
|
||||
### New
|
||||
|
||||
- Create `skeletonDescription` print a skeleton representation of the view.
|
||||
- Create `SKELETON_DEBUG` environment variable, in order to print the view hierarchy when the skeleton appears.
|
||||
|
||||
### Improvements
|
||||
- Add two new methods to `SkeletonFlowDelegate` protocol. Now you can know when the skeleton did show and when it did hide.
|
||||
- `Recursive` protocol
|
||||
|
||||
### Bug fixes
|
||||
- Solved issue [#86](https://github.com/Juanpe/SkeletonView/issues/86) (thanks @reececomo)
|
||||
|
||||
## [Custom defaults (1.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.3)
|
||||
|
||||
### New
|
||||
|
||||
- Default values customizables. Now you can set the default values of Skeleton appearance.(thanks @reececomo)
|
||||
- issues: [[#50](https://github.com/Juanpe/SkeletonView/issues/50), [#83](https://github.com/Juanpe/SkeletonView/issues/83)]
|
||||
|
||||
### Bug fixes
|
||||
- Solved issue [#41](https://github.com/Juanpe/SkeletonView/issues/41). Now, Skeleton works if UICollectionView cell's Nib is registered in code. (thanks @kjoneandrei)
|
||||
|
||||
## [Typo (1.2.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.3)
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fix typo in `SkeletonTableViewDataSource` protocol
|
||||
|
||||
### Improvements
|
||||
|
||||
- Now it takes in account the `UIStackView` to calculate the `SkeletonLayer` bounds (thanks @giantramen)
|
||||
|
||||
## [New face (1.2.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.2)
|
||||
|
||||
### New
|
||||
|
||||
- Rebranding
|
||||
|
||||
### Bug fixes
|
||||
- Solved issue [#23](https://github.com/Juanpe/SkeletonView/issues/23). Problem with UIStackView. (thanks @giantramen)
|
||||
|
||||
## [State (1.2.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.1)
|
||||
|
||||
### New
|
||||
|
||||
- You can set the corner radius multiline elements (thanks @B4V4-G)
|
||||
- Save view state when skeleton appears and recovery when it is hidden (@juanpe)
|
||||
|
||||
### Bug fixes
|
||||
- Solved issue [#51](https://github.com/Juanpe/SkeletonView/issues/51). Support inspectable properties when using Carthage. (thanks @eduardbosch)
|
||||
|
||||
## [On TV (1.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2)
|
||||
|
||||
### New
|
||||
- Now ```SkeletonView``` is **tvOS** compatible! 🎉. (thanks @mihai8804858)
|
||||
|
||||
### Bug fixes
|
||||
- Solved issue [#46](https://github.com/Juanpe/SkeletonView/issues/46). It crashes the application when tap on it, didSelect called and crash.
|
||||
|
||||
|
||||
## [Hotfix (1.1.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.1.1)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.2</string>
|
||||
<string>1.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.2</string>
|
||||
<string>1.3</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
return true
|
||||
}
|
||||
|
||||
func applicationWillResignActive(_ application: UIApplication) {
|
||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
|
||||
}
|
||||
|
||||
func applicationDidEnterBackground(_ application: UIApplication) {
|
||||
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
||||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||
}
|
||||
|
||||
func applicationWillEnterForeground(_ application: UIApplication) {
|
||||
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
|
||||
}
|
||||
|
||||
func applicationDidBecomeActive(_ application: UIApplication) {
|
||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import SkeletonView
|
||||
|
||||
class CollectionViewCell: UICollectionViewCell {
|
||||
|
||||
var label: UILabel!
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
isSkeletonable = true
|
||||
createLabel()
|
||||
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func createLabel() {
|
||||
label = UILabel()
|
||||
label.isSkeletonable = true
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(label)
|
||||
NSLayoutConstraint.activate([
|
||||
label.centerXAnchor.constraint(equalTo: centerXAnchor),
|
||||
label.centerYAnchor.constraint(equalTo: centerYAnchor),
|
||||
label.heightAnchor.constraint(equalToConstant: frame.height / 2),
|
||||
label.widthAnchor.constraint(equalToConstant: frame.width / 2)
|
||||
])
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="irH-dz-xqL">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="qda-qV-vJk">
|
||||
<objects>
|
||||
<viewController id="irH-dz-xqL" customClass="ViewController" customModule="SkeletonViewExampleUICollectionView" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Fso-nq-n6t">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eHI-ka-8vS">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="243"/>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="obr-b6-dib">
|
||||
<rect key="frame" x="45" y="142" width="287" height="78"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="78" id="jx6-c1-U0j"/>
|
||||
</constraints>
|
||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="lastLineFillPercent">
|
||||
<integer key="value" value="40"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</textView>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="avatar" translatesAutoresizingMaskIntoConstraints="NO" id="Ql9-Jy-aWM">
|
||||
<rect key="frame" x="141" y="20" width="93" height="93"/>
|
||||
<color key="backgroundColor" red="0.56078431370000004" green="0.59607843140000005" blue="0.7843137255" alpha="0.90709546230000004" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="93" id="jlG-7K-wMd"/>
|
||||
<constraint firstAttribute="width" constant="93" id="xHX-Y1-dvi"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="243" id="0g6-3g-uII"/>
|
||||
<constraint firstAttribute="trailing" secondItem="obr-b6-dib" secondAttribute="trailing" constant="43" id="3ms-Wk-qcn"/>
|
||||
<constraint firstItem="obr-b6-dib" firstAttribute="centerX" secondItem="eHI-ka-8vS" secondAttribute="centerX" constant="1" id="B5s-DM-eR8"/>
|
||||
<constraint firstAttribute="height" constant="243" id="GX5-3W-tUt"/>
|
||||
<constraint firstItem="Ql9-Jy-aWM" firstAttribute="centerX" secondItem="eHI-ka-8vS" secondAttribute="centerX" id="HsA-ID-oSK"/>
|
||||
<constraint firstItem="Ql9-Jy-aWM" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="top" constant="20" id="Hxu-ae-hXQ"/>
|
||||
<constraint firstItem="obr-b6-dib" firstAttribute="leading" secondItem="eHI-ka-8vS" secondAttribute="leading" constant="45" id="eop-Gq-7mO"/>
|
||||
<constraint firstItem="obr-b6-dib" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="top" constant="142" id="inJ-75-hGX"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="36i-gK-pIa"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="HKL-L0-T2w">
|
||||
<rect key="frame" x="0.0" y="263" width="375" height="264"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="mGU-kn-rfE">
|
||||
<size key="itemSize" width="50" height="50"/>
|
||||
<size key="headerReferenceSize" width="0.0" height="0.0"/>
|
||||
<size key="footerReferenceSize" width="0.0" height="0.0"/>
|
||||
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
</collectionViewFlowLayout>
|
||||
<cells/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="irH-dz-xqL" id="wya-hE-ovQ"/>
|
||||
</connections>
|
||||
</collectionView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JjA-MK-YzZ">
|
||||
<rect key="frame" x="0.0" y="527" width="375" height="140"/>
|
||||
<subviews>
|
||||
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="fMR-vj-7de">
|
||||
<rect key="frame" x="20" y="23" width="140" height="29"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<segments>
|
||||
<segment title="Solid"/>
|
||||
<segment title="Gradient"/>
|
||||
</segments>
|
||||
<connections>
|
||||
<action selector="changeSkeletonType:" destination="irH-dz-xqL" eventType="valueChanged" id="lfR-JV-DU4"/>
|
||||
</connections>
|
||||
</segmentedControl>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KBe-RM-BG8">
|
||||
<rect key="frame" x="310" y="21" width="49" height="31"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<connections>
|
||||
<action selector="changeAnimated:" destination="irH-dz-xqL" eventType="valueChanged" id="dlH-KK-iee"/>
|
||||
</connections>
|
||||
</switch>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Animated" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GSj-Ze-UIK">
|
||||
<rect key="frame" x="211" y="28" width="91" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Color" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4Hm-fj-45V">
|
||||
<rect key="frame" x="32" y="89" width="52" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="HBJ-nh-56V">
|
||||
<rect key="frame" x="92" y="84" width="30" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aUR-Qo-gHK">
|
||||
<rect key="frame" x="20" y="74" width="140" height="52"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<connections>
|
||||
<action selector="btnChangeColorTouchUpInside:" destination="irH-dz-xqL" eventType="touchUpInside" id="Xca-QC-htl"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="140" id="QDV-wu-e3I"/>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="140" id="qR5-cz-YAm"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="eHI-ka-8vS" firstAttribute="trailing" secondItem="2Gq-Y8-1TU" secondAttribute="trailing" id="0dc-Vd-yJY"/>
|
||||
<constraint firstItem="JjA-MK-YzZ" firstAttribute="leading" secondItem="2Gq-Y8-1TU" secondAttribute="leading" id="57i-UV-Wqd"/>
|
||||
<constraint firstItem="eHI-ka-8vS" firstAttribute="leading" secondItem="2Gq-Y8-1TU" secondAttribute="leading" id="5tt-Ne-67Y"/>
|
||||
<constraint firstItem="JjA-MK-YzZ" firstAttribute="bottom" secondItem="2Gq-Y8-1TU" secondAttribute="bottom" id="AAr-ke-R7M"/>
|
||||
<constraint firstItem="JjA-MK-YzZ" firstAttribute="trailing" secondItem="2Gq-Y8-1TU" secondAttribute="trailing" id="DtS-9c-zBC"/>
|
||||
<constraint firstItem="HKL-L0-T2w" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="bottom" id="Jgf-jS-PLT"/>
|
||||
<constraint firstItem="eHI-ka-8vS" firstAttribute="top" secondItem="2Gq-Y8-1TU" secondAttribute="top" id="Ux2-GF-HLK"/>
|
||||
<constraint firstItem="JjA-MK-YzZ" firstAttribute="top" secondItem="HKL-L0-T2w" secondAttribute="bottom" id="XEd-Gf-KFI"/>
|
||||
<constraint firstItem="2Gq-Y8-1TU" firstAttribute="trailing" secondItem="HKL-L0-T2w" secondAttribute="trailing" id="bNo-98-pE4"/>
|
||||
<constraint firstItem="HKL-L0-T2w" firstAttribute="leading" secondItem="2Gq-Y8-1TU" secondAttribute="leading" id="iIq-cx-paX"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="2Gq-Y8-1TU"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="avatarImage" destination="Ql9-Jy-aWM" id="VoL-by-ygR"/>
|
||||
<outlet property="collectionView" destination="HKL-L0-T2w" id="HSe-j0-S5d"/>
|
||||
<outlet property="colorSelectedView" destination="HBJ-nh-56V" id="Iiq-iY-Glj"/>
|
||||
<outlet property="skeletonTypeSelector" destination="fMR-vj-7de" id="CgX-3A-weo"/>
|
||||
<outlet property="switchAnimated" destination="KBe-RM-BG8" id="emU-g9-NHT"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="PkM-Y0-M5i" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-972" y="-209.14542728635683"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="avatar" width="215" height="211"/>
|
||||
</resources>
|
||||
</document>
|
||||
@@ -0,0 +1,173 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import SkeletonView
|
||||
|
||||
class ViewController: UIViewController {
|
||||
|
||||
@IBOutlet weak var collectionView: UICollectionView! {
|
||||
didSet {
|
||||
collectionView.isSkeletonable = true
|
||||
collectionView.backgroundColor = .clear
|
||||
collectionView.showsHorizontalScrollIndicator = false
|
||||
collectionView.showsVerticalScrollIndicator = false
|
||||
|
||||
collectionView.dataSource = self
|
||||
collectionView.delegate = self
|
||||
|
||||
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
|
||||
}
|
||||
}
|
||||
|
||||
@IBOutlet weak var avatarImage: UIImageView! {
|
||||
didSet {
|
||||
avatarImage.layer.cornerRadius = avatarImage.frame.width/2
|
||||
avatarImage.layer.masksToBounds = true
|
||||
}
|
||||
}
|
||||
|
||||
@IBOutlet weak var colorSelectedView: UIView! {
|
||||
didSet {
|
||||
colorSelectedView.layer.cornerRadius = 5
|
||||
colorSelectedView.layer.masksToBounds = true
|
||||
colorSelectedView.backgroundColor = SkeletonAppearance.default.tintColor
|
||||
}
|
||||
}
|
||||
|
||||
@IBOutlet weak var switchAnimated: UISwitch!
|
||||
@IBOutlet weak var skeletonTypeSelector: UISegmentedControl!
|
||||
|
||||
var type: SkeletonType {
|
||||
return skeletonTypeSelector.selectedSegmentIndex == 0 ? .solid : .gradient
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.isSkeletonable = true
|
||||
collectionView.prepareSkeleton(completion: { done in
|
||||
self.view.showAnimatedSkeleton()
|
||||
})
|
||||
}
|
||||
|
||||
@IBAction func changeAnimated(_ sender: Any) {
|
||||
if switchAnimated.isOn {
|
||||
view.startSkeletonAnimation()
|
||||
} else {
|
||||
view.stopSkeletonAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func changeSkeletonType(_ sender: Any) {
|
||||
refreshSkeleton()
|
||||
}
|
||||
|
||||
@IBAction func btnChangeColorTouchUpInside(_ sender: Any) {
|
||||
showAlertPicker()
|
||||
}
|
||||
|
||||
func refreshSkeleton() {
|
||||
self.view.hideSkeleton()
|
||||
if type == .gradient { showGradientSkeleton() }
|
||||
else { showSolidSkeleton() }
|
||||
}
|
||||
|
||||
func showSolidSkeleton() {
|
||||
if switchAnimated.isOn {
|
||||
view.showAnimatedSkeleton(usingColor: colorSelectedView.backgroundColor!)
|
||||
} else {
|
||||
view.showSkeleton(usingColor: colorSelectedView.backgroundColor!)
|
||||
}
|
||||
}
|
||||
|
||||
func showGradientSkeleton() {
|
||||
let gradient = SkeletonGradient(baseColor: colorSelectedView.backgroundColor!)
|
||||
if switchAnimated.isOn {
|
||||
view.showAnimatedGradientSkeleton(usingGradient: gradient)
|
||||
} else {
|
||||
view.showGradientSkeleton(usingGradient: gradient)
|
||||
}
|
||||
}
|
||||
|
||||
func showAlertPicker() {
|
||||
|
||||
let alertView = UIAlertController(title: "Select color", message: "\n\n\n\n\n\n", preferredStyle: .alert)
|
||||
|
||||
let pickerView = UIPickerView(frame: CGRect(x: 0, y: 50, width: 260, height: 115))
|
||||
pickerView.dataSource = self
|
||||
pickerView.delegate = self
|
||||
|
||||
alertView.view.addSubview(pickerView)
|
||||
|
||||
let action = UIAlertAction(title: "OK", style: .default) { [unowned pickerView, unowned self] _ in
|
||||
let row = pickerView.selectedRow(inComponent: 0)
|
||||
self.colorSelectedView.backgroundColor = colors[row].0
|
||||
self.refreshSkeleton()
|
||||
}
|
||||
alertView.addAction(action)
|
||||
|
||||
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
|
||||
alertView.addAction(cancelAction)
|
||||
|
||||
present(alertView, animated: false, completion: {
|
||||
pickerView.frame.size.width = alertView.view.frame.size.width
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIPickerViewDelegate, UIPickerViewDataSource
|
||||
|
||||
extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource {
|
||||
|
||||
func numberOfComponents(in pickerView: UIPickerView) -> Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
|
||||
return colors.count
|
||||
}
|
||||
|
||||
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
|
||||
return colors[row].1
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UICollectionViewDelegateFlowLayout
|
||||
|
||||
extension ViewController: UICollectionViewDelegateFlowLayout {
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
|
||||
return CGSize(width: view.frame.width/3 - 10, height: view.frame.width/3 - 10)
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
|
||||
return 5
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
|
||||
return 5
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SkeletonCollectionViewDataSource
|
||||
|
||||
extension ViewController: SkeletonCollectionViewDataSource {
|
||||
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier {
|
||||
return "CollectionViewCell"
|
||||
}
|
||||
|
||||
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return 10
|
||||
}
|
||||
|
||||
// MARK: - UICollectionViewDataSource
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
|
||||
return cell
|
||||
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
var window: UIWindow?
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "83.5x83.5",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"size" : "1024x1024",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 933 B After Width: | Height: | Size: 933 B |
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
@@ -31,7 +31,7 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="120"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="oiE-tt-nc2">
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="avatar" translatesAutoresizingMaskIntoConstraints="NO" id="oiE-tt-nc2">
|
||||
<rect key="frame" x="15" y="18" width="82" height="82"/>
|
||||
<color key="backgroundColor" red="0.56078431370000004" green="0.59607843140000005" blue="0.7843137255" alpha="0.90709546230000004" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
@@ -52,6 +52,9 @@
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="linesCornerRadius">
|
||||
<integer key="value" value="0"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
@@ -76,7 +79,7 @@
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="NO"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="BYZ-38-t0r" id="Hxi-nC-gbY"/>
|
||||
@@ -99,6 +102,9 @@
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="lastLineFillPercent">
|
||||
<integer key="value" value="40"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="linesCornerRadius">
|
||||
<integer key="value" value="6"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</textView>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="avatar" translatesAutoresizingMaskIntoConstraints="NO" id="nMj-pU-5wJ">
|
||||
@@ -195,6 +201,9 @@
|
||||
<constraint firstItem="XgY-1a-UGc" firstAttribute="bottom" secondItem="6Tk-OE-BBY" secondAttribute="bottom" id="vnZ-9k-MfI"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="NO"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" id="BEI-dU-kr2"/>
|
||||
<connections>
|
||||
@@ -207,7 +216,7 @@
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-897" y="-376"/>
|
||||
<point key="canvasLocation" x="-2582" y="-400"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
@@ -1,10 +1,4 @@
|
||||
//
|
||||
// Constants.swift
|
||||
// SkeletonView-iOS
|
||||
//
|
||||
// Created by Renato Mendes on 28/11/2017.
|
||||
// Copyright © 2017 SkeletonView. All rights reserved.
|
||||
//
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
@@ -13,7 +13,7 @@ class ViewController: UIViewController {
|
||||
|
||||
@IBOutlet weak var tableview: UITableView! {
|
||||
didSet {
|
||||
tableview.rowHeight = UITableViewAutomaticDimension
|
||||
tableview.rowHeight = UITableView.automaticDimension
|
||||
tableview.estimatedRowHeight = 120.0
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ class ViewController: UIViewController {
|
||||
didSet {
|
||||
colorSelectedView.layer.cornerRadius = 5
|
||||
colorSelectedView.layer.masksToBounds = true
|
||||
colorSelectedView.backgroundColor = SkeletonDefaultConfig.tintColor
|
||||
colorSelectedView.backgroundColor = SkeletonAppearance.default.tintColor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ extension ViewController: SkeletonTableViewDataSource {
|
||||
return 9
|
||||
}
|
||||
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
|
||||
return "CellIdentifier"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.0)
|
||||
activesupport (4.2.10)
|
||||
i18n (~> 0.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.5.2)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
atomos (0.1.3)
|
||||
babosa (1.0.2)
|
||||
claide (1.0.2)
|
||||
cocoapods (1.5.3)
|
||||
activesupport (>= 4.0.2, < 5)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
cocoapods-core (= 1.5.3)
|
||||
cocoapods-deintegrate (>= 1.0.2, < 2.0)
|
||||
cocoapods-downloader (>= 1.2.0, < 2.0)
|
||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||
cocoapods-search (>= 1.0.0, < 2.0)
|
||||
cocoapods-stats (>= 1.0.0, < 2.0)
|
||||
cocoapods-trunk (>= 1.3.0, < 2.0)
|
||||
cocoapods-try (>= 1.1.0, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
escape (~> 0.0.4)
|
||||
fourflusher (~> 2.0.1)
|
||||
gh_inspector (~> 1.0)
|
||||
molinillo (~> 0.6.5)
|
||||
nap (~> 1.0)
|
||||
ruby-macho (~> 1.1)
|
||||
xcodeproj (>= 1.5.7, < 2.0)
|
||||
cocoapods-core (1.5.3)
|
||||
activesupport (>= 4.0.2, < 6)
|
||||
fuzzy_match (~> 2.0.4)
|
||||
nap (~> 1.0)
|
||||
cocoapods-deintegrate (1.0.2)
|
||||
cocoapods-downloader (1.2.2)
|
||||
cocoapods-plugins (1.0.0)
|
||||
nap
|
||||
cocoapods-search (1.0.0)
|
||||
cocoapods-stats (1.0.0)
|
||||
cocoapods-trunk (1.3.1)
|
||||
nap (>= 0.8, < 2.0)
|
||||
netrc (~> 0.11)
|
||||
cocoapods-try (1.1.0)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander-fastlane (4.4.6)
|
||||
highline (~> 1.7.2)
|
||||
concurrent-ruby (1.0.5)
|
||||
declarative (0.0.10)
|
||||
declarative-option (0.1.0)
|
||||
digest-crc (0.4.1)
|
||||
domain_name (0.5.20180417)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.5.0)
|
||||
emoji_regex (0.1.1)
|
||||
escape (0.0.4)
|
||||
excon (0.62.0)
|
||||
faraday (0.15.4)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-cookie_jar (0.0.6)
|
||||
faraday (>= 0.7.4)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday_middleware (0.12.2)
|
||||
faraday (>= 0.7.4, < 1.0)
|
||||
fastimage (2.1.5)
|
||||
fastlane (2.110.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
babosa (>= 1.0.2, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 2.0.0)
|
||||
colored
|
||||
commander-fastlane (>= 4.4.6, < 5.0.0)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (~> 0.1)
|
||||
excon (>= 0.45.0, < 1.0.0)
|
||||
faraday (~> 0.9)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 0.9)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-api-client (>= 0.21.2, < 0.24.0)
|
||||
google-cloud-storage (>= 1.15.0, < 2.0.0)
|
||||
highline (>= 1.7.2, < 2.0.0)
|
||||
json (< 3.0.0)
|
||||
mini_magick (~> 4.5.1)
|
||||
multi_json
|
||||
multi_xml (~> 0.5)
|
||||
multipart-post (~> 2.0.0)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
public_suffix (~> 2.0.0)
|
||||
rubyzip (>= 1.2.2, < 2.0.0)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
slack-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-notifier (>= 1.6.2, < 2.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.6.0, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
fourflusher (2.0.1)
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
google-api-client (0.23.9)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.5, < 0.7.0)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
mime-types (~> 3.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
signet (~> 0.9)
|
||||
google-cloud-core (1.2.7)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-env (1.0.5)
|
||||
faraday (~> 0.11)
|
||||
google-cloud-storage (1.15.0)
|
||||
digest-crc (~> 0.4)
|
||||
google-api-client (~> 0.23)
|
||||
google-cloud-core (~> 1.2)
|
||||
googleauth (~> 0.6.2)
|
||||
googleauth (0.6.7)
|
||||
faraday (~> 0.12)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (~> 0.7)
|
||||
highline (1.7.10)
|
||||
http-cookie (1.0.3)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
json (2.1.0)
|
||||
jwt (2.1.0)
|
||||
memoist (0.16.0)
|
||||
mime-types (3.2.2)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2018.0812)
|
||||
mini_magick (4.5.1)
|
||||
minitest (5.11.3)
|
||||
molinillo (0.6.6)
|
||||
multi_json (1.13.1)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.2.6)
|
||||
nap (1.1.0)
|
||||
naturally (2.2.0)
|
||||
netrc (0.11.0)
|
||||
os (1.0.0)
|
||||
plist (3.4.0)
|
||||
public_suffix (2.0.5)
|
||||
representable (3.0.4)
|
||||
declarative (< 0.1.0)
|
||||
declarative-option (< 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rouge (2.0.7)
|
||||
ruby-macho (1.3.1)
|
||||
rubyzip (1.2.2)
|
||||
security (0.1.3)
|
||||
signet (0.11.0)
|
||||
addressable (~> 2.3)
|
||||
faraday (~> 0.9)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.5)
|
||||
CFPropertyList
|
||||
naturally
|
||||
slack-notifier (2.3.2)
|
||||
terminal-notifier (1.8.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
thread_safe (0.3.6)
|
||||
tty-cursor (0.6.0)
|
||||
tty-screen (0.6.5)
|
||||
tty-spinner (0.9.0)
|
||||
tty-cursor (~> 0.6.0)
|
||||
tzinfo (1.2.5)
|
||||
thread_safe (~> 0.1)
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.5)
|
||||
unicode-display_width (1.4.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.7.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.2.6)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.0)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
cocoapods
|
||||
fastlane
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.6
|
||||
@@ -1,30 +1,36 @@
|
||||

|
||||

|
||||
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/Juanpe/SkeletonView">
|
||||
<img src="https://img.shields.io/travis/Juanpe/SkeletonView.svg">
|
||||
<a href="https://app.bitrise.io/app/6d289a17e22c8323">
|
||||
<img src="https://app.bitrise.io/app/6d289a17e22c8323/status.svg?token=fI7gKC41XD9-aRXDScCKBw&branch=master">
|
||||
</a>
|
||||
<img src="https://img.shields.io/badge/Swift-4.1-orange.svg" />
|
||||
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
|
||||
<a href="https://github.com/Juanpe/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/p/SkeletonView.svg" alt="Platforms">
|
||||
</a>
|
||||
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
|
||||
<a href="https://cocoapods.org/pods/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
|
||||
</a>
|
||||
<a href="https://github.com/Carthage/Carthage">
|
||||
<img src="https://img.shields.io/badge/carthage-compatible-4BC51D.svg?style=flat" alt="Carthage" />
|
||||
</a>
|
||||
<a href="https://cocoapods.org/pods/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/dt/SkeletonView.svg?style=flat" alt="CocoaPods downloads" />
|
||||
</a>
|
||||
<a href="https://twitter.com/JuanpeCatalan">
|
||||
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
|
||||
</a>
|
||||
<a href="https://opensource.org/licenses/MIT">
|
||||
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License" />
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView¤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>
|
||||
<br/>
|
||||
<a href="https://twitter.com/intent/tweet?text=Wow%20This%20library%20is%20awesome:&url=https%3A%2F%2Fgithub.com%2FJuanpe%2FSkeletonView">
|
||||
<img src="https://img.shields.io/twitter/url/https/github.com/Juanpe/SkeletonView.svg?style=social" alt="License" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
🌎 Translations: </br>
|
||||
[🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) by [@WhatsXie](https://twitter.com/WhatsXie) </br>
|
||||
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) by [@brunomunizaf](https://twitter.com/brunomuniz_af)
|
||||
|
||||
Today almost all apps have async processes, such as Api requests, long running processes, etc. And while the processes are working, usually developers place a loading view to show users that something is going on.
|
||||
|
||||
```SkeletonView``` has been conceived to address this need, an elegant way to show users that something is happening and also prepare them to which contents he is waiting.
|
||||
@@ -32,8 +38,7 @@ Today almost all apps have async processes, such as Api requests, long running p
|
||||
Enjoy it! 🙂
|
||||
|
||||
* [Features](#-features)
|
||||
* [Requirements](#-supported-os--sdk-versions)
|
||||
* [Example Project](#-example)
|
||||
* [Guides](#-guides)
|
||||
* [Installation](#-installation)
|
||||
* [Cocoapods](#using-cocoapods)
|
||||
* [Carthage](#using-carthage)
|
||||
@@ -41,11 +46,15 @@ Enjoy it! 🙂
|
||||
* [Collections](#-collections)
|
||||
* [Multiline text](#-multiline-text)
|
||||
* [Custom colors](#-custom-colors)
|
||||
* [Appearance](#-appearance)
|
||||
* [Custom animations](#-custom-animations)
|
||||
* [Hierarchy](#-hierarchy)
|
||||
* [Debug](#-debug)
|
||||
* [Documentation](#-documentation)
|
||||
* [Supported OS & SDK Versions](#-supported-os--sdk-versions)
|
||||
* [Next steps](#-next-steps)
|
||||
* [Contributed](#-contributed)
|
||||
* [Contributing](#-contributing)
|
||||
* [Mentions](#-mentions)
|
||||
* [Author](#-author)
|
||||
* [License](#-license)
|
||||
|
||||
@@ -60,17 +69,9 @@ Enjoy it! 🙂
|
||||
- [x] Simple Swift syntax
|
||||
- [x] Lightweight readable codebase
|
||||
|
||||
### 📋 Supported OS & SDK Versions
|
||||
## 🎬 Guides
|
||||
|
||||
* iOS 9.0+
|
||||
* tvOS 9.0+
|
||||
* Swift 4
|
||||
|
||||
### 🔮 Example
|
||||
|
||||
To run the example project, clone the repo and run `SkeletonViewExample` target.
|
||||
|
||||

|
||||
[<img src="Assets/thumb_getting_started.png">](https://youtu.be/75kgOhWsPNA)
|
||||
|
||||
## 📲 Installation
|
||||
|
||||
@@ -158,7 +159,7 @@ avatarImageView.isSkeletonable = true
|
||||
|
||||
Now, ```SkeletonView``` is compatible with ```UITableView``` and ```UICollectionView```.
|
||||
|
||||
###### UITableView
|
||||
#### UITableView
|
||||
|
||||
If you want to show the skeleton in a ```UITableView```, you need to conform to ```SkeletonTableViewDataSource``` protocol.
|
||||
|
||||
@@ -166,7 +167,7 @@ If you want to show the skeleton in a ```UITableView```, you need to conform to
|
||||
public protocol SkeletonTableViewDataSource: UITableViewDataSource {
|
||||
func numSections(in collectionSkeletonView: UITableView) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
}
|
||||
```
|
||||
As you can see, this protocol inherits from ```UITableViewDataSource```, so you can replace this protocol with the skeleton protocol.
|
||||
@@ -186,12 +187,12 @@ func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection s
|
||||
|
||||
There is only one method you need to implement to let Skeleton know the cell identifier. This method doesn't have default implementation:
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
```
|
||||
|
||||
**Example**
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
|
||||
return "CellIdentifier"
|
||||
}
|
||||
```
|
||||
@@ -199,7 +200,15 @@ There is only one method you need to implement to let Skeleton know the cell ide
|
||||
> **IMPORTANT!**
|
||||
> If you are using resizable cells (`tableView.rowHeight = UITableViewAutomaticDimension` ), it's mandatory define the `estimatedRowHeight`.
|
||||
|
||||
###### UICollectionView
|
||||
👩🏼🏫 **How specify which elements are skeletonables?**
|
||||
|
||||
Here is an illustration that shows how you should specify which elements are skeletonables when you are using an `UITableView`:
|
||||
|
||||

|
||||
|
||||
As you can see, we have to make skeletonable the tableview, the cell and the UI elements, but we don't need to set as skeletonable the `contentView`
|
||||
|
||||
#### UICollectionView
|
||||
|
||||
For ```UICollectionView```, you need to conform to ```SkeletonCollectionViewDataSource``` protocol.
|
||||
|
||||
@@ -221,17 +230,27 @@ The rest of the process is the same as ```UITableView```
|
||||
When using elements with text, ```SkeletonView``` draws lines to simulate text.
|
||||
Besides, you can decide how many lines you want. If ```numberOfLines``` is set to zero, it will calculate how many lines needed to populate the whole skeleton and it will be drawn. Instead, if you set it to one, two or any number greater than zero, it will only draw this number of lines.
|
||||
|
||||
**NEW** Now, you can set the filling percent of the last line. **Default: 70%**
|
||||
##### 🎛 Customize
|
||||
|
||||
To modify the percent **using code**, set the property:
|
||||
You can set some properties for multilines elements.
|
||||
|
||||
|
||||
| Property | Values | Default | Preview
|
||||
| ------- | ------- |------- | -------
|
||||
| **Filling percent** of the last line. | `0...100` | `70%` | 
|
||||
| **Corner radius** of lines. (**NEW**) | `0...10` | `0` | 
|
||||
|
||||
|
||||
|
||||
To modify the percent or radius **using code**, set the properties:
|
||||
```swift
|
||||
descriptionTextView.lastLineFillPercent = 50
|
||||
descriptionTextView.linesCornerRadius = 5
|
||||
```
|
||||
|
||||
Or, if you prefer use **IB/Storyboard**:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### 🎨 Custom colors
|
||||
|
||||
@@ -256,9 +275,34 @@ Besides, ```SkeletonView``` features 20 flat colors 🤙🏼
|
||||

|
||||
###### Image captured from website [https://flatuicolors.com](https://flatuicolors.com)
|
||||
|
||||
### 🦋 Appearance
|
||||
|
||||
**NEW** The skeletons have a default appearance. So, when you don't specify the color, gradient or multilines properties, `SkeletonView` uses the default values.
|
||||
|
||||
Default values:
|
||||
- **tintColor**: UIColor
|
||||
- *default: .clouds*
|
||||
- **gradient**: SkeletonGradient
|
||||
- *default: SkeletonGradient(baseColor: .clouds)*
|
||||
- **multilineHeight**: CGFloat
|
||||
- *default: 15*
|
||||
- **multilineSpacing**: CGFloat
|
||||
- *default: 10*
|
||||
- **multilineLastLineFillPercent**: Int
|
||||
- *default: 70*
|
||||
- **multilineCornerRadius**: Int
|
||||
- *default: 0*
|
||||
|
||||
To get these default values you can use `SkeletonAppearance.default`. Using this property you can set the values as well:
|
||||
```Swift
|
||||
SkeletonAppearance.default.multilineHeight = 20
|
||||
SkeletonAppearance.default.tintColor = .green
|
||||
```
|
||||
|
||||
|
||||
### 🤓 Custom animations
|
||||
|
||||
Now, ```SkeletonView``` has two built-in animations, *pulse* for solid skeletons and *sliding* for gradients.
|
||||
```SkeletonView``` has two built-in animations, *pulse* for solid skeletons and *sliding* for gradients.
|
||||
|
||||
Besides, if you want to do your own skeleton animation, it's really easy.
|
||||
|
||||
@@ -280,7 +324,7 @@ view.showAnimatedSkeleton { (layer) -> CAAnimation in
|
||||
}
|
||||
```
|
||||
|
||||
**NEW** It's available ```SkeletonAnimationBuilder```. It's a builder to make ```SkeletonLayerAnimation```.
|
||||
It's available ```SkeletonAnimationBuilder```. It's a builder to make ```SkeletonLayerAnimation```.
|
||||
|
||||
Today, you can create **sliding animations** for gradients, deciding the **direction** and setting the **duration** of the animation (default = 1.5s).
|
||||
|
||||
@@ -298,7 +342,7 @@ view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
|
||||
|------- | -------
|
||||
| .leftRight | 
|
||||
| .rightLeft | 
|
||||
| .topBottom | 
|
||||
| .topBottom | 
|
||||
| .bottomTop | 
|
||||
| .topLeftBottomRight | 
|
||||
| .bottomRightTopLeft | 
|
||||
@@ -323,10 +367,41 @@ Because an image is worth a thousand words:
|
||||
| | 
|
||||
|
||||
|
||||
### 🔬 Debug
|
||||
|
||||
**NEW** In order to facilitate the debug tasks when something is not working fine. `SkeletonView` has some new tools.
|
||||
|
||||
First, `UIView` has available a new property with his skeleton info:
|
||||
```swift
|
||||
var skeletonDescription: String
|
||||
|
||||
```
|
||||
The skeleton representation looks like this:
|
||||
|
||||

|
||||
|
||||
Besides, you can activate the new **debug mode**. You just add the environment variable `SKELETON_DEBUG` and activate it.
|
||||
|
||||

|
||||
|
||||
Then, when the skeleton appears, you can see the view hierarchy in the Xcode console.
|
||||
|
||||
<details>
|
||||
<summary>Open to see an output example </summary>
|
||||
<img src="Assets/hierarchy_output.png" />
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
### 📚 Documentation
|
||||
Coming soon...😅
|
||||
|
||||
### 📋 Supported OS & SDK Versions
|
||||
|
||||
* iOS 9.0+
|
||||
* tvOS 9.0+
|
||||
* Swift 4.2
|
||||
|
||||
## 📬 Next steps
|
||||
|
||||
* [x] Set the filling percent of the last line in multiline elements
|
||||
@@ -334,12 +409,14 @@ Coming soon...😅
|
||||
* [x] Supported resizable cells
|
||||
* [x] CollectionView compatible
|
||||
* [x] tvOS compatible
|
||||
* [ ] Add recovery state
|
||||
* [x] Add recovery state
|
||||
* [x] Custom default appearance
|
||||
* [x] Debug mode
|
||||
* [ ] Custom collections compatible
|
||||
* [ ] Add animations when it shows/hides the skeletons
|
||||
* [ ] MacOS and WatchOS compatible
|
||||
|
||||
## ❤️ Contributed
|
||||
## ❤️ Contributing
|
||||
This is an open source project, so feel free to contribute. How?
|
||||
- Open an [issue](https://github.com/Juanpe/SkeletonView/issues/new).
|
||||
- Send feedback via [email](mailto://juanpecatalan.com).
|
||||
@@ -349,12 +426,31 @@ See [all contributors](https://github.com/Juanpe/SkeletonView/graphs/contributor
|
||||
|
||||
###### Project generated with [SwiftPlate](https://github.com/JohnSundell/SwiftPlate)
|
||||
|
||||
## 📢 Mentions
|
||||
|
||||
- [iOS Dev Weekly #327](https://iosdevweekly.com/issues/327#start)
|
||||
- [Hacking with Swift Articles](https://www.hackingwithswift.com/articles/40/skeletonview-makes-loading-content-beautiful)
|
||||
- [Top 10 Swift Articles November](https://medium.mybridge.co/swift-top-10-articles-for-the-past-month-v-nov-2017-dfed7861cd65)
|
||||
- [30 Amazing iOS Swift Libraries (v2018)](https://medium.mybridge.co/30-amazing-ios-swift-libraries-for-the-past-year-v-2018-7cf15027eee9)
|
||||
- [AppCoda Weekly #44](http://digest.appcoda.com/issues/appcoda-weekly-issue-44-81899)
|
||||
- [iOS Cookies Newsletter #103](https://us11.campaign-archive.com/?u=cd1f3ed33c6527331d82107ba&id=48131a516d)
|
||||
- [Swift Developments Newsletter #113](https://andybargh.com/swiftdevelopments-113/)
|
||||
- [iOS Goodies #204](http://ios-goodies.com/post/167557280951/week-204)
|
||||
- [Swift Weekly #96](http://digest.swiftweekly.com/issues/swift-weekly-issue-96-81759)
|
||||
- [CocoaControls](https://www.cocoacontrols.com/controls/skeletonview)
|
||||
- [Awesome iOS Newsletter #74](https://ios.libhunt.com/newsletter/74)
|
||||
- [Swift News #36](https://www.youtube.com/watch?v=mAGpsQiy6so)
|
||||
|
||||
|
||||
|
||||
## 👨🏻💻 Author
|
||||
[1.1]: http://i.imgur.com/tXSoThF.png
|
||||
[1]: http://www.twitter.com/JuanpeCatalan
|
||||
|
||||
* Juanpe Catalán [![alt text][1.1]][1]
|
||||
|
||||
<a class="bmc-button" target="_blank" href="https://www.buymeacoffee.com/CDou4xtIK"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy me a coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;"><span style="margin-left:5px"></span></a>
|
||||
|
||||
## 👮🏻 License
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,442 @@
|
||||

|
||||
|
||||
<p align="center">
|
||||
<a href="https://app.bitrise.io/app/6d289a17e22c8323">
|
||||
<img src="https://app.bitrise.io/app/6d289a17e22c8323/status.svg?token=fI7gKC41XD9-aRXDScCKBw&branch=master">
|
||||
</a>
|
||||
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
|
||||
<a href="https://github.com/Juanpe/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/p/SkeletonView.svg" alt="Platforms">
|
||||
</a>
|
||||
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
|
||||
<a href="https://cocoapods.org/pods/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
|
||||
</a>
|
||||
<a href="https://cocoapods.org/pods/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/dt/SkeletonView.svg?style=flat" alt="CocoaPods downloads" />
|
||||
</a>
|
||||
<a href="https://twitter.com/JuanpeCatalan">
|
||||
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
|
||||
</a>
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView¤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>
|
||||
<br/>
|
||||
<a href="https://twitter.com/intent/tweet?text=Wow%20This%20library%20is%20awesome:&url=https%3A%2F%2Fgithub.com%2FJuanpe%2FSkeletonView">
|
||||
<img src="https://img.shields.io/twitter/url/https/github.com/Juanpe/SkeletonView.svg?style=social" alt="License" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
🌎 Traduções: </br>
|
||||
[Original](https://github.com/Juanpe/SkeletonView) </br>
|
||||
[🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) por [@WhatsXie](https://twitter.com/WhatsXie) </br>
|
||||
|
||||
Hoje, quase todos os apps têm processos assíncronos, como requisições de API, processos longos, etc. E enquanto os processos estão ocorrendo, normalmente os desenvolvedores usam uma view que mostra os usuarios que algo está ocorrendo.
|
||||
|
||||
```SkeletonView``` foi criado para essa necessidade, um jeito elegante de mostrar aos usuários que algo está acontecendo e já prepará-los para qual conteúdo será carregado.
|
||||
|
||||
Aproveite! 🙂
|
||||
|
||||
* [Features](#-features)
|
||||
* [Requerimentos](#-supported-os--sdk-versions)
|
||||
* [Projeto de exemplo](#-example)
|
||||
* [Instalação](#-installation)
|
||||
* [Cocoapods](#using-cocoapods)
|
||||
* [Carthage](#using-carthage)
|
||||
* [Como usar](#-how-to-use)
|
||||
* [Coleções](#-collections)
|
||||
* [Texto em várias linhas](#-multiline-text)
|
||||
* [Cores customizadas](#-custom-colors)
|
||||
* [Aparência](#-appearance)
|
||||
* [Animaçōes customizadas](#-custom-animations)
|
||||
* [Hierarquia](#-hierarchy)
|
||||
* [Documentação](#-documentation)
|
||||
* [Próximos passos](#-next-steps)
|
||||
* [Contribuindo](#-contributing)
|
||||
* [Menções](#-mentions)
|
||||
* [Autor](#-author)
|
||||
* [Licença](#-license)
|
||||
|
||||
|
||||
## 🌟 Features
|
||||
|
||||
- [x] Fácil de usar
|
||||
- [x] Todas as UIViews são skeletonables
|
||||
- [x] Completamente customizável
|
||||
- [x] Universal (iPhone & iPad)
|
||||
- [x] Interface Builder friendly
|
||||
- [x] Sintaxe simples em Swift
|
||||
- [x] Código leve e legível
|
||||
|
||||
### 📋 Versões do SDK e OS suportados
|
||||
|
||||
* iOS 9.0+
|
||||
* tvOS 9.0+
|
||||
* Swift 4.2
|
||||
|
||||
### 🔮 Exemplo
|
||||
|
||||
Para rodar o projeto de exemplo, clone o repositório e use o target `SkeletonViewExample`.
|
||||
|
||||
## 📲 Instalação
|
||||
|
||||
#### Usando [CocoaPods](https://cocoapods.org)
|
||||
|
||||
Edite seu `Podfile` e especifíque a dependência:
|
||||
|
||||
```ruby
|
||||
pod "SkeletonView"
|
||||
```
|
||||
|
||||
#### Usando [Carthage](https://github.com/carthage)
|
||||
|
||||
Edite seu `Cartfile` e especifíque a dependência:
|
||||
|
||||
```bash
|
||||
github "Juanpe/SkeletonView"
|
||||
```
|
||||
|
||||
## 🐒 Como usar
|
||||
|
||||
Apenas **3** passos necessários para usar `SkeletonView`:
|
||||
|
||||
**1.** Importe SkeletonView no lugar desejado.
|
||||
```swift
|
||||
import SkeletonView
|
||||
```
|
||||
|
||||
**2.** Agora, especifíque quais views serão `skeletonables`. Você consegue fazer isso de duas formas:
|
||||
|
||||
**Usando código:**
|
||||
```swift
|
||||
avatarImageView.isSkeletonable = true
|
||||
```
|
||||
**Usando IB/Storyboards:**
|
||||
|
||||

|
||||
|
||||
**3.** Uma vez que você setou as views, você pode mostrar o **skeleton**. Para fazê-lo, você tem **4** escolhas:
|
||||
|
||||
```swift
|
||||
(1) view.showSkeleton() // Solid
|
||||
(2) view.showGradientSkeleton() // Gradient
|
||||
(3) view.showAnimatedSkeleton() // Solid animated
|
||||
(4) view.showAnimatedGradientSkeleton() // Gradient animated
|
||||
```
|
||||
|
||||
**Pré-visualização**
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="25%">
|
||||
<center>Solid</center>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<center>Gradient</center>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<center>Solid Animated</center>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<center>Gradient Animated</center>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="25%">
|
||||
<img src="Assets/solid.png"></img>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<img src="Assets/gradient.png"></img>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<img src="Assets/solid_animated.gif"></img>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<img src="Assets/gradient_animated.gif"></img>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
> **IMPORTANTE!**
|
||||
>>```SkeletonView``` é recursivo, então se você quer mostrar o esqueleto em todas as views skeletonables, você só precisa chamar o método na main container view. Por exemplo, com UIViewControllers
|
||||
|
||||
### 🌿 Coleções
|
||||
|
||||
```SkeletonView``` é compatível com ```UITableView``` e ```UICollectionView```.
|
||||
|
||||
###### UITableView
|
||||
|
||||
Se você quer mostrar o skeleton em uma ```UITableView```, você precisa conformar com o protocolo ```SkeletonTableViewDataSource```.
|
||||
|
||||
``` swift
|
||||
public protocol SkeletonTableViewDataSource: UITableViewDataSource {
|
||||
func numSections(in collectionSkeletonView: UITableView) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
}
|
||||
```
|
||||
Como você pode ver, esse protocolo herda de ```UITableViewDataSource```, então você pode substituir esse protocolo com o protocolo do skeleton.
|
||||
|
||||
Esse protocolo tem uma implementação padrão:
|
||||
|
||||
``` swift
|
||||
func numSections(in collectionSkeletonView: UITableView) -> Int
|
||||
// Default: 1
|
||||
```
|
||||
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
|
||||
// Default:
|
||||
// It calculates how many cells need to populate whole tableview
|
||||
```
|
||||
|
||||
Esse é o único método que você precisa implementar para informar o skeleton sobre o cell identifier. Esse método não possui uma implementação padrão:
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
```
|
||||
|
||||
**Exemplo**
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
|
||||
return "CellIdentifier"
|
||||
}
|
||||
```
|
||||
|
||||
> **IMPORTANTE!**
|
||||
> Se você está usando resizable cells (`tableView.rowHeight = UITableViewAutomaticDimension` ), é obrigatório definir a `estimatedRowHeight`.
|
||||
|
||||
###### UICollectionView
|
||||
|
||||
Para ```UICollectionView```, você precisa conformar com o protocolo ```SkeletonCollectionViewDataSource```.
|
||||
|
||||
``` swift
|
||||
public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
|
||||
func numSections(in collectionSkeletonView: UICollectionView) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
}
|
||||
```
|
||||
|
||||
O resto do processo é o mesmo da ```UITableView```
|
||||
|
||||
### 📰 Texto de várias linhas
|
||||
|
||||
|
||||

|
||||
|
||||
Quando você usar elementos com texto, ```SkeletonView``` desenha linhas para simular o texto.
|
||||
Além disso, você pode decidir quantas linhas você quer. Se ```numberOfLines``` está setado para zero (0), haverá um cálculo para saber quantas linhas são necessárias para preencher o skeleton inteiro e será desenhado. Caso contrário, se você setar para um (1) ou qualquer outro número maior que zero, só serão desenhadas aquele número de linhas.
|
||||
|
||||
##### 🎛 Customização
|
||||
|
||||
Você pode setar algumas propriedades para elementos de várias linhas.
|
||||
|
||||
|
||||
| Property | Values | Default | Preview
|
||||
| ------- | ------- |------- | -------
|
||||
| **Filling percent** of the last line. | `0...100` | `70%` | 
|
||||
| **Corner radius** of lines. (**NEW**) | `0...10` | `0` | 
|
||||
|
||||
|
||||
|
||||
Para modificar a percentagem ou o raio **usando código**, especifique as propriedades:
|
||||
```swift
|
||||
descriptionTextView.lastLineFillPercent = 50
|
||||
descriptionTextView.linesCornerRadius = 5
|
||||
```
|
||||
|
||||
Ou, se você preferir use **IB/Storyboard**:
|
||||
|
||||

|
||||
|
||||
### 🎨 Cores customizadas
|
||||
|
||||
Você pode decidir que cor o skeleton esta pintado. Você só precisa parametrizar a cor e o gradiente que deseja.
|
||||
|
||||
**Usando cores sólidas**
|
||||
``` swift
|
||||
view.showSkeleton(usingColor: UIColor.gray) // Solid
|
||||
// or
|
||||
view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0))
|
||||
```
|
||||
**Usando gradientes**
|
||||
``` swift
|
||||
let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
|
||||
view.showGradientSkeleton(usingGradient: gradient) // Gradient
|
||||
```
|
||||
|
||||
Além do mais, ```SkeletonView``` tem 20 cores flat 🤙🏼
|
||||
|
||||
```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...```
|
||||
|
||||

|
||||
###### Imagem capturada do site [https://flatuicolors.com](https://flatuicolors.com)
|
||||
|
||||
### 🦋 Aparência
|
||||
|
||||
**NOVIDADE** Os skeletons tem uma aparência padrão. Então, quando você não especifíca a cor, gradiente ou propriedades de várias linhas, `SkeletonView` usa os valores padrões.
|
||||
|
||||
Valores padrões:
|
||||
- **tintColor**: UIColor
|
||||
- *default: .clouds*
|
||||
- **gradient**: SkeletonGradient
|
||||
- *default: SkeletonGradient(baseColor: .clouds)*
|
||||
- **multilineHeight**: CGFloat
|
||||
- *default: 15*
|
||||
- **multilineSpacing**: CGFloat
|
||||
- *default: 10*
|
||||
- **multilineLastLineFillPercent**: Int
|
||||
- *default: 70*
|
||||
- **multilineCornerRadius**: Int
|
||||
- *default: 0*
|
||||
|
||||
Para obter esses valores padrões você pode usar `SkeletonAppearance.default`. Usando essa propriedade você pode declarar os valores também:
|
||||
```Swift
|
||||
SkeletonAppearance.default.multilineHeight = 20
|
||||
SkeletonAppearance.default.tintColor = .green
|
||||
```
|
||||
|
||||
|
||||
### 🤓 Animações customizadas
|
||||
|
||||
```SkeletonView``` tem duas animações pré-definidas, *pulse* para skeletons solidos e *sliding* para gradientes.
|
||||
|
||||
Além disso, se você quiser fazer suas próprias animações no skeleton, é muito fácil.
|
||||
|
||||
|
||||
Skeleton disponibiliza a função `showAnimatedSkeleton` que tem o closure ```SkeletonLayerAnimation``` onde você pode definir sua animação customizada.
|
||||
|
||||
```swift
|
||||
public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation
|
||||
```
|
||||
|
||||
Você pode chamar esta função assim:
|
||||
|
||||
```swift
|
||||
view.showAnimatedSkeleton { (layer) -> CAAnimation in
|
||||
let animation = CAAnimation()
|
||||
// Customize here your animation
|
||||
|
||||
return animation
|
||||
}
|
||||
```
|
||||
|
||||
Está disponível ```SkeletonAnimationBuilder```. É um construtor para ```SkeletonLayerAnimation```.
|
||||
|
||||
Hoje, você pode criar **sliding animations** para gradientes, decidindo a **direction** e setando a **duration** da animaçāo (padrão = 1.5s).
|
||||
|
||||
```swift
|
||||
// func makeSlidingAnimation(withDirection direction: GradientDirection, duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation
|
||||
|
||||
let animation = SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: .leftToRight)
|
||||
view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
|
||||
|
||||
```
|
||||
|
||||
```GradientDirection``` é um enum, com os seguintes cases:
|
||||
|
||||
| Direction | Preview
|
||||
|------- | -------
|
||||
| .leftRight | 
|
||||
| .rightLeft | 
|
||||
| .topBottom | 
|
||||
| .bottomTop | 
|
||||
| .topLeftBottomRight | 
|
||||
| .bottomRightTopLeft | 
|
||||
|
||||
> **😉 TRUQUE!**
|
||||
Existe outra forma de criar sliding animations, apenas usando este atalho:
|
||||
>>```let animation = GradientDirection.leftToRight.slidingAnimation()```
|
||||
|
||||
### 👨👧👦 Hierarquia
|
||||
|
||||
Já que ```SkeletonView``` é recursiva, e queremos que o skeleton seja muito eficiente, queremos parar a recursão assim que possível. Por este motivo, você deve setar a container view como `Skeletonable`, porque o Skeleton vai parar de procurar por subviews `skeletonable` assim que a view não for mais skeletonable, quebrando a recursão.
|
||||
|
||||
Porque uma imagem vale mais que mil palavras:
|
||||
|
||||
> ```ìsSkeletonable```= ☠️
|
||||
|
||||
| Configuration | Result
|
||||
|------- | -------
|
||||
| | 
|
||||
| | 
|
||||
| | 
|
||||
| | 
|
||||
|
||||
|
||||
|
||||
### 📚 Documentação
|
||||
Em breve...😅
|
||||
|
||||
## 📬 Próximos passos
|
||||
|
||||
* [x] Setar o percentual de preenchimento da última linha em elementos de várias linhas
|
||||
* [x] Adicionar mais animações de gradiente
|
||||
* [x] Suporte para resizable cells
|
||||
* [x] Compatível com CollectionView
|
||||
* [x] Compatível com tvOS
|
||||
* [x] Adicionar recovery state
|
||||
* [x] Aparência padrão customizável
|
||||
* [ ] Compatível com coleções customizáveis
|
||||
* [ ] Adicionar animações quando mostra/esconde os skeletons
|
||||
* [ ] Compatível com MacOS e WatchOS
|
||||
|
||||
## ❤️ Contribuindo
|
||||
Este é um projeto de código aberto, então sinta-se a vontade para contribuir. Como?
|
||||
- Abra uma [issue](https://github.com/Juanpe/SkeletonView/issues/new).
|
||||
- Envie feedback por [email](mailto://juanpecatalan.com).
|
||||
- Proponha seus próprios fixes, sugestões e abra um pull request com as alterações.
|
||||
|
||||
Ver [todos os contribuidores](https://github.com/Juanpe/SkeletonView/graphs/contributors)
|
||||
|
||||
###### Projeto gerado com [SwiftPlate](https://github.com/JohnSundell/SwiftPlate)
|
||||
|
||||
## 📢 Menções
|
||||
|
||||
- [iOS Dev Weekly #327](https://iosdevweekly.com/issues/327#start)
|
||||
- [Hacking with Swift Articles](https://www.hackingwithswift.com/articles/40/skeletonview-makes-loading-content-beautiful)
|
||||
- [Top 10 Swift Articles November](https://medium.mybridge.co/swift-top-10-articles-for-the-past-month-v-nov-2017-dfed7861cd65)
|
||||
- [30 Amazing iOS Swift Libraries (v2018)](https://medium.mybridge.co/30-amazing-ios-swift-libraries-for-the-past-year-v-2018-7cf15027eee9)
|
||||
- [AppCoda Weekly #44](http://digest.appcoda.com/issues/appcoda-weekly-issue-44-81899)
|
||||
- [iOS Cookies Newsletter #103](https://us11.campaign-archive.com/?u=cd1f3ed33c6527331d82107ba&id=48131a516d)
|
||||
- [Swift Developments Newsletter #113](https://andybargh.com/swiftdevelopments-113/)
|
||||
- [iOS Goodies #204](http://ios-goodies.com/post/167557280951/week-204)
|
||||
- [Swift Weekly #96](http://digest.swiftweekly.com/issues/swift-weekly-issue-96-81759)
|
||||
- [CocoaControls](https://www.cocoacontrols.com/controls/skeletonview)
|
||||
- [Awesome iOS Newsletter #74](https://ios.libhunt.com/newsletter/74)
|
||||
|
||||
|
||||
|
||||
## 👨🏻💻 Autor
|
||||
[1.1]: http://i.imgur.com/tXSoThF.png
|
||||
[1]: http://www.twitter.com/JuanpeCatalan
|
||||
|
||||
* Juanpe Catalán [![alt text][1.1]][1]
|
||||
|
||||
<a class="bmc-button" target="_blank" href="https://www.buymeacoffee.com/CDou4xtIK"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy me a coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;"><span style="margin-left:5px"></span></a>
|
||||
|
||||
## 👮🏻 Licença
|
||||
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Juanpe Catalán
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
@@ -0,0 +1,418 @@
|
||||

|
||||
|
||||
<p align="center">
|
||||
<a href="https://app.bitrise.io/app/6d289a17e22c8323">
|
||||
<img src="https://app.bitrise.io/app/6d289a17e22c8323/status.svg?token=fI7gKC41XD9-aRXDScCKBw&branch=master">
|
||||
</a>
|
||||
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
|
||||
<a href="https://github.com/Juanpe/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/p/SkeletonView.svg" alt="Platforms">
|
||||
</a>
|
||||
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
|
||||
<a href="https://cocoapods.org/pods/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
|
||||
</a>
|
||||
<a href="https://cocoapods.org/pods/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/dt/SkeletonView.svg?style=flat" alt="CocoaPods downloads" />
|
||||
</a>
|
||||
<a href="https://twitter.com/JuanpeCatalan">
|
||||
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
|
||||
</a>
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView¤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>
|
||||
<br/>
|
||||
<a href="https://twitter.com/intent/tweet?text=Wow%20This%20library%20is%20awesome:&url=https%3A%2F%2Fgithub.com%2FJuanpe%2FSkeletonView">
|
||||
<img src="https://img.shields.io/twitter/url/https/github.com/Juanpe/SkeletonView.svg?style=social" alt="License" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
🌎 翻译: [ [原版的](https://github.com/Juanpe/SkeletonView) ] </br>
|
||||
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) [@brunomunizaf](https://twitter.com/brunomuniz_af)
|
||||
|
||||
|
||||
今天,几乎所有的应用程序都有异步流程,例如:Api请求、长时间运行的流程等。虽然流程正在运行,但通常开发人员会设置一个加载视图来向用户显示正在发生的事情。
|
||||
|
||||
```SkeletonView``` 已经构想出来满足这种需求,这是一种优雅的方式,向用户展示正在发生的事情,并为他们等待的内容做好准备。
|
||||
|
||||
好好享受! 🙂
|
||||
|
||||
* [特征](#-特征)
|
||||
* [版本要求](#-版本要求)
|
||||
* [示例项目](#-示例)
|
||||
* [安装](#-安装)
|
||||
* [Cocoapods](#使用-cocoapods)
|
||||
* [Carthage](#使用-carthage)
|
||||
* [如何使用](#-如何使用)
|
||||
* [集合](#-集合)
|
||||
* [多行文字](#-多行文字)
|
||||
* [自定义颜色](#-自定义颜色)
|
||||
* [自定义动画](#-自定义动画)
|
||||
* [等级制度](#-等级制度)
|
||||
* [文档](#-文档)
|
||||
* [下一步](#-下一步)
|
||||
* [特约](#-特约)
|
||||
* [提及](#-提及)
|
||||
* [作者](#-作者)
|
||||
* [许可证](#-许可证)
|
||||
|
||||
|
||||
## 🌟 特征
|
||||
|
||||
- [x] 使用方便
|
||||
- [x] 支持所有 UIView
|
||||
- [x] 完全可定制
|
||||
- [x] 通用(iPhone和iPad)
|
||||
- [x] Interface Builder 友好
|
||||
- [x] 简单的 Swift 语法
|
||||
- [x] 轻量级可读代码库
|
||||
|
||||
### 📋 版本要求
|
||||
|
||||
* iOS 9.0+
|
||||
* tvOS 9.0+
|
||||
* Swift 4.2
|
||||
|
||||
### 🔮 示例
|
||||
|
||||
要运行示例项目,请克隆并运行 `SkeletonViewExample` 项目。
|
||||
|
||||
## 📲 安装
|
||||
|
||||
#### 使用 [CocoaPods](https://cocoapods.org)
|
||||
|
||||
使用 CocoaPods 编辑您的 Podfile 并指定依赖项:
|
||||
|
||||
```ruby
|
||||
pod "SkeletonView"
|
||||
```
|
||||
|
||||
#### 使用 [Carthage](https://github.com/carthage)
|
||||
|
||||
编辑您的 Cartfile 并指定依赖项:
|
||||
|
||||
```bash
|
||||
github "Juanpe/SkeletonView"
|
||||
```
|
||||
|
||||
## 🐒 如何使用
|
||||
|
||||
只需 **3** 个步骤即可使用 `SkeletonView`:
|
||||
|
||||
**1.** 在适当的位置导入SkeletonView
|
||||
```swift
|
||||
import SkeletonView
|
||||
```
|
||||
|
||||
**2.** 现在,您可以通过两种设置方式实现 `SkeletonView` 效果
|
||||
|
||||
**使用纯代码:**
|
||||
```swift
|
||||
avatarImageView.isSkeletonable = true
|
||||
```
|
||||
**使用 IB/Storyboards:**
|
||||
|
||||

|
||||
|
||||
**3.** 设置视图后,可以显示 **skeleton**. 并且您有 **4** 种效果可供选择:
|
||||
|
||||
```swift
|
||||
(1) view.showSkeleton() // 固体
|
||||
(2) view.showGradientSkeleton() // 渐变
|
||||
(3) view.showAnimatedSkeleton() // 纯色动画
|
||||
(4) view.showAnimatedGradientSkeleton() // 渐变动画
|
||||
```
|
||||
|
||||
**Preview**
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="25%">
|
||||
<center>固体</center>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<center>渐变</center>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<center>纯色动画</center>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<center>渐变动画</center>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="25%">
|
||||
<img src="Assets/solid.png"></img>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<img src="Assets/gradient.png"></img>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<img src="Assets/solid_animated.gif"></img>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<img src="Assets/gradient_animated.gif"></img>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
> **重要!**
|
||||
>>```SkeletonView``` 是递归的,所以如果你想在所有可骨架化的视图中显示骨架,你只需要在主容器视图中调用show方法。例如,使用UIViewControllers
|
||||
|
||||
### 🌿 集合
|
||||
|
||||
现在,```SkeletonView``` 兼容 ```UITableView``` 和 ```UICollectionView```。
|
||||
|
||||
###### UITableView
|
||||
|
||||
如果你要显示 skeleton 在一个 ```UITableView```上,你需要符合 ```SkeletonTableViewDataSource``` 协议。
|
||||
|
||||
``` swift
|
||||
public protocol SkeletonTableViewDataSource: UITableViewDataSource {
|
||||
func numSections(in collectionSkeletonView: UITableView) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
}
|
||||
```
|
||||
如您所见,此协议继承自 UITableViewDataSource,因此您可以使用骨架协议替换此协议。
|
||||
|
||||
该协议具有默认实现:
|
||||
|
||||
``` swift
|
||||
func numSections(in collectionSkeletonView: UITableView) -> Int
|
||||
// 默认值:1
|
||||
```
|
||||
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
|
||||
// 默认值:
|
||||
// 它计算填充整个tableview需要多少个单元格
|
||||
```
|
||||
|
||||
为了让Skeleton知道单元标识符,您只需要实现一种方法。此方法没有默认实现:
|
||||
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
```
|
||||
|
||||
**示例**
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
|
||||
return "CellIdentifier"
|
||||
}
|
||||
```
|
||||
|
||||
> **重要!**
|
||||
> 如果您使用可调整大小的单元格 (`tableView.rowHeight = UITableViewAutomaticDimension` ),则必须定义 `estimatedRowHeight`。
|
||||
|
||||
###### UICollectionView
|
||||
|
||||
要为 ```UICollectionView``` 设置效果, 您需要符合 ```SkeletonCollectionViewDataSource``` 协议。
|
||||
|
||||
``` swift
|
||||
public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
|
||||
func numSections(in collectionSkeletonView: UICollectionView) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
}
|
||||
```
|
||||
|
||||
其余操作与 ```UITableView``` 相同。
|
||||
|
||||
### 📰 多行文字
|
||||
|
||||
|
||||

|
||||
|
||||
使用带有文本的元素时, ```SkeletonView``` 绘制线条以模拟文本。此外,您可以决定您想要多少行。如果 ```numberOfLines``` 设置为零,它将计算填充整个骨架所需的行数,并将绘制它。相反,如果将其设置为一,二或任何大于零的数字,它将只绘制此行数。
|
||||
|
||||
##### 🎛 定制
|
||||
|
||||
您可以为多行元素设置一些属性。
|
||||
|
||||
|
||||
| 属性 | 值范围 | 默认 | 延时
|
||||
| ------- | ------- |------- | -------
|
||||
| **Filling percent** 最后一行的长度百分比 | `0...100` | `70%` | 
|
||||
| **Corner radius** 条目圆角半径. (**新**) | `0...10` | `0` | 
|
||||
|
||||
|
||||
|
||||
**纯代码**修改百分比或半径:
|
||||
|
||||
```swift
|
||||
descriptionTextView.lastLineFillPercent = 50
|
||||
descriptionTextView.linesCornerRadius = 5
|
||||
```
|
||||
|
||||
或者,如果您更喜欢使用 **IB/Storyboard**:
|
||||
|
||||

|
||||
|
||||
### 🎨 自定义颜色
|
||||
|
||||
您可以决定 ```SkeletonView``` 的显示颜色。您只需要传递颜色或渐变的参数。
|
||||
|
||||
**使用纯色**
|
||||
``` swift
|
||||
view.showSkeleton(usingColor: UIColor.gray) // 固体效果
|
||||
// 或者
|
||||
view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0))
|
||||
```
|
||||
**使用渐变色**
|
||||
``` swift
|
||||
let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
|
||||
view.showGradientSkeleton(usingGradient: gradient) // 梯度效果
|
||||
```
|
||||
|
||||
此外, ```SkeletonView``` 附带的 20 种颜色 🤙🏼
|
||||
|
||||
```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...```
|
||||
|
||||

|
||||
###### 从网站 [https://flatuicolors.com](https://flatuicolors.com)捕获的图像
|
||||
|
||||
### 🤓 自定义动画
|
||||
|
||||
现在,```SkeletonView``` 有两个内置动画,*pulse* 脉冲效果和 *sliding* 渐变滑动效果。
|
||||
|
||||
此外,如果你想做自己的 skeleton 动画,那真的很容易。
|
||||
|
||||
|
||||
Skeleton 提供了 `showAnimatedSkeleton` 一个具有 ```SkeletonLayerAnimation``` 闭包的功能,您可以在其中定义自定义动画。
|
||||
|
||||
```swift
|
||||
public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation
|
||||
```
|
||||
|
||||
您可以像这样调用函数:
|
||||
|
||||
```swift
|
||||
view.showAnimatedSkeleton { (layer) -> CAAnimation in
|
||||
let animation = CAAnimation()
|
||||
// 在这里自定义你的动画
|
||||
|
||||
return animation
|
||||
}
|
||||
```
|
||||
|
||||
**新** 它可用 ```SkeletonAnimationBuilder```。这是一个 ```SkeletonLayerAnimation```的衍生。
|
||||
|
||||
今天,您可以为渐变创建 **滑动动画**,确定 **方向** 并设置动画的 **持续时间** (默认值 = 1.5s)。
|
||||
|
||||
```swift
|
||||
// func makeSlidingAnimation(withDirection direction: GradientDirection, duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation
|
||||
|
||||
let animation = SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: .leftToRight)
|
||||
view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
|
||||
|
||||
```
|
||||
|
||||
```GradientDirection``` 是一个枚举,在这种情况下:
|
||||
|
||||
| 方向 | 效果
|
||||
|------- | -------
|
||||
| .leftRight | 
|
||||
| .rightLeft | 
|
||||
| .topBottom | 
|
||||
| .bottomTop | 
|
||||
| .topLeftBottomRight | 
|
||||
| .bottomRightTopLeft | 
|
||||
|
||||
> **😉 技巧!**
|
||||
存在另一种创建滑动动画的方法,只需使用此快捷方式:
|
||||
>>```let animation = GradientDirection.leftToRight.slidingAnimation()```
|
||||
|
||||
### 👨👧👦 等级制度
|
||||
|
||||
由于 ```SkeletonView``` 是递归的,我们希望 skeleton 效率高效, 我们希望尽快停止递归。因此,您必须将容器视图设置为 `Skeletonable` ,因为`skeletonable` 一旦视图不是 Skeletonable, Skeleton 将停止查找子视图,然后断开递归。
|
||||
|
||||
一图胜千言:
|
||||
|
||||
> 设置 ```ìsSkeletonable```= ☠️
|
||||
|
||||
| 分组 | 结果
|
||||
|------- | -------
|
||||
| | 
|
||||
| | 
|
||||
| | 
|
||||
| | 
|
||||
|
||||
|
||||
|
||||
### 📚 文档
|
||||
快出来...😅
|
||||
|
||||
## 📬 下一步
|
||||
|
||||
* [x] 设置多行元素中最后一行的填充百分比
|
||||
* [x] 添加更多渐变动画
|
||||
* [x] 支持可调整大小的单元
|
||||
* [x] CollectionView 兼容
|
||||
* [x] tvOS 兼容
|
||||
* [x] 添加恢复状态
|
||||
* [ ] 自定义集合兼容
|
||||
* [ ] 在显示/隐藏骨架时添加动画
|
||||
* [ ] MacOS 和 WatchOS兼容
|
||||
|
||||
## ❤️ 特约
|
||||
这是一个开源项目,所以请随时贡献。怎么样?
|
||||
|
||||
- 打开一个 [issue](https://github.com/Juanpe/SkeletonView/issues/new)
|
||||
- 反馈通过发送 [email](mailto://juanpecatalan.com)
|
||||
- 提出您自己的修复和建议,并带有拉取的请求。
|
||||
|
||||
查看 [所有贡献者](https://github.com/Juanpe/SkeletonView/graphs/contributors)
|
||||
|
||||
###### 使用 [SwiftPlate](https://github.com/JohnSundell/SwiftPlate) 生成的项目
|
||||
|
||||
## 📢 提及
|
||||
|
||||
- [iOS Dev Weekly #327](https://iosdevweekly.com/issues/327#start)
|
||||
- [Hacking with Swift Articles](https://www.hackingwithswift.com/articles/40/skeletonview-makes-loading-content-beautiful)
|
||||
- [Top 10 Swift Articles November](https://medium.mybridge.co/swift-top-10-articles-for-the-past-month-v-nov-2017-dfed7861cd65)
|
||||
- [30 Amazing iOS Swift Libraries (v2018)](https://medium.mybridge.co/30-amazing-ios-swift-libraries-for-the-past-year-v-2018-7cf15027eee9)
|
||||
- [AppCoda Weekly #44](http://digest.appcoda.com/issues/appcoda-weekly-issue-44-81899)
|
||||
- [iOS Cookies Newsletter #103](https://us11.campaign-archive.com/?u=cd1f3ed33c6527331d82107ba&id=48131a516d)
|
||||
- [Swift Developments Newsletter #113](https://andybargh.com/swiftdevelopments-113/)
|
||||
- [iOS Goodies #204](http://ios-goodies.com/post/167557280951/week-204)
|
||||
- [Swift Weekly #96](http://digest.swiftweekly.com/issues/swift-weekly-issue-96-81759)
|
||||
- [CocoaControls](https://www.cocoacontrols.com/controls/skeletonview)
|
||||
- [Awesome iOS Newsletter #74](https://ios.libhunt.com/newsletter/74)
|
||||
|
||||
|
||||
|
||||
## 👨🏻💻 作者
|
||||
[1.1]: http://i.imgur.com/tXSoThF.png
|
||||
[1]: http://www.twitter.com/JuanpeCatalan
|
||||
|
||||
* Juanpe Catalán [![alt text][1.1]][1]
|
||||
|
||||
<a class="bmc-button" target="_blank" href="https://www.buymeacoffee.com/CDou4xtIK"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy me a coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;"><span style="margin-left:5px"></span></a>
|
||||
|
||||
## 👮🏻 许可证
|
||||
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Juanpe Catalán
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "SkeletonView"
|
||||
s.version = "1.2"
|
||||
s.version = "1.5"
|
||||
s.summary = "An elegant way to show users that something is happening and also prepare them to which contents he is waiting"
|
||||
s.description = <<-DESC
|
||||
Today almost all apps have async processes, as API requests, long runing processes, etc. And while the processes are working, usually developers place a loading view to show users that something is going on.
|
||||
@@ -12,6 +12,7 @@ Pod::Spec.new do |s|
|
||||
s.social_media_url = "https://twitter.com/JuanpeCatalan"
|
||||
s.ios.deployment_target = "9.0"
|
||||
s.tvos.deployment_target = "9.0"
|
||||
s.swift_version = "5.0"
|
||||
s.source = { :git => "https://github.com/Juanpe/SkeletonView.git", :tag => s.version.to_s }
|
||||
s.source_files = "Sources/**/*"
|
||||
end
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
17DD0E12207FB28C00C56334 /* UIView+IBInspectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */; };
|
||||
17DD0E13207FB28C00C56334 /* UIView+UIApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */; };
|
||||
17DD0E14207FB28F00C56334 /* SkeletonAnimationBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */; };
|
||||
17DD0E15207FB28F00C56334 /* SkeletonDefaultConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2F1FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift */; };
|
||||
17DD0E15207FB28F00C56334 /* SkeletonAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2F1FB0EC9D00EE67C5 /* SkeletonAppearance.swift */; };
|
||||
17DD0E16207FB28F00C56334 /* SkeletonGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2B1FAF6BC900EE67C5 /* SkeletonGradient.swift */; };
|
||||
17DD0E17207FB28F00C56334 /* SkeletonLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F899E81FAB9D2B002E8FDA /* SkeletonLayer.swift */; };
|
||||
17DD0E18207FB28F00C56334 /* SkeletonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* SkeletonView.swift */; };
|
||||
@@ -29,6 +29,30 @@
|
||||
17DD0E1D207FB32100C56334 /* ContainsMultilineText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */; };
|
||||
17DD0E1E207FB32100C56334 /* PrepareForSkeletonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */; };
|
||||
17DD0E1F207FB32100C56334 /* RecursiveProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */; };
|
||||
3B83EE4820C41488005178A4 /* UIView+IBInspectable.swift in Headers */ = {isa = PBXBuildFile; fileRef = 3B83EE4620C41488005178A4 /* UIView+IBInspectable.swift */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
42ABD063210B548200BEEFF4 /* SkeletonView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* SkeletonView.framework */; };
|
||||
42ABD069210B548200BEEFF4 /* SkeletonView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* SkeletonView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
42ABD078210B54E200BEEFF4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD070210B54E100BEEFF4 /* AppDelegate.swift */; };
|
||||
42ABD079210B54E200BEEFF4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD071210B54E100BEEFF4 /* Main.storyboard */; };
|
||||
42ABD07A210B54E200BEEFF4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD072210B54E100BEEFF4 /* Assets.xcassets */; };
|
||||
42ABD07B210B54E200BEEFF4 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD073210B54E100BEEFF4 /* ViewController.swift */; };
|
||||
42ABD07C210B54E200BEEFF4 /* Base.lproj in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD074210B54E100BEEFF4 /* Base.lproj */; };
|
||||
42ABD07F210B54E200BEEFF4 /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */; };
|
||||
872D5A5621C177E20037D763 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5521C177E20037D763 /* UIView+Extension.swift */; };
|
||||
872D5A5721C177E20037D763 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5521C177E20037D763 /* UIView+Extension.swift */; };
|
||||
872D5A5C21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */; };
|
||||
872D5A5D21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */; };
|
||||
872D5A5F21C24F8E0037D763 /* UITextView+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */; };
|
||||
872D5A6021C24F8E0037D763 /* UITextView+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */; };
|
||||
8748240D20C6A88A00E92179 /* UIView+IBInspectable.swift in Headers */ = {isa = PBXBuildFile; fileRef = F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
8748240E20C6A88E00E92179 /* ContainsMultilineText.swift in Headers */ = {isa = PBXBuildFile; fileRef = F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
877EFA4521BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */; };
|
||||
877EFA4621BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */; };
|
||||
877EFA4821BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */; };
|
||||
877EFA4921BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */; };
|
||||
8785E3A1211C9C9800CC9DFD /* SkeletonDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */; };
|
||||
8785E3A2211C9CA500CC9DFD /* SkeletonDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */; };
|
||||
8785E3A3211C9CE800CC9DFD /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88DEA97D1FCDBD1F006C80EF /* Constants.swift */; };
|
||||
88DEA97F1FCDBD78006C80EF /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88DEA97D1FCDBD1F006C80EF /* Constants.swift */; };
|
||||
8933C7851EB5B820000D00A4 /* SkeletonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* SkeletonView.swift */; };
|
||||
F51DE1091FBF70A70037919A /* SkeletonAnimationBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */; };
|
||||
@@ -40,7 +64,7 @@
|
||||
F51ED28520973CC9008B2434 /* SkeletonReusableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F51DF870206E91B300D23301 /* SkeletonReusableCell.swift */; };
|
||||
F5307E2C1FAF6BC900EE67C5 /* SkeletonGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2B1FAF6BC900EE67C5 /* SkeletonGradient.swift */; };
|
||||
F5307E2E1FB0E5E400EE67C5 /* UIView+Frame.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2D1FB0E5E400EE67C5 /* UIView+Frame.swift */; };
|
||||
F5307E301FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2F1FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift */; };
|
||||
F5307E301FB0EC9D00EE67C5 /* SkeletonAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2F1FB0EC9D00EE67C5 /* SkeletonAppearance.swift */; };
|
||||
F5307E321FB0F42F00EE67C5 /* RecursiveProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */; };
|
||||
F5307E371FB1076E00EE67C5 /* SkeletonTableViewProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E361FB1076E00EE67C5 /* SkeletonTableViewProtocols.swift */; };
|
||||
F5307E391FB1078E00EE67C5 /* SkeletonCollectionViewProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E381FB1078E00EE67C5 /* SkeletonCollectionViewProtocols.swift */; };
|
||||
@@ -53,6 +77,14 @@
|
||||
F56B94461FAE20AF0095662F /* PrepareForSkeletonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */; };
|
||||
F587FB84202CBFC8002DB5FE /* SkeletonFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F587FB83202CBFC8002DB5FE /* SkeletonFlow.swift */; };
|
||||
F587FB86202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */; };
|
||||
F58A6E6B20A8C54100612494 /* RecoverableViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F58A6E6A20A8C54100612494 /* RecoverableViewState.swift */; };
|
||||
F58A6E6C20A8C54100612494 /* RecoverableViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F58A6E6A20A8C54100612494 /* RecoverableViewState.swift */; };
|
||||
F58A6E6E20A8C66300612494 /* Recoverable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F58A6E6D20A8C66300612494 /* Recoverable.swift */; };
|
||||
F58A6E6F20A8C66300612494 /* Recoverable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F58A6E6D20A8C66300612494 /* Recoverable.swift */; };
|
||||
F5A5E8B4211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5A5E8B3211DCE7000FD20D0 /* Int+Whitespaces.swift */; };
|
||||
F5A5E8B5211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5A5E8B3211DCE7000FD20D0 /* Int+Whitespaces.swift */; };
|
||||
F5D3FB0B209DCAA300003FCF /* SubviewsSkeletonables.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5D3FB0A209DCAA300003FCF /* SubviewsSkeletonables.swift */; };
|
||||
F5D3FB0C209DCAA300003FCF /* SubviewsSkeletonables.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5D3FB0A209DCAA300003FCF /* SubviewsSkeletonables.swift */; };
|
||||
F5F622411FAC6E31007C062A /* UIColor+Skeleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F622401FAC6E31007C062A /* UIColor+Skeleton.swift */; };
|
||||
F5F622431FAC81FD007C062A /* CALayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F622421FAC81FD007C062A /* CALayer+Extensions.swift */; };
|
||||
F5F622451FACA338007C062A /* Cell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F622441FACA338007C062A /* Cell.swift */; };
|
||||
@@ -69,6 +101,13 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
42ABD05C210B548200BEEFF4 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 52D6D9731BEFF229002C0205 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 52D6D97B1BEFF229002C0205;
|
||||
remoteInfo = "SkeletonView-iOS";
|
||||
};
|
||||
F5307E431FB3B84500EE67C5 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 52D6D9731BEFF229002C0205 /* Project object */;
|
||||
@@ -79,6 +118,17 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
42ABD068210B548200BEEFF4 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
42ABD069210B548200BEEFF4 /* SkeletonView.framework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F5307E451FB3B84600EE67C5 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -96,7 +146,22 @@
|
||||
17DD0E00207FB27400C56334 /* SkeletonView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SkeletonView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
17DD0E1A207FB2C200C56334 /* SkeletonView-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SkeletonView-tvOS.plist"; sourceTree = "<group>"; };
|
||||
17DD0E1B207FB2C200C56334 /* SkeletonView-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SkeletonView-iOS.plist"; sourceTree = "<group>"; };
|
||||
3B83EE4620C41488005178A4 /* UIView+IBInspectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "UIView+IBInspectable.swift"; path = "Sources/Extensions/UIView+IBInspectable.swift"; sourceTree = "<group>"; };
|
||||
42ABD06D210B548200BEEFF4 /* SkeletonViewExampleUICollectionView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SkeletonViewExampleUICollectionView.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
42ABD070210B54E100BEEFF4 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
42ABD071210B54E100BEEFF4 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
|
||||
42ABD072210B54E100BEEFF4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
42ABD073210B54E100BEEFF4 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
42ABD074210B54E100BEEFF4 /* Base.lproj */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Base.lproj; sourceTree = "<group>"; };
|
||||
42ABD076210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SkeletonViewExampleCollectionview-Info.plist"; sourceTree = "<group>"; };
|
||||
42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
52D6D97C1BEFF229002C0205 /* SkeletonView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SkeletonView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
872D5A5521C177E20037D763 /* UIView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = "<group>"; };
|
||||
872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Multiline.swift"; sourceTree = "<group>"; };
|
||||
872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+Multiline.swift"; sourceTree = "<group>"; };
|
||||
877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonLayerBuilder.swift; sourceTree = "<group>"; };
|
||||
877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonMultilineLayerBuilder.swift; sourceTree = "<group>"; };
|
||||
8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonDebug.swift; sourceTree = "<group>"; };
|
||||
88DEA97D1FCDBD1F006C80EF /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
||||
8933C7841EB5B820000D00A4 /* SkeletonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkeletonView.swift; sourceTree = "<group>"; };
|
||||
F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonAnimationBuilder.swift; sourceTree = "<group>"; };
|
||||
@@ -105,7 +170,7 @@
|
||||
F51DF878206E9F5500D23301 /* SkeletonCollectionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonCollectionDelegate.swift; sourceTree = "<group>"; };
|
||||
F5307E2B1FAF6BC900EE67C5 /* SkeletonGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonGradient.swift; sourceTree = "<group>"; };
|
||||
F5307E2D1FB0E5E400EE67C5 /* UIView+Frame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Frame.swift"; sourceTree = "<group>"; };
|
||||
F5307E2F1FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonDefaultConfig.swift; sourceTree = "<group>"; };
|
||||
F5307E2F1FB0EC9D00EE67C5 /* SkeletonAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonAppearance.swift; sourceTree = "<group>"; };
|
||||
F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecursiveProtocol.swift; sourceTree = "<group>"; };
|
||||
F5307E361FB1076E00EE67C5 /* SkeletonTableViewProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonTableViewProtocols.swift; sourceTree = "<group>"; };
|
||||
F5307E381FB1078E00EE67C5 /* SkeletonCollectionViewProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonCollectionViewProtocols.swift; sourceTree = "<group>"; };
|
||||
@@ -116,6 +181,10 @@
|
||||
F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrepareForSkeletonProtocol.swift; sourceTree = "<group>"; };
|
||||
F587FB83202CBFC8002DB5FE /* SkeletonFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonFlow.swift; sourceTree = "<group>"; };
|
||||
F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+UIApplicationDelegate.swift"; sourceTree = "<group>"; };
|
||||
F58A6E6A20A8C54100612494 /* RecoverableViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoverableViewState.swift; sourceTree = "<group>"; };
|
||||
F58A6E6D20A8C66300612494 /* Recoverable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Recoverable.swift; sourceTree = "<group>"; };
|
||||
F5A5E8B3211DCE7000FD20D0 /* Int+Whitespaces.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Whitespaces.swift"; sourceTree = "<group>"; };
|
||||
F5D3FB0A209DCAA300003FCF /* SubviewsSkeletonables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubviewsSkeletonables.swift; sourceTree = "<group>"; };
|
||||
F5F622401FAC6E31007C062A /* UIColor+Skeleton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Skeleton.swift"; sourceTree = "<group>"; };
|
||||
F5F622421FAC81FD007C062A /* CALayer+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+Extensions.swift"; sourceTree = "<group>"; };
|
||||
F5F622441FACA338007C062A /* Cell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cell.swift; sourceTree = "<group>"; };
|
||||
@@ -141,6 +210,14 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
42ABD062210B548200BEEFF4 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
42ABD063210B548200BEEFF4 /* SkeletonView.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
52D6D9781BEFF229002C0205 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -162,6 +239,7 @@
|
||||
52D6D9721BEFF229002C0205 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3B83EE4620C41488005178A4 /* UIView+IBInspectable.swift */,
|
||||
F5F899F31FABA607002E8FDA /* Example */,
|
||||
8933C7811EB5B7E0000D00A4 /* Sources */,
|
||||
52D6D99C1BEFF38C002C0205 /* Configs */,
|
||||
@@ -176,6 +254,7 @@
|
||||
52D6D97C1BEFF229002C0205 /* SkeletonView.framework */,
|
||||
F5F899F21FABA607002E8FDA /* SkeletonViewExample.app */,
|
||||
17DD0E00207FB27400C56334 /* SkeletonView.framework */,
|
||||
42ABD06D210B548200BEEFF4 /* SkeletonViewExampleUICollectionView.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -189,18 +268,85 @@
|
||||
path = Configs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
872D5A5821C17D850037D763 /* CollectionView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
42ABD070210B54E100BEEFF4 /* AppDelegate.swift */,
|
||||
42ABD072210B54E100BEEFF4 /* Assets.xcassets */,
|
||||
42ABD074210B54E100BEEFF4 /* Base.lproj */,
|
||||
42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */,
|
||||
42ABD071210B54E100BEEFF4 /* Main.storyboard */,
|
||||
42ABD073210B54E100BEEFF4 /* ViewController.swift */,
|
||||
);
|
||||
path = CollectionView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
872D5A5921C17D980037D763 /* TableView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F5F899F41FABA607002E8FDA /* AppDelegate.swift */,
|
||||
88DEA97D1FCDBD1F006C80EF /* Constants.swift */,
|
||||
F5F899F61FABA607002E8FDA /* ViewController.swift */,
|
||||
F5F622441FACA338007C062A /* Cell.swift */,
|
||||
F5F899F81FABA607002E8FDA /* Main.storyboard */,
|
||||
F5F899FB1FABA607002E8FDA /* Assets.xcassets */,
|
||||
F5F899FD1FABA607002E8FDA /* LaunchScreen.storyboard */,
|
||||
);
|
||||
path = TableView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
872D5A5A21C24E7C0037D763 /* Multilines */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */,
|
||||
872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */,
|
||||
872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */,
|
||||
);
|
||||
path = Multilines;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
877EFA4321BEE9C40031FC00 /* Builders */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */,
|
||||
877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */,
|
||||
);
|
||||
path = Builders;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8785E39E211C9C6D00CC9DFD /* Appearance */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F5307E2F1FB0EC9D00EE67C5 /* SkeletonAppearance.swift */,
|
||||
);
|
||||
path = Appearance;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8785E39F211C9C7C00CC9DFD /* Debug */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */,
|
||||
);
|
||||
path = Debug;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8933C7811EB5B7E0000D00A4 /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
872D5A5A21C24E7C0037D763 /* Multilines */,
|
||||
877EFA4321BEE9C40031FC00 /* Builders */,
|
||||
8785E39F211C9C7C00CC9DFD /* Debug */,
|
||||
8785E39E211C9C6D00CC9DFD /* Appearance */,
|
||||
F58A6E7020A8C87100612494 /* Recoverable */,
|
||||
F5307E331FB1068500EE67C5 /* Collections */,
|
||||
F5307E341FB106A500EE67C5 /* Extensions */,
|
||||
F5307E351FB106BF00EE67C5 /* Helpers */,
|
||||
F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */,
|
||||
F5307E2F1FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift */,
|
||||
F587FB83202CBFC8002DB5FE /* SkeletonFlow.swift */,
|
||||
F5307E2B1FAF6BC900EE67C5 /* SkeletonGradient.swift */,
|
||||
F5F899E81FAB9D2B002E8FDA /* SkeletonLayer.swift */,
|
||||
8933C7841EB5B820000D00A4 /* SkeletonView.swift */,
|
||||
F587FB83202CBFC8002DB5FE /* SkeletonFlow.swift */,
|
||||
F5D3FB0A209DCAA300003FCF /* SubviewsSkeletonables.swift */,
|
||||
);
|
||||
path = Sources;
|
||||
sourceTree = "<group>";
|
||||
@@ -262,7 +408,9 @@
|
||||
F5F622401FAC6E31007C062A /* UIColor+Skeleton.swift */,
|
||||
F5307E2D1FB0E5E400EE67C5 /* UIView+Frame.swift */,
|
||||
F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */,
|
||||
872D5A5521C177E20037D763 /* UIView+Extension.swift */,
|
||||
F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */,
|
||||
F5A5E8B3211DCE7000FD20D0 /* Int+Whitespaces.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@@ -271,23 +419,26 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F5F899D11FAB9630002E8FDA /* AssociationPolicy.swift */,
|
||||
F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */,
|
||||
F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */,
|
||||
F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F58A6E7020A8C87100612494 /* Recoverable */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F58A6E6A20A8C54100612494 /* RecoverableViewState.swift */,
|
||||
F58A6E6D20A8C66300612494 /* Recoverable.swift */,
|
||||
);
|
||||
path = Recoverable;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F5F899F31FABA607002E8FDA /* Example */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F5F899F41FABA607002E8FDA /* AppDelegate.swift */,
|
||||
88DEA97D1FCDBD1F006C80EF /* Constants.swift */,
|
||||
F5F899F61FABA607002E8FDA /* ViewController.swift */,
|
||||
F5F622441FACA338007C062A /* Cell.swift */,
|
||||
F5F899F81FABA607002E8FDA /* Main.storyboard */,
|
||||
F5F899FB1FABA607002E8FDA /* Assets.xcassets */,
|
||||
F5F899FD1FABA607002E8FDA /* LaunchScreen.storyboard */,
|
||||
872D5A5921C17D980037D763 /* TableView */,
|
||||
872D5A5821C17D850037D763 /* CollectionView */,
|
||||
);
|
||||
path = Example;
|
||||
sourceTree = "<group>";
|
||||
@@ -302,6 +453,7 @@
|
||||
F5F89A061FABA725002E8FDA /* Example */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
42ABD076210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist */,
|
||||
F5F89A001FABA607002E8FDA /* SkeletonViewExampleInfo.plist */,
|
||||
);
|
||||
name = Example;
|
||||
@@ -314,6 +466,8 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8748240D20C6A88A00E92179 /* UIView+IBInspectable.swift in Headers */,
|
||||
8748240E20C6A88E00E92179 /* ContainsMultilineText.swift in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -321,6 +475,7 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3B83EE4820C41488005178A4 /* UIView+IBInspectable.swift in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -345,6 +500,25 @@
|
||||
productReference = 17DD0E00207FB27400C56334 /* SkeletonView.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
42ABD05A210B548200BEEFF4 /* SkeletonViewExampleUICollectionView */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 42ABD06A210B548200BEEFF4 /* Build configuration list for PBXNativeTarget "SkeletonViewExampleUICollectionView" */;
|
||||
buildPhases = (
|
||||
42ABD05D210B548200BEEFF4 /* Sources */,
|
||||
42ABD062210B548200BEEFF4 /* Frameworks */,
|
||||
42ABD064210B548200BEEFF4 /* Resources */,
|
||||
42ABD068210B548200BEEFF4 /* Embed Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
42ABD05B210B548200BEEFF4 /* PBXTargetDependency */,
|
||||
);
|
||||
name = SkeletonViewExampleUICollectionView;
|
||||
productName = SkeletonViewExample;
|
||||
productReference = 42ABD06D210B548200BEEFF4 /* SkeletonViewExampleUICollectionView.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
52D6D97B1BEFF229002C0205 /* SkeletonView-iOS */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "SkeletonView-iOS" */;
|
||||
@@ -389,28 +563,31 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0910;
|
||||
LastUpgradeCheck = 0930;
|
||||
LastUpgradeCheck = 1020;
|
||||
ORGANIZATIONNAME = SkeletonView;
|
||||
TargetAttributes = {
|
||||
17DD0DFF207FB27400C56334 = {
|
||||
CreatedOnToolsVersion = 9.3;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
42ABD05A210B548200BEEFF4 = {
|
||||
LastSwiftMigration = 0940;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
52D6D97B1BEFF229002C0205 = {
|
||||
CreatedOnToolsVersion = 7.1;
|
||||
DevelopmentTeam = KWEMDK92F4;
|
||||
LastSwiftMigration = 0800;
|
||||
LastSwiftMigration = 1000;
|
||||
};
|
||||
F5F899F11FABA607002E8FDA = {
|
||||
CreatedOnToolsVersion = 9.1;
|
||||
DevelopmentTeam = KWEMDK92F4;
|
||||
LastSwiftMigration = 1000;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "SkeletonView" */;
|
||||
compatibilityVersion = "Xcode 6.3";
|
||||
developmentRegion = English;
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
@@ -424,6 +601,7 @@
|
||||
52D6D97B1BEFF229002C0205 /* SkeletonView-iOS */,
|
||||
17DD0DFF207FB27400C56334 /* SkeletonView-tvOS */,
|
||||
F5F899F11FABA607002E8FDA /* SkeletonViewExample */,
|
||||
42ABD05A210B548200BEEFF4 /* SkeletonViewExampleUICollectionView */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -436,6 +614,16 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
42ABD064210B548200BEEFF4 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
42ABD07A210B54E200BEEFF4 /* Assets.xcassets in Resources */,
|
||||
42ABD07C210B54E200BEEFF4 /* Base.lproj in Resources */,
|
||||
42ABD079210B54E200BEEFF4 /* Main.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
52D6D97A1BEFF229002C0205 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -460,60 +648,91 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
872D5A5721C177E20037D763 /* UIView+Extension.swift in Sources */,
|
||||
17DD0E1F207FB32100C56334 /* RecursiveProtocol.swift in Sources */,
|
||||
872D5A5D21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */,
|
||||
17DD0E1E207FB32100C56334 /* PrepareForSkeletonProtocol.swift in Sources */,
|
||||
F51ED28520973CC9008B2434 /* SkeletonReusableCell.swift in Sources */,
|
||||
17DD0E17207FB28F00C56334 /* SkeletonLayer.swift in Sources */,
|
||||
8785E3A2211C9CA500CC9DFD /* SkeletonDebug.swift in Sources */,
|
||||
17DD0E08207FB28900C56334 /* CollectionSkeletonProtocol.swift in Sources */,
|
||||
F58A6E6C20A8C54100612494 /* RecoverableViewState.swift in Sources */,
|
||||
17DD0E0B207FB28900C56334 /* SkeletonTableViewProtocols.swift in Sources */,
|
||||
17DD0E0D207FB28900C56334 /* UITableView+CollectionSkeleton.swift in Sources */,
|
||||
17DD0E0E207FB28900C56334 /* UIView+CollectionSkeleton.swift in Sources */,
|
||||
877EFA4921BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */,
|
||||
F51ED28320973CBB008B2434 /* SkeletonCollectionDelegate.swift in Sources */,
|
||||
F58A6E6F20A8C66300612494 /* Recoverable.swift in Sources */,
|
||||
17DD0E18207FB28F00C56334 /* SkeletonView.swift in Sources */,
|
||||
17DD0E19207FB28F00C56334 /* SkeletonFlow.swift in Sources */,
|
||||
17DD0E09207FB28900C56334 /* SkeletonCollectionDataSource.swift in Sources */,
|
||||
17DD0E0C207FB28900C56334 /* UICollectionView+CollectionSkeleton.swift in Sources */,
|
||||
17DD0E13207FB28C00C56334 /* UIView+UIApplicationDelegate.swift in Sources */,
|
||||
F5D3FB0C209DCAA300003FCF /* SubviewsSkeletonables.swift in Sources */,
|
||||
17DD0E12207FB28C00C56334 /* UIView+IBInspectable.swift in Sources */,
|
||||
17DD0E14207FB28F00C56334 /* SkeletonAnimationBuilder.swift in Sources */,
|
||||
17DD0E11207FB28C00C56334 /* UIView+Frame.swift in Sources */,
|
||||
17DD0E15207FB28F00C56334 /* SkeletonDefaultConfig.swift in Sources */,
|
||||
17DD0E15207FB28F00C56334 /* SkeletonAppearance.swift in Sources */,
|
||||
872D5A6021C24F8E0037D763 /* UITextView+Multiline.swift in Sources */,
|
||||
17DD0E10207FB28C00C56334 /* UIColor+Skeleton.swift in Sources */,
|
||||
F51ED28420973CC6008B2434 /* GenericCollectionView.swift in Sources */,
|
||||
17DD0E0A207FB28900C56334 /* SkeletonCollectionViewProtocols.swift in Sources */,
|
||||
17DD0E1C207FB32100C56334 /* AssociationPolicy.swift in Sources */,
|
||||
877EFA4621BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */,
|
||||
F5A5E8B5211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */,
|
||||
17DD0E1D207FB32100C56334 /* ContainsMultilineText.swift in Sources */,
|
||||
17DD0E0F207FB28C00C56334 /* CALayer+Extensions.swift in Sources */,
|
||||
17DD0E16207FB28F00C56334 /* SkeletonGradient.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
42ABD05D210B548200BEEFF4 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
42ABD07B210B54E200BEEFF4 /* ViewController.swift in Sources */,
|
||||
42ABD078210B54E200BEEFF4 /* AppDelegate.swift in Sources */,
|
||||
42ABD07F210B54E200BEEFF4 /* CollectionViewCell.swift in Sources */,
|
||||
8785E3A3211C9CE800CC9DFD /* Constants.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
52D6D9771BEFF229002C0205 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
872D5A5621C177E20037D763 /* UIView+Extension.swift in Sources */,
|
||||
F5F899D01FAA6A4D002E8FDA /* UIView+IBInspectable.swift in Sources */,
|
||||
872D5A5C21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */,
|
||||
F54CF5E42024CEB000330B0D /* UIView+CollectionSkeleton.swift in Sources */,
|
||||
F5307E371FB1076E00EE67C5 /* SkeletonTableViewProtocols.swift in Sources */,
|
||||
8933C7851EB5B820000D00A4 /* SkeletonView.swift in Sources */,
|
||||
F5307E301FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift in Sources */,
|
||||
8785E3A1211C9C9800CC9DFD /* SkeletonDebug.swift in Sources */,
|
||||
F5307E301FB0EC9D00EE67C5 /* SkeletonAppearance.swift in Sources */,
|
||||
F58A6E6B20A8C54100612494 /* RecoverableViewState.swift in Sources */,
|
||||
F54CF5E52024CEB000330B0D /* UITableView+CollectionSkeleton.swift in Sources */,
|
||||
F5307E321FB0F42F00EE67C5 /* RecursiveProtocol.swift in Sources */,
|
||||
F5F622431FAC81FD007C062A /* CALayer+Extensions.swift in Sources */,
|
||||
877EFA4821BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */,
|
||||
F5F899ED1FAB9F04002E8FDA /* CollectionSkeletonProtocol.swift in Sources */,
|
||||
F58A6E6E20A8C66300612494 /* Recoverable.swift in Sources */,
|
||||
F5307E2C1FAF6BC900EE67C5 /* SkeletonGradient.swift in Sources */,
|
||||
F587FB86202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift in Sources */,
|
||||
F5F899EB1FAB9DA3002E8FDA /* SkeletonCollectionDataSource.swift in Sources */,
|
||||
F5F622411FAC6E31007C062A /* UIColor+Skeleton.swift in Sources */,
|
||||
F587FB84202CBFC8002DB5FE /* SkeletonFlow.swift in Sources */,
|
||||
F5D3FB0B209DCAA300003FCF /* SubviewsSkeletonables.swift in Sources */,
|
||||
F5F899D21FAB9630002E8FDA /* AssociationPolicy.swift in Sources */,
|
||||
F54CF5E62024CEB000330B0D /* UICollectionView+CollectionSkeleton.swift in Sources */,
|
||||
F56B94461FAE20AF0095662F /* PrepareForSkeletonProtocol.swift in Sources */,
|
||||
F5307E391FB1078E00EE67C5 /* SkeletonCollectionViewProtocols.swift in Sources */,
|
||||
872D5A5F21C24F8E0037D763 /* UITextView+Multiline.swift in Sources */,
|
||||
F51DF873206E91FB00D23301 /* GenericCollectionView.swift in Sources */,
|
||||
F5307E3B1FB123C100EE67C5 /* ContainsMultilineText.swift in Sources */,
|
||||
F5F899E91FAB9D2B002E8FDA /* SkeletonLayer.swift in Sources */,
|
||||
F5307E2E1FB0E5E400EE67C5 /* UIView+Frame.swift in Sources */,
|
||||
877EFA4521BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */,
|
||||
F5A5E8B4211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */,
|
||||
F51DF871206E91B300D23301 /* SkeletonReusableCell.swift in Sources */,
|
||||
F51DF879206E9F5500D23301 /* SkeletonCollectionDelegate.swift in Sources */,
|
||||
F51DE1091FBF70A70037919A /* SkeletonAnimationBuilder.swift in Sources */,
|
||||
@@ -534,6 +753,11 @@
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
42ABD05B210B548200BEEFF4 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 52D6D97B1BEFF229002C0205 /* SkeletonView-iOS */;
|
||||
targetProxy = 42ABD05C210B548200BEEFF4 /* PBXContainerItemProxy */;
|
||||
};
|
||||
F5307E441FB3B84500EE67C5 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 52D6D97B1BEFF229002C0205 /* SkeletonView-iOS */;
|
||||
@@ -587,7 +811,7 @@
|
||||
SDKROOT = appletvos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 3;
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
};
|
||||
@@ -619,16 +843,70 @@
|
||||
SDKROOT = appletvos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 3;
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
42ABD06B210B548200BEEFF4 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = "Configs/SkeletonViewExampleCollectionview-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
42ABD06C210B548200BEEFF4 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = "Configs/SkeletonViewExampleCollectionview-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
52D6D98E1BEFF229002C0205 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
@@ -677,7 +955,7 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@@ -688,6 +966,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
@@ -728,7 +1007,7 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
@@ -744,7 +1023,7 @@
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = KWEMDK92F4;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
@@ -757,7 +1036,7 @@
|
||||
PRODUCT_NAME = SkeletonView;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -769,7 +1048,7 @@
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = KWEMDK92F4;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
@@ -781,7 +1060,7 @@
|
||||
PRODUCT_NAME = SkeletonView;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -797,7 +1076,7 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = KWEMDK92F4;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/Configs/SkeletonViewExampleInfo.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
@@ -805,7 +1084,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -822,7 +1101,7 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = KWEMDK92F4;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/Configs/SkeletonViewExampleInfo.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
@@ -830,7 +1109,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
@@ -847,6 +1126,15 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
42ABD06A210B548200BEEFF4 /* Build configuration list for PBXNativeTarget "SkeletonViewExampleUICollectionView" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
42ABD06B210B548200BEEFF4 /* Debug */,
|
||||
42ABD06C210B548200BEEFF4 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "SkeletonView" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>FILEHEADER</key>
|
||||
<string> ___COPYRIGHT___</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0930"
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0930"
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright © 2017 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
public protocol Appearance {
|
||||
var tintColor: UIColor { get set }
|
||||
var gradient: SkeletonGradient { get set }
|
||||
var multilineHeight: CGFloat { get set }
|
||||
var multilineSpacing: CGFloat { get set }
|
||||
var multilineLastLineFillPercent: Int { get set }
|
||||
var multilineCornerRadius: Int { get set }
|
||||
}
|
||||
|
||||
public enum SkeletonAppearance {
|
||||
public static var `default`: Appearance = SkeletonViewAppearance.shared
|
||||
}
|
||||
|
||||
// codebeat:disable[TOO_MANY_IVARS]
|
||||
class SkeletonViewAppearance: Appearance {
|
||||
|
||||
static var shared = SkeletonViewAppearance()
|
||||
|
||||
var tintColor: UIColor = .clouds
|
||||
|
||||
var gradient: SkeletonGradient = SkeletonGradient(baseColor: .clouds)
|
||||
|
||||
var multilineHeight: CGFloat = 15
|
||||
|
||||
var multilineSpacing: CGFloat = 10
|
||||
|
||||
var multilineLastLineFillPercent: Int = 70
|
||||
|
||||
var multilineCornerRadius: Int = 0
|
||||
}
|
||||
// codebeat:enable[TOO_MANY_IVARS]
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
/// Object that facilitates the creation of skeleton layers,
|
||||
/// based on the builder pattern
|
||||
class SkeletonLayerBuilder {
|
||||
|
||||
var skeletonType: SkeletonType?
|
||||
var colors: [UIColor] = []
|
||||
var holder: UIView?
|
||||
|
||||
func setSkeletonType(_ type: SkeletonType) -> SkeletonLayerBuilder {
|
||||
self.skeletonType = type
|
||||
return self
|
||||
}
|
||||
|
||||
func addColor(_ color: UIColor) -> SkeletonLayerBuilder {
|
||||
return addColors([color])
|
||||
}
|
||||
|
||||
func addColors(_ colors: [UIColor]) -> SkeletonLayerBuilder {
|
||||
self.colors.append(contentsOf: colors)
|
||||
return self
|
||||
}
|
||||
|
||||
func setHolder(_ holder: UIView) -> SkeletonLayerBuilder {
|
||||
self.holder = holder
|
||||
return self
|
||||
}
|
||||
|
||||
func build() -> SkeletonLayer? {
|
||||
guard let type = skeletonType,
|
||||
let holder = holder
|
||||
else { return nil }
|
||||
|
||||
return SkeletonLayer(withType: type,
|
||||
usingColors: colors,
|
||||
andSkeletonHolder: holder)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
/// Object that facilitates the creation of skeleton layers for multiline
|
||||
/// elements, based on the builder pattern
|
||||
class SkeletonMultilineLayerBuilder {
|
||||
|
||||
var skeletonType: SkeletonType?
|
||||
var index: Int?
|
||||
var width: CGFloat?
|
||||
var cornerRadius: Int?
|
||||
|
||||
func setSkeletonType(_ type: SkeletonType) -> SkeletonMultilineLayerBuilder {
|
||||
self.skeletonType = type
|
||||
return self
|
||||
}
|
||||
|
||||
func setIndex(_ index: Int) -> SkeletonMultilineLayerBuilder {
|
||||
self.index = index
|
||||
return self
|
||||
}
|
||||
|
||||
func setWidth(_ width: CGFloat) -> SkeletonMultilineLayerBuilder {
|
||||
self.width = width
|
||||
return self
|
||||
}
|
||||
|
||||
func setCornerRadius(_ radius: Int) -> SkeletonMultilineLayerBuilder {
|
||||
self.cornerRadius = radius
|
||||
return self
|
||||
}
|
||||
|
||||
func build() -> CALayer? {
|
||||
guard let type = skeletonType,
|
||||
let index = index,
|
||||
let width = width,
|
||||
let radius = cornerRadius
|
||||
else { return nil }
|
||||
|
||||
let spaceRequiredForEachLine = SkeletonAppearance.default.multilineHeight + SkeletonAppearance.default.multilineSpacing
|
||||
let layer = type.layer
|
||||
layer.anchorPoint = .zero
|
||||
layer.name = CALayer.skeletonSubLayersName
|
||||
layer.frame = CGRect(x: 0.0, y: CGFloat(index) * spaceRequiredForEachLine, width: width, height: SkeletonAppearance.default.multilineHeight)
|
||||
|
||||
layer.cornerRadius = CGFloat(radius)
|
||||
layer.masksToBounds = true
|
||||
|
||||
return layer
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,6 @@ extension CollectionSkeleton where Self: UIScrollView {
|
||||
var estimatedNumberOfRows: Int { return 0 }
|
||||
func addDummyDataSource() {}
|
||||
func removeDummyDataSource(reloadAfter: Bool) {}
|
||||
func disableUserInteraction() { isUserInteractionEnabled = false }
|
||||
func enableUserInteraction() { isUserInteractionEnabled = true }
|
||||
func disableUserInteraction() { isScrollEnabled = false }
|
||||
func enableUserInteraction() { isScrollEnabled = true }
|
||||
}
|
||||
|
||||
@@ -16,17 +16,17 @@ extension UICollectionView: CollectionSkeleton {
|
||||
}
|
||||
|
||||
var skeletonDataSource: SkeletonCollectionDataSource? {
|
||||
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
|
||||
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
|
||||
set {
|
||||
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource, newValue, AssociationPolicy.retain.objc)
|
||||
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDataSource)
|
||||
self.dataSource = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var skeletonDelegate: SkeletonCollectionDelegate? {
|
||||
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
|
||||
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
|
||||
set {
|
||||
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate, newValue, AssociationPolicy.retain.objc)
|
||||
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDelegate)
|
||||
self.delegate = newValue
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ extension UICollectionView: CollectionSkeleton {
|
||||
!(originalDataSource is SkeletonCollectionDataSource)
|
||||
else { return }
|
||||
|
||||
let dataSource = SkeletonCollectionDataSource(collectionViewDataSource: originalDataSource, rowHeight: 0.0)
|
||||
let dataSource = SkeletonCollectionDataSource(collectionViewDataSource: originalDataSource)
|
||||
self.skeletonDataSource = dataSource
|
||||
reloadData()
|
||||
}
|
||||
@@ -52,3 +52,20 @@ extension UICollectionView: CollectionSkeleton {
|
||||
extension UICollectionView: GenericCollectionView {
|
||||
var scrollView: UIScrollView { return self }
|
||||
}
|
||||
|
||||
public extension UICollectionView {
|
||||
func prepareSkeleton(completion: @escaping (Bool) -> Void) {
|
||||
guard let originalDataSource = self.dataSource as? SkeletonCollectionViewDataSource,
|
||||
!(originalDataSource is SkeletonCollectionDataSource)
|
||||
else { return }
|
||||
|
||||
let dataSource = SkeletonCollectionDataSource(collectionViewDataSource: originalDataSource, rowHeight: 0.0)
|
||||
self.skeletonDataSource = dataSource
|
||||
performBatchUpdates({
|
||||
self.reloadData()
|
||||
}) { (done) in
|
||||
completion(done)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ extension SkeletonCollectionDataSource: UITableViewDataSource {
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cellIdentifier = originalTableViewDataSource?.collectionSkeletonView(tableView, cellIdenfierForRowAt: indexPath) ?? ""
|
||||
let cellIdentifier = originalTableViewDataSource?.collectionSkeletonView(tableView, cellIdentifierForRowAt: indexPath) ?? ""
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
|
||||
return cell
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import UIKit
|
||||
public protocol SkeletonTableViewDataSource: UITableViewDataSource {
|
||||
func numSections(in collectionSkeletonView: UITableView) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
}
|
||||
|
||||
public extension SkeletonTableViewDataSource {
|
||||
@@ -21,6 +21,13 @@ public extension SkeletonTableViewDataSource {
|
||||
}
|
||||
|
||||
func numSections(in collectionSkeletonView: UITableView) -> Int { return 1 }
|
||||
|
||||
/// Keeping the misspelled version around until it can be deprecated
|
||||
/// Right now, it just calls the new correctly spelled method and returns its result
|
||||
@available(*, deprecated, renamed: "collectionSkeletonView(_:cellIdentifierForRowAt:)")
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
|
||||
return collectionSkeletonView(skeletonView, cellIdentifierForRowAt: indexPath)
|
||||
}
|
||||
}
|
||||
|
||||
public protocol SkeletonTableViewDelegate: UITableViewDelegate {
|
||||
|
||||
@@ -15,17 +15,17 @@ extension UITableView: CollectionSkeleton {
|
||||
}
|
||||
|
||||
var skeletonDataSource: SkeletonCollectionDataSource? {
|
||||
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
|
||||
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
|
||||
set {
|
||||
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource, newValue, AssociationPolicy.retain.objc)
|
||||
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDataSource)
|
||||
self.dataSource = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var skeletonDelegate: SkeletonCollectionDelegate? {
|
||||
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
|
||||
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
|
||||
set {
|
||||
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate, newValue, AssociationPolicy.retain.objc)
|
||||
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDelegate)
|
||||
self.delegate = newValue
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,9 @@ extension UITableView: CollectionSkeleton {
|
||||
guard let originalDataSource = self.dataSource as? SkeletonTableViewDataSource,
|
||||
!(originalDataSource is SkeletonCollectionDataSource)
|
||||
else { return }
|
||||
let dataSource = SkeletonCollectionDataSource(tableViewDataSource: originalDataSource, rowHeight: calculateRowHeight())
|
||||
let rowHeight = calculateRowHeight()
|
||||
let dataSource = SkeletonCollectionDataSource(tableViewDataSource: originalDataSource,
|
||||
rowHeight: rowHeight)
|
||||
self.skeletonDataSource = dataSource
|
||||
reloadData()
|
||||
}
|
||||
@@ -53,11 +55,8 @@ extension UITableView: CollectionSkeleton {
|
||||
}
|
||||
|
||||
private func calculateRowHeight() -> CGFloat {
|
||||
guard rowHeight == UITableViewAutomaticDimension else { return rowHeight }
|
||||
guard rowHeight == UITableView.automaticDimension else { return rowHeight }
|
||||
rowHeight = estimatedRowHeight
|
||||
return estimatedRowHeight
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
enum SkeletonEnvironmentKey: String {
|
||||
case debugMode = "SKELETON_DEBUG"
|
||||
}
|
||||
|
||||
extension Dictionary {
|
||||
subscript (_ key: SkeletonEnvironmentKey) -> Value? {
|
||||
return self[key.rawValue as! Key]
|
||||
}
|
||||
}
|
||||
|
||||
func printSkeletonHierarchy(in view: UIView) {
|
||||
skeletonLog(view.skeletonHierarchy())
|
||||
}
|
||||
|
||||
func skeletonLog(_ message: String) {
|
||||
if let _ = ProcessInfo.processInfo.environment[.debugMode] {
|
||||
print(message)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
public var skeletonDescription: String {
|
||||
var description = "<\(type(of: self)): \(Unmanaged.passUnretained(self).toOpaque())"
|
||||
let subSkeletons = subviewsSkeletonables
|
||||
if subSkeletons.count != 0 {
|
||||
description += " | (\(subSkeletons.count)) subSkeletons"
|
||||
}
|
||||
if isSkeletonable {
|
||||
description += " | ☠️ "
|
||||
}
|
||||
return description + ">"
|
||||
}
|
||||
|
||||
public func skeletonHierarchy(index: Int = 0) -> String {
|
||||
var description = index == 0 ? "\n ⬇⬇ ☠️ Root view hierarchy with Skeletons ⬇⬇ \n" : ""
|
||||
description += "\(index == 0 ? "\n" : 3.whitespaces) \(skeletonDescription) \n"
|
||||
subviewsToSkeleton.forEach {
|
||||
description += (index + 2).whitespaces
|
||||
description += $0.skeletonHierarchy(index: index + 1)
|
||||
}
|
||||
return description
|
||||
}
|
||||
}
|
||||
@@ -10,18 +10,20 @@ import UIKit
|
||||
|
||||
extension CALayer {
|
||||
@objc func tint(withColors colors: [UIColor]) {
|
||||
recursiveSearch(inArray: skeletonSublayers,
|
||||
leafBlock: { backgroundColor = colors.first?.cgColor }) {
|
||||
$0.tint(withColors: colors)
|
||||
skeletonSublayers.recursiveSearch(leafBlock: {
|
||||
backgroundColor = colors.first?.cgColor
|
||||
}) {
|
||||
$0.tint(withColors: colors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CAGradientLayer {
|
||||
override func tint(withColors colors: [UIColor]) {
|
||||
recursiveSearch(inArray: skeletonSublayers,
|
||||
leafBlock: { self.colors = colors.map { $0.cgColor } }) {
|
||||
$0.tint(withColors: colors)
|
||||
skeletonSublayers.recursiveSearch(leafBlock: {
|
||||
self.colors = colors.map { $0.cgColor }
|
||||
}) {
|
||||
$0.tint(withColors: colors)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,22 +38,30 @@ extension CALayer {
|
||||
return sublayers?.filter { $0.name == CALayer.skeletonSubLayersName } ?? [CALayer]()
|
||||
}
|
||||
|
||||
func addMultilinesLayers(lines: Int, type: SkeletonType, lastLineFillPercent: Int) {
|
||||
func addMultilinesLayers(lines: Int, type: SkeletonType, lastLineFillPercent: Int, multilineCornerRadius: Int) {
|
||||
let numberOfSublayers = calculateNumLines(maxLines: lines)
|
||||
for index in 0..<numberOfSublayers {
|
||||
|
||||
let layerBuilder = SkeletonMultilineLayerBuilder()
|
||||
.setSkeletonType(type)
|
||||
.setCornerRadius(multilineCornerRadius)
|
||||
|
||||
(0..<numberOfSublayers).forEach { index in
|
||||
var width = bounds.width
|
||||
|
||||
if index == numberOfSublayers-1 && numberOfSublayers != 1 {
|
||||
width = width * CGFloat(lastLineFillPercent)/100;
|
||||
if index == numberOfSublayers - 1 && numberOfSublayers != 1 {
|
||||
width = width * CGFloat(lastLineFillPercent) / 100;
|
||||
}
|
||||
|
||||
if let layer = layerBuilder
|
||||
.setIndex(index)
|
||||
.setWidth(width)
|
||||
.build() {
|
||||
addSublayer(layer)
|
||||
}
|
||||
|
||||
let layer = SkeletonLayerFactory().makeMultilineLayer(withType: type, for: index, width: width)
|
||||
addSublayer(layer)
|
||||
}
|
||||
}
|
||||
|
||||
private func calculateNumLines(maxLines: Int) -> Int {
|
||||
let spaceRequitedForEachLine = SkeletonDefaultConfig.multilineHeight + SkeletonDefaultConfig.multilineSpacing
|
||||
let spaceRequitedForEachLine = SkeletonAppearance.default.multilineHeight + SkeletonAppearance.default.multilineSpacing
|
||||
var numberOfSublayers = Int(round(CGFloat(bounds.height)/CGFloat(spaceRequitedForEachLine)))
|
||||
if maxLines != 0, maxLines <= numberOfSublayers { numberOfSublayers = maxLines }
|
||||
return numberOfSublayers
|
||||
@@ -66,7 +76,7 @@ public extension CALayer {
|
||||
pulseAnimation.fromValue = backgroundColor
|
||||
pulseAnimation.toValue = UIColor(cgColor: backgroundColor!).complementaryColor.cgColor
|
||||
pulseAnimation.duration = 1
|
||||
pulseAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
|
||||
pulseAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
|
||||
pulseAnimation.autoreverses = true
|
||||
pulseAnimation.repeatCount = .infinity
|
||||
return pulseAnimation
|
||||
@@ -84,23 +94,25 @@ public extension CALayer {
|
||||
let animGroup = CAAnimationGroup()
|
||||
animGroup.animations = [startPointAnim, endPointAnim]
|
||||
animGroup.duration = 1.5
|
||||
animGroup.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
|
||||
animGroup.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
|
||||
animGroup.repeatCount = .infinity
|
||||
|
||||
return animGroup
|
||||
}
|
||||
|
||||
func playAnimation(_ anim: SkeletonLayerAnimation, key: String) {
|
||||
recursiveSearch(inArray: skeletonSublayers,
|
||||
leafBlock: { add(anim(self), forKey: key) }) {
|
||||
$0.playAnimation(anim, key: key)
|
||||
skeletonSublayers.recursiveSearch(leafBlock: {
|
||||
add(anim(self), forKey: key)
|
||||
}) {
|
||||
$0.playAnimation(anim, key: key)
|
||||
}
|
||||
}
|
||||
|
||||
func stopAnimation(forKey key: String) {
|
||||
recursiveSearch(inArray: skeletonSublayers,
|
||||
leafBlock: { removeAnimation(forKey: key) }) {
|
||||
$0.stopAnimation(forKey: key)
|
||||
skeletonSublayers.recursiveSearch(leafBlock: {
|
||||
removeAnimation(forKey: key)
|
||||
}) {
|
||||
$0.stopAnimation(forKey: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Int {
|
||||
|
||||
var whitespace: String {
|
||||
return whitespaces
|
||||
}
|
||||
|
||||
var whitespaces: String {
|
||||
return String(repeating: " ", count: self)
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,8 @@
|
||||
//
|
||||
// UIColor+Skeleton.swift
|
||||
// SkeletonView-iOS
|
||||
//
|
||||
// Created by Juanpe Catalán on 03/11/2017.
|
||||
// Copyright © 2017 SkeletonView. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
// codebeat:disable[TOO_MANY_IVARS]
|
||||
extension UIColor {
|
||||
|
||||
convenience init(_ hex: UInt) {
|
||||
@@ -20,9 +15,8 @@ extension UIColor {
|
||||
}
|
||||
|
||||
func isLight() -> Bool {
|
||||
// algorithm from: http://www.w3.org/WAI/ER/WD-AERT/#color-contrast
|
||||
guard let components = cgColor.components,
|
||||
components.count >= 3 else { return false }
|
||||
components.count >= 3 else { return false }
|
||||
let brightness = ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000
|
||||
return !(brightness < 0.5)
|
||||
}
|
||||
@@ -72,4 +66,4 @@ public extension UIColor {
|
||||
static var silver = UIColor(0xbdc3c7)
|
||||
static var asbestos = UIColor(0x7f8c8d)
|
||||
}
|
||||
|
||||
// codebeat:enable[TOO_MANY_IVARS]
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
// codebeat:disable[TOO_MANY_IVARS]
|
||||
enum ViewAssociatedKeys {
|
||||
static var skeletonable = "skeletonable"
|
||||
static var status = "status"
|
||||
static var skeletonLayer = "layer"
|
||||
static var flowDelegate = "flowDelegate"
|
||||
static var isSkeletonAnimated = "isSkeletonAnimated"
|
||||
static var viewState = "viewState"
|
||||
}
|
||||
// codebeat:enable[TOO_MANY_IVARS]
|
||||
|
||||
extension UIView {
|
||||
|
||||
enum Status {
|
||||
case on
|
||||
case off
|
||||
}
|
||||
|
||||
var flowDelegate: SkeletonFlowDelegate? {
|
||||
get { return ao_get(pkey: &ViewAssociatedKeys.flowDelegate) as? SkeletonFlowDelegate }
|
||||
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.flowDelegate) }
|
||||
}
|
||||
|
||||
var skeletonLayer: SkeletonLayer? {
|
||||
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonLayer) as? SkeletonLayer }
|
||||
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.skeletonLayer) }
|
||||
}
|
||||
|
||||
var status: Status! {
|
||||
get { return ao_get(pkey: &ViewAssociatedKeys.status) as? Status ?? .off }
|
||||
set { ao_set(newValue ?? .off, pkey: &ViewAssociatedKeys.status) }
|
||||
}
|
||||
|
||||
var skeletonIsAnimated: Bool! {
|
||||
get { return ao_get(pkey: &ViewAssociatedKeys.isSkeletonAnimated) as? Bool ?? false }
|
||||
set { ao_set(newValue ?? false, pkey: &ViewAssociatedKeys.isSkeletonAnimated) }
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,18 @@ import UIKit
|
||||
extension UIView {
|
||||
|
||||
var maxBoundsEstimated: CGRect {
|
||||
if let parentStackView = (superview as? UIStackView) {
|
||||
var origin: CGPoint = .zero
|
||||
switch parentStackView.alignment {
|
||||
case .center:
|
||||
origin.x = maxWidthEstimated / 2
|
||||
case .trailing:
|
||||
origin.x = maxWidthEstimated
|
||||
default:
|
||||
break
|
||||
}
|
||||
return CGRect(origin: origin, size: maxSizeEstimated)
|
||||
}
|
||||
return CGRect(origin: .zero, size: maxSizeEstimated)
|
||||
}
|
||||
|
||||
@@ -20,12 +32,12 @@ extension UIView {
|
||||
}
|
||||
|
||||
var maxWidthEstimated: CGFloat {
|
||||
let constraintsWidth = constraints.filter({ $0.firstAttribute == NSLayoutAttribute.width })
|
||||
let constraintsWidth = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutConstraint.Attribute.width })
|
||||
return max(between: frame.size.width, andContantsOf: constraintsWidth)
|
||||
}
|
||||
|
||||
var maxHeightEstimated: CGFloat {
|
||||
let constraintsHeight = constraints.filter({ $0.firstAttribute == NSLayoutAttribute.height })
|
||||
let constraintsHeight = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutConstraint.Attribute.height })
|
||||
return max(between: frame.size.height, andContantsOf: constraintsHeight)
|
||||
}
|
||||
|
||||
@@ -37,4 +49,8 @@ extension UIView {
|
||||
})
|
||||
return max
|
||||
}
|
||||
|
||||
var nonContentSizeLayoutConstraints: [NSLayoutConstraint] {
|
||||
return constraints.filter({ "\(type(of: $0))" != "NSContentSizeLayoutConstraint" })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,7 @@
|
||||
//
|
||||
// UIView+IBInspectable.swift
|
||||
// SkeletonView-iOS
|
||||
//
|
||||
// Created by Juanpe Catalán on 01/11/2017.
|
||||
// Copyright © 2017 SkeletonView. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
private enum AssociatedKeys {
|
||||
static var skeletonable = "skeletonable"
|
||||
static var status = "status"
|
||||
static var skeletonLayer = "layer"
|
||||
static var flowDelegate = "flowDelegate"
|
||||
static var isSkeletonAnimated = "isSkeletonAnimated"
|
||||
}
|
||||
|
||||
public extension UIView {
|
||||
|
||||
@IBInspectable
|
||||
@@ -27,38 +13,10 @@ public extension UIView {
|
||||
var isSkeletonActive: Bool {
|
||||
return status == .on || (subviewsSkeletonables.first(where: { $0.isSkeletonActive }) != nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
enum Status {
|
||||
case on
|
||||
case off
|
||||
}
|
||||
|
||||
var flowDelegate: SkeletonFlowDelegate? {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.flowDelegate) as? SkeletonFlowDelegate }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.flowDelegate, newValue, AssociationPolicy.retain.objc) }
|
||||
}
|
||||
|
||||
var skeletonLayer: SkeletonLayer? {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.skeletonLayer) as? SkeletonLayer }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.skeletonLayer, newValue, AssociationPolicy.retain.objc) }
|
||||
}
|
||||
|
||||
var status: Status! {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.status) as? Status ?? .off }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.status, newValue, AssociationPolicy.retain.objc) }
|
||||
}
|
||||
|
||||
var skeletonIsAnimated: Bool! {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.isSkeletonAnimated) as? Bool ?? false }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.isSkeletonAnimated, newValue, AssociationPolicy.retain.objc) }
|
||||
}
|
||||
|
||||
fileprivate var skeletonable: Bool! {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.skeletonable) as? Bool ?? false }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.skeletonable, newValue, AssociationPolicy.retain.objc) }
|
||||
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonable) as? Bool ?? false }
|
||||
set { ao_set(newValue ?? false, pkey: &ViewAssociatedKeys.skeletonable) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import UIKit
|
||||
extension UIView {
|
||||
|
||||
enum Constants {
|
||||
static let becomeActiveNotification = NSNotification.Name.UIApplicationDidBecomeActive
|
||||
static let enterForegroundNotification = NSNotification.Name.UIApplicationDidEnterBackground
|
||||
static let becomeActiveNotification = UIApplication.didBecomeActiveNotification
|
||||
static let enterForegroundNotification = UIApplication.didEnterBackgroundNotification
|
||||
static let needAnimatedSkeletonKey = "needAnimateSkeleton"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
//
|
||||
// AssociationPolicy.swift
|
||||
// SkeletonView-iOS
|
||||
//
|
||||
// Created by Juanpe Catalán on 02/11/2017.
|
||||
// Copyright © 2017 SkeletonView. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@@ -21,3 +15,32 @@ enum AssociationPolicy: UInt {
|
||||
return objc_AssociationPolicy(rawValue: rawValue)!
|
||||
}
|
||||
}
|
||||
|
||||
protocol AssociatedObjects: class { }
|
||||
|
||||
// transparent wrappers
|
||||
extension AssociatedObjects {
|
||||
|
||||
/// wrapper around `objc_getAssociatedObject`
|
||||
func ao_get(pkey: UnsafeRawPointer) -> Any? {
|
||||
return objc_getAssociatedObject(self, pkey)
|
||||
}
|
||||
|
||||
/// wrapper around `objc_setAssociatedObject`
|
||||
func ao_setOptional(_ value: Any?, pkey: UnsafeRawPointer, policy: AssociationPolicy = .retainNonatomic) {
|
||||
guard let value = value else { return }
|
||||
objc_setAssociatedObject(self, pkey, value, policy.objc)
|
||||
}
|
||||
|
||||
/// wrapper around `objc_setAssociatedObject`
|
||||
func ao_set(_ value: Any, pkey: UnsafeRawPointer, policy: AssociationPolicy = .retainNonatomic) {
|
||||
objc_setAssociatedObject(self, pkey, value, policy.objc)
|
||||
}
|
||||
|
||||
/// wrapper around 'objc_removeAssociatedObjects'
|
||||
func ao_removeAll() {
|
||||
objc_removeAssociatedObjects(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension NSObject: AssociatedObjects { }
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
//
|
||||
// ContainsMultilineText.swift
|
||||
// SkeletonView-iOS
|
||||
//
|
||||
// Created by Juanpe Catalán on 07/11/2017.
|
||||
// Copyright © 2017 SkeletonView. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
private enum AssociatedKeys {
|
||||
static var lastLineFillingPercent = "lastLineFillingPercent"
|
||||
}
|
||||
|
||||
protocol ContainsMultilineText {
|
||||
var numLines: Int { get }
|
||||
var lastLineFillingPercent: Int { get }
|
||||
}
|
||||
|
||||
extension ContainsMultilineText {
|
||||
var numLines: Int { return 0 }
|
||||
}
|
||||
|
||||
public extension UILabel {
|
||||
|
||||
@IBInspectable
|
||||
var lastLineFillPercent: Int {
|
||||
get { return lastLineFillingPercent }
|
||||
set { lastLineFillingPercent = min(newValue, 100) }
|
||||
}
|
||||
}
|
||||
|
||||
public extension UITextView {
|
||||
|
||||
@IBInspectable
|
||||
var lastLineFillPercent: Int {
|
||||
get { return lastLineFillingPercent }
|
||||
set { lastLineFillingPercent = min(newValue, 100) }
|
||||
}
|
||||
}
|
||||
|
||||
extension UILabel: ContainsMultilineText {
|
||||
var numLines: Int {
|
||||
return numberOfLines
|
||||
}
|
||||
|
||||
var lastLineFillingPercent: Int {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent) as? Int ?? SkeletonDefaultConfig.multilineLastLineFillPercent }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent, newValue, AssociationPolicy.retain.objc) }
|
||||
}
|
||||
}
|
||||
extension UITextView: ContainsMultilineText {
|
||||
|
||||
var lastLineFillingPercent: Int {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent) as? Int ?? SkeletonDefaultConfig.multilineLastLineFillPercent }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent, newValue, AssociationPolicy.retain.objc) }
|
||||
}
|
||||
}
|
||||
@@ -14,14 +14,14 @@ protocol PrepareForSkeleton {
|
||||
|
||||
extension UILabel: PrepareForSkeleton {
|
||||
func prepareViewForSkeleton() {
|
||||
text = nil
|
||||
text = " "
|
||||
resignFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
extension UITextView: PrepareForSkeleton {
|
||||
func prepareViewForSkeleton() {
|
||||
text = nil
|
||||
text = " "
|
||||
resignFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,28 @@
|
||||
//
|
||||
// HelperProtocols.swift
|
||||
// SkeletonView-iOS
|
||||
//
|
||||
// Created by Juanpe Catalán on 06/11/2017.
|
||||
// Copyright © 2017 SkeletonView. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
typealias VoidBlock = () -> Void
|
||||
typealias RecursiveBlock<T> = (T) -> Void
|
||||
|
||||
protocol IterableElement {}
|
||||
extension UIView: IterableElement {}
|
||||
extension CALayer: IterableElement {}
|
||||
|
||||
//MARK: Recursive
|
||||
protocol Recursive {
|
||||
associatedtype Element
|
||||
func recursiveSearch(inArray array:[Element], leafBlock: VoidBlock, recursiveBlock: RecursiveBlock<Element>)
|
||||
associatedtype Element: IterableElement
|
||||
func recursiveSearch(leafBlock: VoidBlock, recursiveBlock: RecursiveBlock<Element>)
|
||||
}
|
||||
|
||||
extension Recursive {
|
||||
func recursiveSearch(inArray array:[Element], leafBlock: VoidBlock, recursiveBlock: RecursiveBlock<Element>) {
|
||||
guard array.count > 0 else {
|
||||
extension Array: Recursive where Element: IterableElement {
|
||||
func recursiveSearch(leafBlock: VoidBlock, recursiveBlock: RecursiveBlock<Element>) {
|
||||
guard count > 0 else {
|
||||
leafBlock()
|
||||
return
|
||||
}
|
||||
array.forEach { recursiveBlock($0) }
|
||||
forEach { recursiveBlock($0) }
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView: Recursive {
|
||||
typealias Element = UIView
|
||||
}
|
||||
extension CALayer: Recursive {
|
||||
typealias Element = CALayer
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright © 2017 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
enum MultilineAssociatedKeys {
|
||||
static var lastLineFillingPercent = "lastLineFillingPercent"
|
||||
static var multilineCornerRadius = "multilineCornerRadius"
|
||||
}
|
||||
|
||||
protocol ContainsMultilineText {
|
||||
var numLines: Int { get }
|
||||
var lastLineFillingPercent: Int { get }
|
||||
var multilineCornerRadius: Int { get }
|
||||
}
|
||||
|
||||
extension ContainsMultilineText {
|
||||
var numLines: Int { return 0 }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UILabel {
|
||||
@IBInspectable
|
||||
var lastLineFillPercent: Int {
|
||||
get { return lastLineFillingPercent }
|
||||
set { lastLineFillingPercent = min(newValue, 100) }
|
||||
}
|
||||
@IBInspectable
|
||||
var linesCornerRadius: Int {
|
||||
get { return multilineCornerRadius }
|
||||
set { multilineCornerRadius = min(newValue, 10) }
|
||||
}
|
||||
}
|
||||
|
||||
extension UILabel: ContainsMultilineText {
|
||||
var numLines: Int {
|
||||
return numberOfLines
|
||||
}
|
||||
|
||||
var lastLineFillingPercent: Int {
|
||||
get { return ao_get(pkey: &MultilineAssociatedKeys.lastLineFillingPercent) as? Int ?? SkeletonAppearance.default.multilineLastLineFillPercent }
|
||||
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.lastLineFillingPercent) }
|
||||
}
|
||||
|
||||
var multilineCornerRadius: Int {
|
||||
get { return ao_get(pkey: &MultilineAssociatedKeys.multilineCornerRadius) as? Int ?? SkeletonAppearance.default.multilineCornerRadius }
|
||||
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.multilineCornerRadius) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UITextView {
|
||||
|
||||
@IBInspectable
|
||||
var lastLineFillPercent: Int {
|
||||
get { return lastLineFillingPercent }
|
||||
set { lastLineFillingPercent = min(newValue, 100) }
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var linesCornerRadius: Int {
|
||||
get { return multilineCornerRadius }
|
||||
set { multilineCornerRadius = min(newValue, 10) }
|
||||
}
|
||||
}
|
||||
|
||||
extension UITextView: ContainsMultilineText {
|
||||
var lastLineFillingPercent: Int {
|
||||
get {
|
||||
let defaultValue = SkeletonAppearance.default.multilineLastLineFillPercent
|
||||
return ao_get(pkey: &MultilineAssociatedKeys.lastLineFillingPercent) as? Int ?? defaultValue
|
||||
}
|
||||
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.lastLineFillingPercent) }
|
||||
}
|
||||
|
||||
var multilineCornerRadius: Int {
|
||||
get {
|
||||
let defaultValue = SkeletonAppearance.default.multilineCornerRadius
|
||||
return ao_get(pkey: &MultilineAssociatedKeys.multilineCornerRadius) as? Int ?? defaultValue
|
||||
}
|
||||
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.multilineCornerRadius) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// Recoverable.swift
|
||||
// SkeletonView
|
||||
//
|
||||
// Created by Juanpe Catalán on 13/05/2018.
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
protocol Recoverable {
|
||||
var viewState: RecoverableViewState? { get set }
|
||||
func saveViewState()
|
||||
func recoverViewState(forced: Bool)
|
||||
}
|
||||
|
||||
extension UIView: Recoverable {
|
||||
|
||||
var viewState: RecoverableViewState? {
|
||||
get { return ao_get(pkey: &ViewAssociatedKeys.viewState) as? RecoverableViewState }
|
||||
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.viewState) }
|
||||
}
|
||||
|
||||
@objc func saveViewState() {
|
||||
viewState = RecoverableViewState(view: self)
|
||||
}
|
||||
|
||||
@objc func recoverViewState(forced: Bool) {
|
||||
guard let safeViewState = viewState else { return }
|
||||
|
||||
layer.cornerRadius = safeViewState.cornerRadius
|
||||
layer.masksToBounds = safeViewState.clipToBounds
|
||||
|
||||
if safeViewState.backgroundColor != backgroundColor || forced {
|
||||
backgroundColor = safeViewState.backgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UILabel {
|
||||
override func saveViewState() {
|
||||
super.saveViewState()
|
||||
viewState?.text = text
|
||||
}
|
||||
|
||||
override func recoverViewState(forced: Bool) {
|
||||
super.recoverViewState(forced: forced)
|
||||
text = text == " " || forced ? viewState?.text : text
|
||||
}
|
||||
}
|
||||
|
||||
extension UITextView {
|
||||
override func saveViewState() {
|
||||
super.saveViewState()
|
||||
viewState?.text = text
|
||||
}
|
||||
|
||||
override func recoverViewState(forced: Bool) {
|
||||
super.recoverViewState(forced: forced)
|
||||
text = text == " " || forced ? viewState?.text : text
|
||||
}
|
||||
}
|
||||
|
||||
extension UIImageView {
|
||||
override func saveViewState() {
|
||||
super.saveViewState()
|
||||
viewState?.image = image
|
||||
}
|
||||
|
||||
override func recoverViewState(forced: Bool) {
|
||||
super.recoverViewState(forced: forced)
|
||||
image = image == nil || forced ? viewState?.image : image
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// RecoverableViewState.swift
|
||||
// SkeletonView
|
||||
//
|
||||
// Created by Juanpe Catalán on 13/05/2018.
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
struct RecoverableViewState {
|
||||
var backgroundColor: UIColor?
|
||||
var cornerRadius: CGFloat
|
||||
var clipToBounds: Bool
|
||||
|
||||
// UI text
|
||||
var text: String?
|
||||
|
||||
// UI image
|
||||
var image: UIImage?
|
||||
}
|
||||
|
||||
extension RecoverableViewState {
|
||||
init(view: UIView) {
|
||||
self.backgroundColor = view.backgroundColor
|
||||
self.clipToBounds = view.layer.masksToBounds
|
||||
self.cornerRadius = view.layer.cornerRadius
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,8 @@ public enum GradientDirection {
|
||||
public func slidingAnimation(duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation {
|
||||
return SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: self, duration: duration)
|
||||
}
|
||||
|
||||
|
||||
// codebeat:disable[ABC]
|
||||
var startPoint: GradientAnimationPoint {
|
||||
switch self {
|
||||
case .leftRight:
|
||||
@@ -55,6 +56,7 @@ public enum GradientDirection {
|
||||
return ( from: CGPoint(x:2, y:2), to: CGPoint(x:0, y:0))
|
||||
}
|
||||
}
|
||||
// codebeat:enable[ABC]
|
||||
}
|
||||
|
||||
public class SkeletonAnimationBuilder {
|
||||
@@ -76,7 +78,7 @@ public class SkeletonAnimationBuilder {
|
||||
let animGroup = CAAnimationGroup()
|
||||
animGroup.animations = [startPointAnim, endPointAnim]
|
||||
animGroup.duration = duration
|
||||
animGroup.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
|
||||
animGroup.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
|
||||
animGroup.repeatCount = .infinity
|
||||
|
||||
return animGroup
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
//
|
||||
// SkeletonDefaultConfig.swift
|
||||
// SkeletonView-iOS
|
||||
//
|
||||
// Created by Juanpe Catalán on 06/11/2017.
|
||||
// Copyright © 2017 SkeletonView. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public enum SkeletonDefaultConfig {
|
||||
|
||||
public static let tintColor = UIColor.clouds
|
||||
|
||||
public static let gradient = SkeletonGradient(baseColor: tintColor)
|
||||
|
||||
public static let multilineHeight: CGFloat = 15
|
||||
|
||||
public static let multilineSpacing: CGFloat = 10
|
||||
|
||||
public static let multilineLastLineFillPercent = 70
|
||||
}
|
||||
@@ -1,26 +1,29 @@
|
||||
//
|
||||
// SkeletonFlow.swift
|
||||
// SkeletonView-iOS
|
||||
//
|
||||
// Created by Juanpe Catalán on 08/02/2018.
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
protocol SkeletonFlowDelegate {
|
||||
func willBeginShowingSkeletons(withRootView rootView: UIView)
|
||||
func didShowSkeletons(withRootView rootView: UIView)
|
||||
func willBeginHidingSkeletons(withRootView rootView: UIView)
|
||||
func didHideSkeletons(withRootView rootView: UIView)
|
||||
}
|
||||
|
||||
class SkeletonFlowHandler: SkeletonFlowDelegate {
|
||||
|
||||
|
||||
func willBeginShowingSkeletons(withRootView rootView: UIView) {
|
||||
rootView.addAppNotificationsObservers()
|
||||
}
|
||||
|
||||
func didShowSkeletons(withRootView rootView: UIView) {
|
||||
printSkeletonHierarchy(in: rootView)
|
||||
}
|
||||
|
||||
func willBeginHidingSkeletons(withRootView rootView: UIView) {
|
||||
rootView.removeAppNoticationsObserver()
|
||||
}
|
||||
|
||||
func didHideSkeletons(withRootView rootView: UIView) {
|
||||
rootView.flowDelegate = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,22 +8,6 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SkeletonLayerFactory {
|
||||
|
||||
func makeSkeletonLayer(withType type: SkeletonType, usingColors colors: [UIColor], andHolder holder: UIView) -> SkeletonLayer {
|
||||
return SkeletonLayer(withType: type, usingColors: colors, andSkeletonHolder: holder)
|
||||
}
|
||||
|
||||
func makeMultilineLayer(withType type: SkeletonType, for index: Int, width: CGFloat) -> CALayer {
|
||||
let spaceRequiredForEachLine = SkeletonDefaultConfig.multilineHeight + SkeletonDefaultConfig.multilineSpacing
|
||||
let layer = type.layer
|
||||
layer.anchorPoint = .zero
|
||||
layer.name = CALayer.skeletonSubLayersName
|
||||
layer.frame = CGRect(x: 0.0, y: CGFloat(index) * spaceRequiredForEachLine, width: width, height: SkeletonDefaultConfig.multilineHeight)
|
||||
return layer
|
||||
}
|
||||
}
|
||||
|
||||
public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation
|
||||
|
||||
public enum SkeletonType {
|
||||
@@ -77,7 +61,7 @@ struct SkeletonLayer {
|
||||
|
||||
func addMultilinesIfNeeded() {
|
||||
guard let multiLineView = holder as? ContainsMultilineText else { return }
|
||||
maskLayer.addMultilinesLayers(lines: multiLineView.numLines, type: type, lastLineFillPercent: multiLineView.lastLineFillingPercent)
|
||||
maskLayer.addMultilinesLayers(lines: multiLineView.numLines, type: type, lastLineFillPercent: multiLineView.lastLineFillingPercent, multilineCornerRadius: multiLineView.multilineCornerRadius)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,49 +1,41 @@
|
||||
//
|
||||
// SkeletonView.swift
|
||||
// SkeletonView
|
||||
//
|
||||
// Created by Juanpe Catalán on 01/11/2017.
|
||||
// Copyright © 2017 SkeletonView. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIView {
|
||||
|
||||
func showSkeleton(usingColor color: UIColor = SkeletonDefaultConfig.tintColor) {
|
||||
func showSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor) {
|
||||
showSkeleton(withType: .solid, usingColors: [color])
|
||||
}
|
||||
|
||||
func showGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonDefaultConfig.gradient) {
|
||||
func showGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient) {
|
||||
showSkeleton(withType: .gradient, usingColors: gradient.colors)
|
||||
}
|
||||
|
||||
func showAnimatedSkeleton(usingColor color: UIColor = SkeletonDefaultConfig.tintColor, animation: SkeletonLayerAnimation? = nil) {
|
||||
func showAnimatedSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor, animation: SkeletonLayerAnimation? = nil) {
|
||||
showSkeleton(withType: .solid, usingColors: [color], animated: true, animation: animation)
|
||||
}
|
||||
|
||||
func showAnimatedGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonDefaultConfig.gradient, animation: SkeletonLayerAnimation? = nil) {
|
||||
func showAnimatedGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient, animation: SkeletonLayerAnimation? = nil) {
|
||||
showSkeleton(withType: .gradient, usingColors: gradient.colors, animated: true, animation: animation)
|
||||
}
|
||||
|
||||
func hideSkeleton(reloadDataAfter reload: Bool = true) {
|
||||
flowDelegate?.willBeginHidingSkeletons(withRootView: self)
|
||||
recursiveHideSkeleton(reloadDataAfter: reload)
|
||||
recursiveHideSkeleton(reloadDataAfter: reload, root: self)
|
||||
}
|
||||
|
||||
func startSkeletonAnimation(_ anim: SkeletonLayerAnimation? = nil) {
|
||||
skeletonIsAnimated = true
|
||||
recursiveSearch(inArray: subviewsSkeletonables,
|
||||
leafBlock: startSkeletonLayerAnimationBlock(anim)) {
|
||||
$0.startSkeletonAnimation(anim)
|
||||
}
|
||||
subviewsSkeletonables.recursiveSearch(leafBlock: startSkeletonLayerAnimationBlock(anim)) { subview in
|
||||
subview.startSkeletonAnimation(anim)
|
||||
}
|
||||
}
|
||||
|
||||
func stopSkeletonAnimation() {
|
||||
skeletonIsAnimated = false
|
||||
recursiveSearch(inArray: subviewsSkeletonables,
|
||||
leafBlock: stopSkeletonLayerAnimationBlock) {
|
||||
$0.stopSkeletonAnimation()
|
||||
subviewsSkeletonables.recursiveSearch(leafBlock: stopSkeletonLayerAnimationBlock) { subview in
|
||||
subview.stopSkeletonAnimation()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,31 +46,39 @@ extension UIView {
|
||||
skeletonIsAnimated = animated
|
||||
flowDelegate = SkeletonFlowHandler()
|
||||
flowDelegate?.willBeginShowingSkeletons(withRootView: self)
|
||||
recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation)
|
||||
recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation, root: self)
|
||||
}
|
||||
|
||||
fileprivate func recursiveShowSkeleton(withType type: SkeletonType = .solid, usingColors colors: [UIColor], animated: Bool = false, animation: SkeletonLayerAnimation? = nil) {
|
||||
fileprivate func recursiveShowSkeleton(withType type: SkeletonType, usingColors colors: [UIColor], animated: Bool, animation: SkeletonLayerAnimation?, root: UIView? = nil) {
|
||||
addDummyDataSourceIfNeeded()
|
||||
recursiveSearch(inArray: subviewsSkeletonables,
|
||||
leafBlock: {
|
||||
guard !isSkeletonActive else { return }
|
||||
isUserInteractionEnabled = false
|
||||
(self as? PrepareForSkeleton)?.prepareViewForSkeleton()
|
||||
addSkeletonLayer(withType: type, usingColors: colors, animated: animated, animation: animation)
|
||||
}) {
|
||||
$0.recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation)
|
||||
|
||||
subviewsSkeletonables.recursiveSearch(leafBlock: {
|
||||
guard !isSkeletonActive else { return }
|
||||
isUserInteractionEnabled = false
|
||||
saveViewState()
|
||||
(self as? PrepareForSkeleton)?.prepareViewForSkeleton()
|
||||
addSkeletonLayer(withType: type, usingColors: colors, animated: animated, animation: animation)
|
||||
}) { subview in
|
||||
subview.recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation)
|
||||
}
|
||||
|
||||
if let root = root {
|
||||
flowDelegate?.didShowSkeletons(withRootView: root)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func recursiveHideSkeleton(reloadDataAfter reload: Bool = true) {
|
||||
removeDummyDataSourceIfNeeded()
|
||||
fileprivate func recursiveHideSkeleton(reloadDataAfter reload: Bool, root: UIView? = nil) {
|
||||
removeDummyDataSourceIfNeeded(reloadAfter: reload)
|
||||
isUserInteractionEnabled = true
|
||||
recursiveSearch(inArray: subviewsSkeletonables,
|
||||
leafBlock: {
|
||||
removeSkeletonLayer()
|
||||
}, recursiveBlock: {
|
||||
$0.recursiveHideSkeleton(reloadDataAfter: reload)
|
||||
})
|
||||
subviewsSkeletonables.recursiveSearch(leafBlock: {
|
||||
recoverViewState(forced: false)
|
||||
removeSkeletonLayer()
|
||||
}) { subview in
|
||||
subview.recursiveHideSkeleton(reloadDataAfter: reload)
|
||||
}
|
||||
if let root = root {
|
||||
flowDelegate?.didHideSkeletons(withRootView: root)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func startSkeletonLayerAnimationBlock(_ anim: SkeletonLayerAnimation? = nil) -> VoidBlock {
|
||||
@@ -96,48 +96,19 @@ extension UIView {
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
@objc var subviewsSkeletonables: [UIView] {
|
||||
return subviews.filter { $0.isSkeletonable }
|
||||
}
|
||||
}
|
||||
|
||||
extension UITableView {
|
||||
override var subviewsSkeletonables: [UIView] {
|
||||
return visibleCells.filter { $0.isSkeletonable }
|
||||
}
|
||||
}
|
||||
|
||||
extension UITableViewCell {
|
||||
override var subviewsSkeletonables: [UIView] {
|
||||
return contentView.subviews.filter { $0.isSkeletonable }
|
||||
}
|
||||
}
|
||||
|
||||
extension UICollectionView {
|
||||
override var subviewsSkeletonables: [UIView] {
|
||||
return subviews.filter { $0.isSkeletonable }
|
||||
}
|
||||
}
|
||||
|
||||
extension UICollectionViewCell {
|
||||
override var subviewsSkeletonables: [UIView] {
|
||||
return contentView.subviews.filter { $0.isSkeletonable }
|
||||
}
|
||||
}
|
||||
|
||||
extension UIStackView {
|
||||
override var subviewsSkeletonables: [UIView] {
|
||||
return arrangedSubviews.filter { $0.isSkeletonable }
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
func addSkeletonLayer(withType type: SkeletonType, usingColors colors: [UIColor], gradientDirection direction: GradientDirection? = nil, animated: Bool, animation: SkeletonLayerAnimation? = nil) {
|
||||
self.skeletonLayer = SkeletonLayerFactory().makeSkeletonLayer(withType: type, usingColors: colors, andHolder: self)
|
||||
layer.insertSublayer(skeletonLayer!.contentLayer, at: UInt32.max)
|
||||
if animated { skeletonLayer!.start(animation) }
|
||||
guard let skeletonLayer = SkeletonLayerBuilder()
|
||||
.setSkeletonType(type)
|
||||
.addColors(colors)
|
||||
.setHolder(self)
|
||||
.build()
|
||||
else { return }
|
||||
|
||||
self.skeletonLayer = skeletonLayer
|
||||
layer.insertSublayer(skeletonLayer.contentLayer, at: UInt32.max)
|
||||
if animated { skeletonLayer.start(animation) }
|
||||
status = .on
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
@objc var subviewsSkeletonables: [UIView] {
|
||||
return subviewsToSkeleton.filter { $0.isSkeletonable }
|
||||
}
|
||||
|
||||
@objc var subviewsToSkeleton: [UIView] {
|
||||
return subviews
|
||||
}
|
||||
}
|
||||
|
||||
extension UITableView {
|
||||
|
||||
override var subviewsToSkeleton: [UIView] {
|
||||
return visibleCells
|
||||
}
|
||||
}
|
||||
|
||||
extension UITableViewCell {
|
||||
|
||||
override var subviewsToSkeleton: [UIView] {
|
||||
return contentView.subviews
|
||||
}
|
||||
}
|
||||
|
||||
extension UICollectionView {
|
||||
|
||||
override var subviewsToSkeleton: [UIView] {
|
||||
return subviews
|
||||
}
|
||||
}
|
||||
|
||||
extension UICollectionViewCell {
|
||||
|
||||
override var subviewsToSkeleton: [UIView] {
|
||||
return contentView.subviews
|
||||
}
|
||||
}
|
||||
|
||||
extension UIStackView {
|
||||
|
||||
override var subviewsToSkeleton: [UIView] {
|
||||
return arrangedSubviews
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
default_platform(:ios)
|
||||
podspec_name = "SkeletonView.podspec"
|
||||
|
||||
lane :release_current do
|
||||
version = version_get_podspec(path: @podspec_name)
|
||||
if git_tag_exists(tag: version)
|
||||
UI.user_error!("The tag #{version} already exists on the repo. To release a new version of the library bump the version on #{@podspec_name}")
|
||||
end
|
||||
pod_lib_lint
|
||||
add_git_tag(tag: "#{version}")
|
||||
push_git_tags
|
||||
pod_push
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
fastlane documentation
|
||||
================
|
||||
# Installation
|
||||
|
||||
Make sure you have the latest version of the Xcode command line tools installed:
|
||||
|
||||
```
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
Install _fastlane_ using
|
||||
```
|
||||
[sudo] gem install fastlane -NV
|
||||
```
|
||||
or alternatively using `brew cask install fastlane`
|
||||
|
||||
# Available Actions
|
||||
### release_current
|
||||
```
|
||||
fastlane release_current
|
||||
```
|
||||
|
||||
|
||||
----
|
||||
|
||||
This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
|
||||
More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
|
||||
The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
|
||||