Compare commits
119 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d665d01469 | |||
| 623b326e7b | |||
| 3a4bbadcbe | |||
| dc92e652cb | |||
| e3f38fdd25 | |||
| 6c1c4dda42 | |||
| 39c60b6da2 | |||
| c75d34e7de | |||
| c5a0bc0050 | |||
| be74f093f3 | |||
| 0f90edfc8e | |||
| 67daffd7c9 | |||
| 961c731888 | |||
| 39fa679ddc | |||
| 40c107af23 | |||
| 0781fceab7 | |||
| eef7a87eb6 | |||
| be25db821b | |||
| 9e014dab0e | |||
| f53b722b52 | |||
| bd02497124 | |||
| 1802e76da9 | |||
| 318b2546c6 | |||
| 37df94b09f | |||
| 81599f2980 | |||
| b28358663b | |||
| b364218213 | |||
| 18048c9bcd | |||
| 510fe34476 | |||
| 932579edc0 | |||
| 96673df005 | |||
| 43c1f2e5ac | |||
| 37f6205421 | |||
| 3471288307 | |||
| b77edf84b2 | |||
| 3e87293a84 | |||
| f637c20ea9 | |||
| 1737536057 | |||
| 31fe04b92e | |||
| f7d577c269 | |||
| 3a7be98057 | |||
| dab24e40a7 | |||
| c729dec667 | |||
| f6f0a11968 | |||
| 6784de9597 | |||
| 6e91b3fa71 | |||
| 4b6f55cf66 | |||
| beaf22d696 | |||
| c62586ccff | |||
| 55cb95e3fa | |||
| d72b28dc53 | |||
| b742179fec | |||
| f2b0329db9 | |||
| 8271110682 | |||
| 096099e2a0 | |||
| 4d4fda2177 | |||
| 4296d1cb04 | |||
| db19eae968 | |||
| 1d2256b4e6 | |||
| 4e5c8627dd | |||
| 6012b0c84b | |||
| 09e1fe31e0 | |||
| 4fb8020c83 | |||
| 303ae5aeb8 | |||
| 4649c7e61f | |||
| 1671af6d28 | |||
| 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 |
@@ -0,0 +1,9 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: skeletonview
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
custom: # Replace with a single custom sponsorship URL
|
||||
+5
-1
@@ -46,7 +46,7 @@ playground.xcworkspace
|
||||
# you should judge for yourself, the pros and cons are mentioned at:
|
||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||
#
|
||||
# Pods/
|
||||
Pods/
|
||||
|
||||
# Carthage
|
||||
#
|
||||
@@ -66,3 +66,7 @@ fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots
|
||||
fastlane/test_output
|
||||
|
||||
# JetBrains
|
||||
|
||||
.idea
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
4.1
|
||||
5.0
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
language: objective-c
|
||||
osx_image: xcode9.3
|
||||
osx_image: xcode10.2
|
||||
script:
|
||||
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-iOS -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=11'
|
||||
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-tvOS -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV,OS=11'
|
||||
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonViewExample -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=11'
|
||||
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-iOS -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=12'
|
||||
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonView-tvOS -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV,OS=12'
|
||||
- xcodebuild -project SkeletonView.xcodeproj -target SkeletonViewExample -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=12'
|
||||
after_success:
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 267 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
@@ -1,6 +1,21 @@
|
||||
# Change Log
|
||||
All notable changes to this project will be documented in this file
|
||||
|
||||
## Next version
|
||||
|
||||
### New
|
||||
- Adding swift news to mentioned section (thanks @osterbergmarcus).
|
||||
|
||||
## [Layout update (1.7)](https://github.com/Juanpe/SkeletonView/releases/tag/1.7)
|
||||
|
||||
### New
|
||||
|
||||
- Allow updating skeleton layout to recalculate skeleton bounds: `layoutSkeletonIfNeeded`. See the examples to know how to use it. (thanks @eduardbosch)
|
||||
|
||||
### Improvements
|
||||
|
||||
- Allow updating skeleton layers without recreating them: `updateSkeleton`, `updateGradientSkeleton`, `updateAnimatedSkeleton`, `updateAnimatedGradientSkeleton`. (thanks @eduardbosch)
|
||||
|
||||
## [Debug (1.4)](https://github.com/Juanpe/SkeletonView/releases/tag/1.4)
|
||||
|
||||
### New
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="irH-dz-xqL">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="irH-dz-xqL">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -13,13 +13,60 @@
|
||||
<!--View Controller-->
|
||||
<scene sceneID="qda-qV-vJk">
|
||||
<objects>
|
||||
<viewController id="irH-dz-xqL" customClass="ViewController" customModule="SkeletonView" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController id="irH-dz-xqL" customClass="ViewController" customModule="SkeletonViewExampleUICollectionView" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Fso-nq-n6t">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eHI-ka-8vS">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="243"/>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="obr-b6-dib">
|
||||
<rect key="frame" x="45" y="142" width="287" height="78"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="78" id="jx6-c1-U0j"/>
|
||||
</constraints>
|
||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="lastLineFillPercent">
|
||||
<integer key="value" value="40"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</textView>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="avatar" translatesAutoresizingMaskIntoConstraints="NO" id="Ql9-Jy-aWM">
|
||||
<rect key="frame" x="141" y="20" width="93" height="93"/>
|
||||
<color key="backgroundColor" red="0.56078431370000004" green="0.59607843140000005" blue="0.7843137255" alpha="0.90709546230000004" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="93" id="jlG-7K-wMd"/>
|
||||
<constraint firstAttribute="width" constant="93" id="xHX-Y1-dvi"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="243" id="0g6-3g-uII"/>
|
||||
<constraint firstAttribute="trailing" secondItem="obr-b6-dib" secondAttribute="trailing" constant="43" id="3ms-Wk-qcn"/>
|
||||
<constraint firstItem="obr-b6-dib" firstAttribute="centerX" secondItem="eHI-ka-8vS" secondAttribute="centerX" constant="1" id="B5s-DM-eR8"/>
|
||||
<constraint firstAttribute="height" constant="243" id="GX5-3W-tUt"/>
|
||||
<constraint firstItem="Ql9-Jy-aWM" firstAttribute="centerX" secondItem="eHI-ka-8vS" secondAttribute="centerX" id="HsA-ID-oSK"/>
|
||||
<constraint firstItem="Ql9-Jy-aWM" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="top" constant="20" id="Hxu-ae-hXQ"/>
|
||||
<constraint firstItem="obr-b6-dib" firstAttribute="leading" secondItem="eHI-ka-8vS" secondAttribute="leading" constant="45" id="eop-Gq-7mO"/>
|
||||
<constraint firstItem="obr-b6-dib" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="top" constant="142" id="inJ-75-hGX"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="36i-gK-pIa"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="HKL-L0-T2w">
|
||||
<rect key="frame" x="0.0" y="243" width="375" height="284"/>
|
||||
<rect key="frame" x="0.0" y="263" width="375" height="264"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="mGU-kn-rfE">
|
||||
<size key="itemSize" width="50" height="50"/>
|
||||
@@ -86,52 +133,6 @@
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="140" id="qR5-cz-YAm"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eHI-ka-8vS">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="243"/>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="obr-b6-dib">
|
||||
<rect key="frame" x="45" y="142" width="287" height="78"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="78" id="jx6-c1-U0j"/>
|
||||
</constraints>
|
||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="lastLineFillPercent">
|
||||
<integer key="value" value="40"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</textView>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="avatar" translatesAutoresizingMaskIntoConstraints="NO" id="Ql9-Jy-aWM">
|
||||
<rect key="frame" x="141" y="20" width="93" height="93"/>
|
||||
<color key="backgroundColor" red="0.56078431370000004" green="0.59607843140000005" blue="0.7843137255" alpha="0.90709546230000004" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="93" id="jlG-7K-wMd"/>
|
||||
<constraint firstAttribute="width" constant="93" id="xHX-Y1-dvi"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="243" id="0g6-3g-uII"/>
|
||||
<constraint firstAttribute="trailing" secondItem="obr-b6-dib" secondAttribute="trailing" constant="43" id="3ms-Wk-qcn"/>
|
||||
<constraint firstItem="obr-b6-dib" firstAttribute="centerX" secondItem="eHI-ka-8vS" secondAttribute="centerX" constant="1" id="B5s-DM-eR8"/>
|
||||
<constraint firstAttribute="height" constant="243" id="GX5-3W-tUt"/>
|
||||
<constraint firstItem="Ql9-Jy-aWM" firstAttribute="centerX" secondItem="eHI-ka-8vS" secondAttribute="centerX" id="HsA-ID-oSK"/>
|
||||
<constraint firstItem="Ql9-Jy-aWM" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="top" constant="20" id="Hxu-ae-hXQ"/>
|
||||
<constraint firstItem="obr-b6-dib" firstAttribute="leading" secondItem="eHI-ka-8vS" secondAttribute="leading" constant="45" id="eop-Gq-7mO"/>
|
||||
<constraint firstItem="obr-b6-dib" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="top" constant="142" id="inJ-75-hGX"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
@@ -141,10 +142,10 @@
|
||||
<constraint firstItem="JjA-MK-YzZ" firstAttribute="bottom" secondItem="2Gq-Y8-1TU" secondAttribute="bottom" id="AAr-ke-R7M"/>
|
||||
<constraint firstItem="JjA-MK-YzZ" firstAttribute="trailing" secondItem="2Gq-Y8-1TU" secondAttribute="trailing" id="DtS-9c-zBC"/>
|
||||
<constraint firstItem="HKL-L0-T2w" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="bottom" id="Jgf-jS-PLT"/>
|
||||
<constraint firstItem="eHI-ka-8vS" firstAttribute="top" secondItem="2Gq-Y8-1TU" secondAttribute="top" id="Ux2-GF-HLK"/>
|
||||
<constraint firstItem="JjA-MK-YzZ" firstAttribute="top" secondItem="HKL-L0-T2w" secondAttribute="bottom" id="XEd-Gf-KFI"/>
|
||||
<constraint firstItem="2Gq-Y8-1TU" firstAttribute="trailing" secondItem="HKL-L0-T2w" secondAttribute="trailing" id="bNo-98-pE4"/>
|
||||
<constraint firstItem="HKL-L0-T2w" firstAttribute="leading" secondItem="2Gq-Y8-1TU" secondAttribute="leading" id="iIq-cx-paX"/>
|
||||
<constraint firstItem="eHI-ka-8vS" firstAttribute="top" secondItem="Fso-nq-n6t" secondAttribute="top" id="tlZ-Wl-lvE"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="2Gq-Y8-1TU"/>
|
||||
</view>
|
||||
@@ -14,7 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
var window: UIWindow?
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
return true
|
||||
}
|
||||
|
Before Width: | Height: | Size: 933 B After Width: | Height: | Size: 933 B |
@@ -13,7 +13,7 @@ class ViewController: UIViewController {
|
||||
|
||||
@IBOutlet weak var tableview: UITableView! {
|
||||
didSet {
|
||||
tableview.rowHeight = UITableViewAutomaticDimension
|
||||
tableview.rowHeight = UITableView.automaticDimension
|
||||
tableview.estimatedRowHeight = 120.0
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,10 @@ class ViewController: UIViewController {
|
||||
super.viewDidLoad()
|
||||
tableview.isSkeletonable = true
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
view.layoutSkeletonIfNeeded()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
@@ -67,25 +71,24 @@ class ViewController: UIViewController {
|
||||
}
|
||||
|
||||
func refreshSkeleton() {
|
||||
self.view.hideSkeleton()
|
||||
if type == .gradient { showGradientSkeleton() }
|
||||
else { showSolidSkeleton() }
|
||||
if type == .gradient { showOrUpdateGradientSkeleton() }
|
||||
else { showOrUpdatepdateSolidSkeleton() }
|
||||
}
|
||||
|
||||
func showSolidSkeleton() {
|
||||
func showOrUpdatepdateSolidSkeleton() {
|
||||
if switchAnimated.isOn {
|
||||
view.showAnimatedSkeleton(usingColor: colorSelectedView.backgroundColor!)
|
||||
view.updateAnimatedSkeleton(usingColor: colorSelectedView.backgroundColor!)
|
||||
} else {
|
||||
view.showSkeleton(usingColor: colorSelectedView.backgroundColor!)
|
||||
view.updateSkeleton(usingColor: colorSelectedView.backgroundColor!)
|
||||
}
|
||||
}
|
||||
|
||||
func showGradientSkeleton() {
|
||||
func showOrUpdateGradientSkeleton() {
|
||||
let gradient = SkeletonGradient(baseColor: colorSelectedView.backgroundColor!)
|
||||
if switchAnimated.isOn {
|
||||
view.showAnimatedGradientSkeleton(usingGradient: gradient)
|
||||
view.updateAnimatedGradientSkeleton(usingGradient: gradient)
|
||||
} else {
|
||||
view.showGradientSkeleton(usingGradient: gradient)
|
||||
view.updateGradientSkeleton(usingGradient: gradient)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +136,7 @@ extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource {
|
||||
extension ViewController: SkeletonTableViewDataSource {
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return 9
|
||||
return 0
|
||||
}
|
||||
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
|
||||
@@ -0,0 +1,4 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "fastlane"
|
||||
gem 'cocoapods', '~> 1.7.0.beta.2'
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.0)
|
||||
activesupport (4.2.11.1)
|
||||
i18n (~> 0.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.6.0)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
atomos (0.1.3)
|
||||
babosa (1.0.2)
|
||||
claide (1.0.2)
|
||||
cocoapods (1.7.0.beta.3)
|
||||
activesupport (>= 4.0.2, < 5)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
cocoapods-core (= 1.7.0.beta.3)
|
||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||
cocoapods-downloader (>= 1.2.2, < 2.0)
|
||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||
cocoapods-search (>= 1.0.0, < 2.0)
|
||||
cocoapods-stats (>= 1.0.0, < 2.0)
|
||||
cocoapods-trunk (>= 1.3.1, < 2.0)
|
||||
cocoapods-try (>= 1.1.0, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
escape (~> 0.0.4)
|
||||
fourflusher (>= 2.2.0, < 3.0)
|
||||
gh_inspector (~> 1.0)
|
||||
molinillo (~> 0.6.6)
|
||||
nap (~> 1.0)
|
||||
ruby-macho (~> 1.4)
|
||||
xcodeproj (>= 1.8.2, < 2.0)
|
||||
cocoapods-core (1.7.0.beta.3)
|
||||
activesupport (>= 4.0.2, < 6)
|
||||
fuzzy_match (~> 2.0.4)
|
||||
nap (~> 1.0)
|
||||
cocoapods-deintegrate (1.0.4)
|
||||
cocoapods-downloader (1.2.2)
|
||||
cocoapods-plugins (1.0.0)
|
||||
nap
|
||||
cocoapods-search (1.0.0)
|
||||
cocoapods-stats (1.1.0)
|
||||
cocoapods-trunk (1.3.1)
|
||||
nap (>= 0.8, < 2.0)
|
||||
netrc (~> 0.11)
|
||||
cocoapods-try (1.1.0)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander-fastlane (4.4.6)
|
||||
highline (~> 1.7.2)
|
||||
concurrent-ruby (1.1.5)
|
||||
declarative (0.0.10)
|
||||
declarative-option (0.1.0)
|
||||
digest-crc (0.4.1)
|
||||
domain_name (0.5.20180417)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.2)
|
||||
emoji_regex (1.0.1)
|
||||
escape (0.0.4)
|
||||
excon (0.64.0)
|
||||
faraday (0.15.4)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-cookie_jar (0.0.6)
|
||||
faraday (>= 0.7.4)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday_middleware (0.13.1)
|
||||
faraday (>= 0.7.4, < 1.0)
|
||||
fastimage (2.1.5)
|
||||
fastlane (2.121.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
babosa (>= 1.0.2, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
colored
|
||||
commander-fastlane (>= 4.4.6, < 5.0.0)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (>= 0.1, < 2.0)
|
||||
excon (>= 0.45.0, < 1.0.0)
|
||||
faraday (~> 0.9)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 0.9)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-api-client (>= 0.21.2, < 0.24.0)
|
||||
google-cloud-storage (>= 1.15.0, < 2.0.0)
|
||||
highline (>= 1.7.2, < 2.0.0)
|
||||
json (< 3.0.0)
|
||||
mini_magick (~> 4.5.1)
|
||||
multi_json
|
||||
multi_xml (~> 0.5)
|
||||
multipart-post (~> 2.0.0)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
public_suffix (~> 2.0.0)
|
||||
rubyzip (>= 1.2.2, < 2.0.0)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
slack-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.8.1, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
fourflusher (2.2.0)
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
google-api-client (0.23.9)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.5, < 0.7.0)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
mime-types (~> 3.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
signet (~> 0.9)
|
||||
google-cloud-core (1.3.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-env (1.0.5)
|
||||
faraday (~> 0.11)
|
||||
google-cloud-storage (1.16.0)
|
||||
digest-crc (~> 0.4)
|
||||
google-api-client (~> 0.23)
|
||||
google-cloud-core (~> 1.2)
|
||||
googleauth (>= 0.6.2, < 0.10.0)
|
||||
googleauth (0.6.7)
|
||||
faraday (~> 0.12)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (~> 0.7)
|
||||
highline (1.7.10)
|
||||
http-cookie (1.0.3)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
json (2.2.0)
|
||||
jwt (2.1.0)
|
||||
memoist (0.16.0)
|
||||
mime-types (3.2.2)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2019.0331)
|
||||
mini_magick (4.5.1)
|
||||
minitest (5.11.3)
|
||||
molinillo (0.6.6)
|
||||
multi_json (1.13.1)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.2.6)
|
||||
nap (1.1.0)
|
||||
naturally (2.2.0)
|
||||
netrc (0.11.0)
|
||||
os (1.0.0)
|
||||
plist (3.5.0)
|
||||
public_suffix (2.0.5)
|
||||
representable (3.0.4)
|
||||
declarative (< 0.1.0)
|
||||
declarative-option (< 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rouge (2.0.7)
|
||||
ruby-macho (1.4.0)
|
||||
rubyzip (1.2.2)
|
||||
security (0.1.3)
|
||||
signet (0.11.0)
|
||||
addressable (~> 2.3)
|
||||
faraday (~> 0.9)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.5)
|
||||
CFPropertyList
|
||||
naturally
|
||||
slack-notifier (2.3.2)
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
thread_safe (0.3.6)
|
||||
tty-cursor (0.6.1)
|
||||
tty-screen (0.6.5)
|
||||
tty-spinner (0.9.0)
|
||||
tty-cursor (~> 0.6.0)
|
||||
tzinfo (1.2.5)
|
||||
thread_safe (~> 0.1)
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.6)
|
||||
unicode-display_width (1.5.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.8.2)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.2.6)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.0)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
cocoapods (~> 1.7.0.beta.2)
|
||||
fastlane
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.6
|
||||
@@ -0,0 +1,23 @@
|
||||
// swift-tools-version:5.0
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "SkeletonView",
|
||||
platforms: [
|
||||
.iOS(.v9),
|
||||
.tvOS(.v9)
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
name: "SkeletonView",
|
||||
targets: ["SkeletonView"])
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "SkeletonView",
|
||||
dependencies: [],
|
||||
path: "Sources")
|
||||
],
|
||||
swiftLanguageVersions: [.v5]
|
||||
)
|
||||
@@ -1,37 +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>
|
||||
<a href="https://instagram.github.io/IGListKit/">
|
||||
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
|
||||
<a href="https://github.com/Juanpe/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/p/SkeletonView.svg" alt="Platforms">
|
||||
</a>
|
||||
<img src="https://img.shields.io/badge/Swift-4.1-orange.svg" />
|
||||
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
|
||||
<a href="https://cocoapods.org/pods/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
|
||||
</a>
|
||||
<a href="https://github.com/Carthage/Carthage">
|
||||
<img src="https://img.shields.io/badge/carthage-compatible-4BC51D.svg?style=flat" alt="Carthage" />
|
||||
<img src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat" alt="Carthage" />
|
||||
</a>
|
||||
<a href="https://cocoapods.org/pods/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/dt/SkeletonView.svg?style=flat" alt="CocoaPods downloads" />
|
||||
<a href="https://github.com/apple/swift-package-manager">
|
||||
<img src="https://img.shields.io/badge/SPM-compatible-brightgreen.svg" alt="SPM" />
|
||||
</a>
|
||||
<a href="https://twitter.com/JuanpeCatalan">
|
||||
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
|
||||
</a>
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView¤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>
|
||||
<a href="https://twitter.com/JuanpeCatalan">
|
||||
<img src="https://img.shields.io/twitter/follow/JuanpeCatalan.svg?style=social&label=Follow" alt="Twitter" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
🌎 Translations: [ [🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) by [@WhatsXie](https://twitter.com/WhatsXie) ]
|
||||
🌎 Translations: </br>
|
||||
[🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) by [@WhatsXie](https://twitter.com/WhatsXie) </br>
|
||||
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) by [@brunomunizaf](https://twitter.com/brunomuniz_af) </br>
|
||||
[🇰🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_ko.md) by [@techinpark](https://twitter.com/techinpark)
|
||||
|
||||
Today almost all apps have async processes, such as Api requests, long running processes, etc. And while the processes are working, usually developers place a loading view to show users that something is going on.
|
||||
|
||||
@@ -40,11 +39,11 @@ Today almost all apps have async processes, such as Api requests, long running p
|
||||
Enjoy it! 🙂
|
||||
|
||||
* [Features](#-features)
|
||||
* [Requirements](#-supported-os--sdk-versions)
|
||||
* [Example Project](#-example)
|
||||
* [Guides](#-guides)
|
||||
* [Installation](#-installation)
|
||||
* [Cocoapods](#using-cocoapods)
|
||||
* [Carthage](#using-carthage)
|
||||
* [SPM](#using-swift-package-manager)
|
||||
* [How to use](#-how-to-use)
|
||||
* [Collections](#-collections)
|
||||
* [Multiline text](#-multiline-text)
|
||||
@@ -54,6 +53,7 @@ Enjoy it! 🙂
|
||||
* [Hierarchy](#-hierarchy)
|
||||
* [Debug](#-debug)
|
||||
* [Documentation](#-documentation)
|
||||
* [Supported OS & SDK Versions](#-supported-os--sdk-versions)
|
||||
* [Next steps](#-next-steps)
|
||||
* [Contributing](#-contributing)
|
||||
* [Mentions](#-mentions)
|
||||
@@ -71,15 +71,9 @@ Enjoy it! 🙂
|
||||
- [x] Simple Swift syntax
|
||||
- [x] Lightweight readable codebase
|
||||
|
||||
### 📋 Supported OS & SDK Versions
|
||||
## 🎬 Guides
|
||||
|
||||
* iOS 9.0+
|
||||
* tvOS 9.0+
|
||||
* Swift 4
|
||||
|
||||
### 🔮 Example
|
||||
|
||||
To run the example project, clone the repo and run `SkeletonViewExample` target.
|
||||
[<img src="Assets/thumb_getting_started.png">](https://youtu.be/75kgOhWsPNA)
|
||||
|
||||
## 📲 Installation
|
||||
|
||||
@@ -99,6 +93,18 @@ Edit your `Cartfile` and specify the dependency:
|
||||
github "Juanpe/SkeletonView"
|
||||
```
|
||||
|
||||
#### Using [Swift Package Manager](https://github.com/apple/swift-package-manager)
|
||||
|
||||
Once you have your Swift package set up, adding `SkeletonView` as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`.
|
||||
|
||||
```swift
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/Juanpe/SkeletonView.git", from: "1.7")
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 🐒 How to use
|
||||
|
||||
Only **3** steps needed to use `SkeletonView`:
|
||||
@@ -163,11 +169,36 @@ avatarImageView.isSkeletonable = true
|
||||
> **IMPORTANT!**
|
||||
>>```SkeletonView``` is recursive, so if you want show the skeleton in all skeletonable views, you only need to call the show method in the main container view. For example, with UIViewControllers
|
||||
|
||||
### Extra
|
||||
|
||||
#### Skeleton views layout
|
||||
|
||||
Sometimes skeleton layout may not fit your layout because the parent view bounds have changed. For example, rotating the device.
|
||||
|
||||
You can relayout the skeleton views like so:
|
||||
|
||||
```swift
|
||||
override func viewDidLayoutSubviews() {
|
||||
view.layoutSkeletonIfNeeded()
|
||||
}
|
||||
```
|
||||
|
||||
#### Update skeleton configuration
|
||||
|
||||
You can change the skeleton configuration at any time like its colour, animation, etc. with the following methods:
|
||||
|
||||
```swift
|
||||
(1) view.updateSkeleton() // Solid
|
||||
(2) view.updateGradientSkeleton() // Gradient
|
||||
(3) view.updateAnimatedSkeleton() // Solid animated
|
||||
(4) view.updateAnimatedGradientSkeleton() // Gradient animated
|
||||
```
|
||||
|
||||
### 🌿 Collections
|
||||
|
||||
Now, ```SkeletonView``` is compatible with ```UITableView``` and ```UICollectionView```.
|
||||
|
||||
###### UITableView
|
||||
#### UITableView
|
||||
|
||||
If you want to show the skeleton in a ```UITableView```, you need to conform to ```SkeletonTableViewDataSource``` protocol.
|
||||
|
||||
@@ -208,7 +239,15 @@ There is only one method you need to implement to let Skeleton know the cell ide
|
||||
> **IMPORTANT!**
|
||||
> If you are using resizable cells (`tableView.rowHeight = UITableViewAutomaticDimension` ), it's mandatory define the `estimatedRowHeight`.
|
||||
|
||||
###### UICollectionView
|
||||
👩🏼🏫 **How specify which elements are skeletonables?**
|
||||
|
||||
Here is an illustration that shows how you should specify which elements are skeletonables when you are using an `UITableView`:
|
||||
|
||||

|
||||
|
||||
As you can see, we have to make skeletonable the tableview, the cell and the UI elements, but we don't need to set as skeletonable the `contentView`
|
||||
|
||||
#### UICollectionView
|
||||
|
||||
For ```UICollectionView```, you need to conform to ```SkeletonCollectionViewDataSource``` protocol.
|
||||
|
||||
@@ -396,6 +435,12 @@ Then, when the skeleton appears, you can see the view hierarchy in the Xcode con
|
||||
### 📚 Documentation
|
||||
Coming soon...😅
|
||||
|
||||
### 📋 Supported OS & SDK Versions
|
||||
|
||||
* iOS 9.0+
|
||||
* tvOS 9.0+
|
||||
* Swift 4.2
|
||||
|
||||
## 📬 Next steps
|
||||
|
||||
* [x] Set the filling percent of the last line in multiline elements
|
||||
@@ -433,6 +478,7 @@ See [all contributors](https://github.com/Juanpe/SkeletonView/graphs/contributor
|
||||
- [Swift Weekly #96](http://digest.swiftweekly.com/issues/swift-weekly-issue-96-81759)
|
||||
- [CocoaControls](https://www.cocoacontrols.com/controls/skeletonview)
|
||||
- [Awesome iOS Newsletter #74](https://ios.libhunt.com/newsletter/74)
|
||||
- [Swift News #36](https://www.youtube.com/watch?v=mAGpsQiy6so)
|
||||
|
||||
|
||||
|
||||
|
||||
+501
@@ -0,0 +1,501 @@
|
||||

|
||||
|
||||
<p align="center">
|
||||
<a href="https://app.bitrise.io/app/6d289a17e22c8323">
|
||||
<img src="https://app.bitrise.io/app/6d289a17e22c8323/status.svg?token=fI7gKC41XD9-aRXDScCKBw&branch=master">
|
||||
</a>
|
||||
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
|
||||
<a href="https://github.com/Juanpe/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/p/SkeletonView.svg" alt="Platforms">
|
||||
</a>
|
||||
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
|
||||
<a href="https://cocoapods.org/pods/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
|
||||
</a>
|
||||
<a href="https://github.com/Carthage/Carthage">
|
||||
<img src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat" alt="Carthage" />
|
||||
</a>
|
||||
<a href="https://github.com/apple/swift-package-manager">
|
||||
<img src="https://img.shields.io/badge/SPM-compatible-brightgreen.svg" alt="SPM" />
|
||||
</a>
|
||||
<a href="https://twitter.com/JuanpeCatalan">
|
||||
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
|
||||
</a>
|
||||
<br/>
|
||||
<a href="https://twitter.com/intent/tweet?text=Wow%20This%20library%20is%20awesome:&url=https%3A%2F%2Fgithub.com%2FJuanpe%2FSkeletonView">
|
||||
<img src="https://img.shields.io/twitter/url/https/github.com/Juanpe/SkeletonView.svg?style=social" alt="License" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
🌎 번역에 도움을 주신분들: </br>
|
||||
[🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) by [@WhatsXie](https://twitter.com/WhatsXie) </br>
|
||||
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) by [@brunomunizaf](https://twitter.com/brunomuniz_af) </br>
|
||||
[🇰🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_ko.md) by [@techinpark](https://twitter.com/techinpark)
|
||||
|
||||
|
||||
오늘날 거의 대부분의 앱들은 비동기 방식의 API 호출을 사용하는 프로세스를 가지고 있습니다.
|
||||
프로세스가 작동하는동안 개발자들은 작업이 실행되고 있다는것을 사용자들에게 보여주기 위해서 로딩 뷰를 배치합니다.
|
||||
|
||||
```SkeletonView```는 이러한 필요에 의해 고안되었고, 사용자들에게 무엇인가 로딩이 되고 있다는것을 보여주면서 기다리는 콘텐츠에 대해서도 미리 준비할 수 있게 해주는 우아하게 표현할수 있는 방법입니다
|
||||
|
||||
맘껏 누리세요 🙂
|
||||
|
||||
* [기능](#-features)
|
||||
* [가이드](#-guides)
|
||||
* [설치방법](#-installation)
|
||||
* [Cocoapods](#using-cocoapods)
|
||||
* [Carthage](#using-carthage)
|
||||
* [SPM](#using-swift-package-manager)
|
||||
* [어떻게 사용하나요?](#-how-to-use)
|
||||
* [Collections](#-collections)
|
||||
* [Multiline text](#-multiline-text)
|
||||
* [Custom colors](#-custom-colors)
|
||||
* [Appearance](#-appearance)
|
||||
* [Custom animations](#-custom-animations)
|
||||
* [Hierarchy](#-hierarchy)
|
||||
* [Debug](#-debug)
|
||||
* [문서화](#-documentation)
|
||||
* [지원되는 OS와 SDK 버전](#-supported-os--sdk-versions)
|
||||
* [Next steps](#-next-steps)
|
||||
* [Contributing](#-contributing)
|
||||
* [Mentions](#-mentions)
|
||||
* [개발자](#-author)
|
||||
* [라이센스](#-license)
|
||||
|
||||
|
||||
## 🌟 기능
|
||||
|
||||
- [x] 사용이 쉽습니다
|
||||
- [x] 모든 `UIView`에서 사용가능합니다
|
||||
- [x] 전체 커스터마이징이 가능합니다
|
||||
- [x] 공통으로 이용가능합니다 (iPhone & iPad)
|
||||
- [x] `Interface Builder` 에서 사용 가능합니다.
|
||||
- [x] 간단한 스위프트 문법
|
||||
- [x] 가볍고 가독성 좋은 코드
|
||||
|
||||
## 🎬 사용가이드
|
||||
|
||||
[<img src="Assets/thumb_getting_started.png">](https://youtu.be/75kgOhWsPNA)
|
||||
|
||||
## 📲 설치 방법
|
||||
|
||||
#### [CocoaPods](https://cocoapods.org) 로 사용하기
|
||||
|
||||
당신의 프로젝트 `Podfile` 파일에 아래와 같이 입력합니다:
|
||||
|
||||
```ruby
|
||||
pod "SkeletonView"
|
||||
```
|
||||
|
||||
#### [Carthage](https://github.com/carthage)로 사용하기
|
||||
|
||||
당신의 프로젝트 `Cartfile` 파일에 아래와 같이 입력합니다:
|
||||
|
||||
```bash
|
||||
github "Juanpe/SkeletonView"
|
||||
```
|
||||
|
||||
#### [Swift Package Manager](https://github.com/apple/swift-package-manager)로 사용하기
|
||||
|
||||
|
||||
당신의 프로젝트에 Swift package를 설정한다면, `SkeletonView` 를 `Package.swift` 파일에 있는 `dependencies`에 추가하시면 됩니다.
|
||||
|
||||
|
||||
```swift
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/Juanpe/SkeletonView.git", from: "1.6")
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 🐒 어떻게 사용하나요?
|
||||
|
||||
`SkeletonView` 를 이용하기 위해서는 딱 **3** 단계만 기억하세요:
|
||||
|
||||
**1.** 사용하고자 하는 파일에서 `SkeletonView` 를 `Import` 합니다.
|
||||
```swift
|
||||
import SkeletonView
|
||||
```
|
||||
|
||||
**2.** 자, 그렇다면 UIView 속성에 `skeletonables` 를 이용하실 수 있습니다. 두가지 옵션이 있습니다
|
||||
|
||||
**코드로 사용하는 방법:**
|
||||
```swift
|
||||
avatarImageView.isSkeletonable = true
|
||||
```
|
||||
**인터페이스빌더 / 스토리보드를 이용하는 방법:**
|
||||
|
||||

|
||||
|
||||
**3.** 당신이 뷰를 세팅할때, **skeleton** 옵션을 사용 할 수 있습니다. 총 **4** 가지 옵션을 지원합니다:
|
||||
|
||||
```swift
|
||||
(1) view.showSkeleton() // Solid
|
||||
(2) view.showGradientSkeleton() // Gradient
|
||||
(3) view.showAnimatedSkeleton() // Solid animated
|
||||
(4) view.showAnimatedGradientSkeleton() // Gradient animated
|
||||
```
|
||||
|
||||
**미리보기**
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="25%">
|
||||
<center>Solid</center>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<center>Gradient</center>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<center>Solid Animated</center>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<center>Gradient Animated</center>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="25%">
|
||||
<img src="Assets/solid.png"></img>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<img src="Assets/gradient.png"></img>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<img src="Assets/solid_animated.gif"></img>
|
||||
</td>
|
||||
<td width="25%">
|
||||
<img src="Assets/gradient_animated.gif"></img>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
> **중요!**
|
||||
>>```SkeletonView``` 는 재귀적으로 되어있습니다, 만약 모든 뷰에 대해서 skeleton을 호출하고 싶다면, 메인 컨테이너 뷰에서 show `method`를 호출하여야 합니다. 예를 들자면 UIViewControllers가 있습니다.
|
||||
|
||||
|
||||
|
||||
### 🌿 Collections
|
||||
|
||||
현재, ```SkeletonView``` 는 ```UITableView``` 와 ```UICollectionView```에서 호환됩니다.
|
||||
|
||||
#### UITableView
|
||||
|
||||
만약 ```UITableView```에서 skeleton을 호출하고 싶다면, ```SkeletonTableViewDataSource``` protocol 을 구현하여야 합니다.
|
||||
|
||||
``` swift
|
||||
public protocol SkeletonTableViewDataSource: UITableViewDataSource {
|
||||
func numSections(in collectionSkeletonView: UITableView) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
}
|
||||
```
|
||||
|
||||
해당 프로토클은 보시다시피 ```UITableViewDataSource```를 상속받아 구현하였으므로, skeleton의 protocol과 대체 가능합니다.
|
||||
|
||||
프로토콜의 기본 구현은 다음과 같습니다:
|
||||
|
||||
``` swift
|
||||
func numSections(in collectionSkeletonView: UITableView) -> Int
|
||||
// Default: 1
|
||||
```
|
||||
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
|
||||
// Default:
|
||||
// 전체 테이블 뷰를 채우는데 필요한 셀 수를 계산합니다
|
||||
```
|
||||
|
||||
해당 메소드는 당신이 구현하여야할 cell identifier을 아는 경우에만 사용합니다, 해당 메소드는 기본으로 구현하지 않아도됩니다 :
|
||||
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
```
|
||||
|
||||
**Example**
|
||||
``` swift
|
||||
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
|
||||
return "CellIdentifier"
|
||||
}
|
||||
```
|
||||
|
||||
> **중요!**
|
||||
> 만약 사이즈가 변하는 셀을 사용한다면 (`tableView.rowHeight = UITableViewAutomaticDimension` ),`estimatedRowHeight`를 무조건 정의해주세요.
|
||||
|
||||
|
||||
👩🏼🏫 **어떻게 특정 요소에 skeleton 을 지정할까요?**
|
||||
|
||||
아래의 그림은 `UITableView` 에서 특정한 요소에 skeleton 을 지정하는 방법을 보여주는 이미지 입니다:
|
||||
|
||||

|
||||
|
||||
위의 이미지에서 보이듯, 테이블 뷰와 셀에 들어가는 UI 요소들에는 적용을 해야하지만, `contentView`에 skeleton을 적용할 필요는 없습니다.
|
||||
|
||||
#### UICollectionView
|
||||
|
||||
```UICollectionView``` 에 적용을 하기 위해서는, ```SkeletonCollectionViewDataSource``` protocol 을 구현할 필요가 있습니다.
|
||||
|
||||
``` swift
|
||||
public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
|
||||
func numSections(in collectionSkeletonView: UICollectionView) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
}
|
||||
```
|
||||
|
||||
```UITableView``` 와 사용방법은 같습니다.
|
||||
|
||||
### 📰 Multiline text
|
||||
|
||||
|
||||

|
||||
|
||||
텍스트가 들어있는 요소를 사용한다면, ```SkeletonView``` 에서 텍스트의 라인을 그려줍니다.
|
||||
그리고, 원하는 라인 수를 설정할 수 있습니다. 만약 ```numberOfLines``` 을 0으로 설정한다면, 자동으로 필요한 라인수를 계산해서 그려줍니다. 대신 값이 설정되어있다면 설정된 수만큼의 라인이 그려집니다.
|
||||
|
||||
##### 🎛 Customize
|
||||
|
||||
당신은 멀티라인을 위해 몇가지 옵션을 설정할 수 있습니다.
|
||||
|
||||
|
||||
| 속성 | 값 | 기본값 | 미리보기 |
|
||||
| ----------------------------------------------- | --------- | ----- | ---------------------------------- |
|
||||
| 마지막 라인의 **퍼센트** 를 지정 할 수 있습니다. | `0...100` | `70%` |  |
|
||||
| 라인의 **Corner radius** 를 지정할 수 있습니다. (**새로운기능**) | `0...10` | `0` |  |
|
||||
|
||||
|
||||
|
||||
라인의 radius를 지정하기 위해서는 **코드** 를 이용합니다, 아래 처럼 코드를 작성합니다:
|
||||
```swift
|
||||
descriptionTextView.lastLineFillPercent = 50
|
||||
descriptionTextView.linesCornerRadius = 5
|
||||
```
|
||||
|
||||
혹은 **IB/Storyboard** 를 이용하실 수 있습니다:
|
||||
|
||||

|
||||
|
||||
### 🎨 Custom colors
|
||||
|
||||
당신은 skeleton의 색상을 지정 할 수 있습니다. 간단하게 원하는 색상을 파라미터로 넘겨주시면 됩니다.
|
||||
|
||||
**단색 이용방법**
|
||||
``` swift
|
||||
view.showSkeleton(usingColor: UIColor.gray) // Solid
|
||||
// or
|
||||
view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0))
|
||||
```
|
||||
**그라디언트 이용 방법**
|
||||
``` swift
|
||||
let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
|
||||
view.showGradientSkeleton(usingGradient: gradient) // Gradient
|
||||
```
|
||||
|
||||
게다가, ```SkeletonView``` 에서는 20가지의 기본 컬러를 지원합니다 🤙🏼
|
||||
|
||||
```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...```
|
||||
|
||||

|
||||
###### 위 이미지는 [https://flatuicolors.com](https://flatuicolors.com) 사이트에서 발췌했습니다.
|
||||
|
||||
### 🦋 Appearance
|
||||
|
||||
**새로운 사항** skeleton 은 기본설정 값이 정해져 있습니다. 만약 커스텀 컬러를 사용할 필요가 없다면, `SkeletonView` 에 지정 되어있는 기본설정을 사용하시면 됩니다.
|
||||
|
||||
기본 설정값:
|
||||
- **tintColor**: UIColor
|
||||
- *기본값: .clouds*
|
||||
- **gradient**: SkeletonGradient
|
||||
- *기본값: SkeletonGradient(baseColor: .clouds)*
|
||||
- **multilineHeight**: CGFloat
|
||||
- *기본값: 15*
|
||||
- **multilineSpacing**: CGFloat
|
||||
- *기본값: 10*
|
||||
- **multilineLastLineFillPercent**: Int
|
||||
- *기본값: 70*
|
||||
- **multilineCornerRadius**: Int
|
||||
- *기본값: 0*
|
||||
|
||||
`SkeletonAppearance.default` 에는 사용 되어지는 기본 값들이 설정되어 있습니다 . 아래의 코드와 같이 사용할 수 있습니다:
|
||||
```Swift
|
||||
SkeletonAppearance.default.multilineHeight = 20
|
||||
SkeletonAppearance.default.tintColor = .green
|
||||
```
|
||||
|
||||
|
||||
### 🤓 커스텀 애니메이션
|
||||
|
||||
```SkeletonView``` 에는 두가지 애니메이션이 내장되어 있습니다, 단색 *바운스* 애니메이션과 그라디언트 *슬라이드* 애니메이션 입니다 .
|
||||
|
||||
게다가, 직접 애니메이션을 추가하고 싶다면 정말 간단합니다.
|
||||
|
||||
|
||||
Skeleton 에서는 `showAnimatedSkeleton` 함수를 ```SkeletonLayerAnimation```에 정의하여 맞춤형 애니메이션을 정의할 수 있도록 되어 있습니다.
|
||||
|
||||
```swift
|
||||
public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation
|
||||
```
|
||||
|
||||
함수는 이렇게 호출 가능합니다:
|
||||
|
||||
```swift
|
||||
view.showAnimatedSkeleton { (layer) -> CAAnimation in
|
||||
let animation = CAAnimation()
|
||||
// Customize here your animation
|
||||
|
||||
return animation
|
||||
}
|
||||
```
|
||||
|
||||
```SkeletonAnimationBuilder```의 사용이 가능합니다. ```SkeletonLayerAnimation```을 만들기 위해 사용됩니다.
|
||||
|
||||
이제, 그라디언트를 위한 **슬라이딩 애니메이션** 을 만들 수 있습니다, 애니메이션을 위한 **방향** 과 **지속시간** 을 설정 할 수 있습니다. (기본값 = 1.5초).
|
||||
|
||||
```swift
|
||||
// func makeSlidingAnimation(withDirection direction: GradientDirection, duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation
|
||||
|
||||
let animation = SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: .leftToRight)
|
||||
view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
|
||||
|
||||
```
|
||||
|
||||
```GradientDirection``` 는 enum 으로 정의 되어있습니다., 아래의 케이스를 참조하세요:
|
||||
|
||||
| 방향 | 미리보기 |
|
||||
| ------------------- | ---------------------------------------------- |
|
||||
| .leftRight |  |
|
||||
| .rightLeft |  |
|
||||
| .topBottom |  |
|
||||
| .bottomTop |  |
|
||||
| .topLeftBottomRight |  |
|
||||
| .bottomRightTopLeft |  |
|
||||
|
||||
> **😉 꿀팁!**
|
||||
슬라이딩 애니메이션을 만들기 위한 또다른 방법이 있습니다, 아래의 코드를 참조하세요:
|
||||
>>```let animation = GradientDirection.leftToRight.slidingAnimation()```
|
||||
|
||||
### 👨👧👦 계층 구조
|
||||
|
||||
```SkeletonView```는 재귀적입니다 , 그리고 우리는 skeleton이 효율적으로 작동하기를 원하기 때문에, 가능한 빨리 재귀작업을 중단하기를 원합니다. 이러한 이유때문에 반드시 컨테이너 뷰를 `Skeletonable` 로 설정해야 합니다, `skeletonable` 되지 않는 뷰를 만나는 순간 재귀 작업을 중단하기 떄문입니다.
|
||||
|
||||
아래의 이미지를 참고하세요 이미지는 한눈에 이해되실겁니다:
|
||||
|
||||
> ```ìsSkeletonable```= ☠️
|
||||
|
||||
| 설정값 | 결과 |
|
||||
| ----------------------------------------- | --------------------------------------------- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
|
||||
### 🔬 디버그
|
||||
|
||||
**새로운소식** 어떤것들이 잘 동작 하지 않을때를 위해 디버그 작업을 용이하게 하기 위해서 `SkeletonView` 에는 몇가지 새로운 것들이 있습니다.
|
||||
|
||||
첫번쨰로, `UIView` 에서 skeleton 정보를 보기위해 다음과 같이 지원하고 있습니다:
|
||||
```swift
|
||||
var skeletonDescription: String
|
||||
|
||||
```
|
||||
skeleton은 이렇게 생겼습니다:
|
||||
|
||||

|
||||
|
||||
그리고, 새로운 **디버그 모드**를 활성화 시킬 수 있습니다. 간단하게 `SKELETON_DEBUG` 이라는 환경 변수를 추가해 활성화 하면 됩니다.
|
||||
|
||||

|
||||
|
||||
그런 이후 skeleton이 나오면 Xcode 콘솔창에서 계층 구조를 볼 수 있습니다.
|
||||
|
||||
<details>
|
||||
<summary>예제를 확인해보세요. </summary>
|
||||
<img src="Assets/hierarchy_output.png" />
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
### 📚 문서화
|
||||
조금만 기다려주세요...😅
|
||||
|
||||
### 📋 지원 가능한 OS & SDK 버전
|
||||
|
||||
* iOS 9.0+
|
||||
* tvOS 9.0+
|
||||
* Swift 4.2
|
||||
|
||||
## 📬 예정된 기능들
|
||||
|
||||
* [x] 멀티라인 에서의 마지막 라인의 채우기 비율 설정
|
||||
* [x] 더많은 그라디언트 애니메이션
|
||||
* [x] resizable cells 지원
|
||||
* [x] CollectionView 호환
|
||||
* [x] tvOS 호환
|
||||
* [x] recovery state 추가
|
||||
* [x] Custom default appearance
|
||||
* [x] 디버그 모드
|
||||
* [ ] Custom collections 호환
|
||||
* [ ] skeletons 가 보이거나 가려질때 애니메이션 추가
|
||||
* [ ] MacOS 와 WatchOS 호환
|
||||
|
||||
## ❤️ 기여하기
|
||||
이 프로젝트는 오픈소스 프로젝트 입니다, 마음편하게 기여해주시면 됩니다 어떻게 하냐구요?
|
||||
- 새로운 [이슈](https://github.com/Juanpe/SkeletonView/issues/new)를 등록합니다.
|
||||
- [email](mailto://juanpecatalan.com)을 보냅니다.
|
||||
- 당신의 수정을 제안합니다, pull request를 포함한 수정을 권장합니다.
|
||||
|
||||
전체 [기여자목록](https://github.com/Juanpe/SkeletonView/graphs/contributors)
|
||||
|
||||
###### [SwiftPlate](https://github.com/JohnSundell/SwiftPlate)를 통해 프로젝트가 생성되었습니다
|
||||
|
||||
## 📢 소식들
|
||||
|
||||
- [iOS Dev Weekly #327](https://iosdevweekly.com/issues/327#start)
|
||||
- [Hacking with Swift Articles](https://www.hackingwithswift.com/articles/40/skeletonview-makes-loading-content-beautiful)
|
||||
- [Top 10 Swift Articles November](https://medium.mybridge.co/swift-top-10-articles-for-the-past-month-v-nov-2017-dfed7861cd65)
|
||||
- [30 Amazing iOS Swift Libraries (v2018)](https://medium.mybridge.co/30-amazing-ios-swift-libraries-for-the-past-year-v-2018-7cf15027eee9)
|
||||
- [AppCoda Weekly #44](http://digest.appcoda.com/issues/appcoda-weekly-issue-44-81899)
|
||||
- [iOS Cookies Newsletter #103](https://us11.campaign-archive.com/?u=cd1f3ed33c6527331d82107ba&id=48131a516d)
|
||||
- [Swift Developments Newsletter #113](https://andybargh.com/swiftdevelopments-113/)
|
||||
- [iOS Goodies #204](http://ios-goodies.com/post/167557280951/week-204)
|
||||
- [Swift Weekly #96](http://digest.swiftweekly.com/issues/swift-weekly-issue-96-81759)
|
||||
- [CocoaControls](https://www.cocoacontrols.com/controls/skeletonview)
|
||||
- [Awesome iOS Newsletter #74](https://ios.libhunt.com/newsletter/74)
|
||||
- [Swift News #36](https://www.youtube.com/watch?v=mAGpsQiy6so)
|
||||
|
||||
|
||||
|
||||
## 👨🏻💻 개발자
|
||||
[1.1]: http://i.imgur.com/tXSoThF.png
|
||||
[1]: http://www.twitter.com/JuanpeCatalan
|
||||
|
||||
* Juanpe Catalán [![alt text][1.1]][1]
|
||||
|
||||
<a class="bmc-button" target="_blank" href="https://www.buymeacoffee.com/CDou4xtIK"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy me a coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;"><span style="margin-left:5px"></span></a>
|
||||
|
||||
## 👮🏻 라이센스
|
||||
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Juanpe Catalán
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
Executable
+442
@@ -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.
|
||||
```
|
||||
+13
-14
@@ -1,19 +1,17 @@
|
||||

|
||||
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/Juanpe/SkeletonView">
|
||||
<img src="https://img.shields.io/travis/Juanpe/SkeletonView.svg">
|
||||
<a href="https://app.bitrise.io/app/6d289a17e22c8323">
|
||||
<img src="https://app.bitrise.io/app/6d289a17e22c8323/status.svg?token=fI7gKC41XD9-aRXDScCKBw&branch=master">
|
||||
</a>
|
||||
<a href="https://instagram.github.io/IGListKit/">
|
||||
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
|
||||
<a href="https://github.com/Juanpe/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/p/SkeletonView.svg" alt="Platforms">
|
||||
</a>
|
||||
<img src="https://img.shields.io/badge/Swift-4.1-orange.svg" />
|
||||
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
|
||||
<a href="https://cocoapods.org/pods/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
|
||||
</a>
|
||||
<a href="https://github.com/Carthage/Carthage">
|
||||
<img src="https://img.shields.io/badge/carthage-compatible-4BC51D.svg?style=flat" alt="Carthage" />
|
||||
</a>
|
||||
<a href="https://cocoapods.org/pods/SkeletonView">
|
||||
<img src="https://img.shields.io/cocoapods/dt/SkeletonView.svg?style=flat" alt="CocoaPods downloads" />
|
||||
</a>
|
||||
@@ -23,15 +21,16 @@
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView¤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>
|
||||
<a href="https://twitter.com/JuanpeCatalan">
|
||||
<img src="https://img.shields.io/twitter/follow/JuanpeCatalan.svg?style=social&label=Follow" alt="Twitter" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
🌎 翻译: [ [原版的](https://github.com/Juanpe/SkeletonView) ]
|
||||
|
||||
🌎 翻译: [ [原版的](https://github.com/Juanpe/SkeletonView) ] </br>
|
||||
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) [@brunomunizaf](https://twitter.com/brunomuniz_af)
|
||||
|
||||
|
||||
今天,几乎所有的应用程序都有异步流程,例如:Api请求、长时间运行的流程等。虽然流程正在运行,但通常开发人员会设置一个加载视图来向用户显示正在发生的事情。
|
||||
|
||||
@@ -71,9 +70,9 @@
|
||||
|
||||
### 📋 版本要求
|
||||
|
||||
* iOS 9.0+
|
||||
* tvOS 9.0+
|
||||
* Swift 4
|
||||
* iOS 10.0+
|
||||
* tvOS 10.0+
|
||||
* Swift 4.2
|
||||
|
||||
### 🔮 示例
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "SkeletonView"
|
||||
s.version = "1.4"
|
||||
s.version = "1.7"
|
||||
s.summary = "An elegant way to show users that something is happening and also prepare them to which contents he is waiting"
|
||||
s.description = <<-DESC
|
||||
Today almost all apps have async processes, as API requests, long runing processes, etc. And while the processes are working, usually developers place a loading view to show users that something is going on.
|
||||
@@ -12,6 +12,7 @@ Pod::Spec.new do |s|
|
||||
s.social_media_url = "https://twitter.com/JuanpeCatalan"
|
||||
s.ios.deployment_target = "9.0"
|
||||
s.tvos.deployment_target = "9.0"
|
||||
s.swift_version = "5.0"
|
||||
s.source = { :git => "https://github.com/Juanpe/SkeletonView.git", :tag => s.version.to_s }
|
||||
s.source_files = "Sources/**/*"
|
||||
end
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
17DD0E1D207FB32100C56334 /* ContainsMultilineText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */; };
|
||||
17DD0E1E207FB32100C56334 /* PrepareForSkeletonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */; };
|
||||
17DD0E1F207FB32100C56334 /* RecursiveProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */; };
|
||||
3B83EE4720C41488005178A4 /* ContainsMultilineText.swift in Headers */ = {isa = PBXBuildFile; fileRef = 3B83EE4520C41488005178A4 /* ContainsMultilineText.swift */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
3B83EE4820C41488005178A4 /* UIView+IBInspectable.swift in Headers */ = {isa = PBXBuildFile; fileRef = 3B83EE4620C41488005178A4 /* UIView+IBInspectable.swift */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
42ABD063210B548200BEEFF4 /* SkeletonView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* SkeletonView.framework */; };
|
||||
42ABD069210B548200BEEFF4 /* SkeletonView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* SkeletonView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
@@ -38,10 +37,21 @@
|
||||
42ABD07A210B54E200BEEFF4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD072210B54E100BEEFF4 /* Assets.xcassets */; };
|
||||
42ABD07B210B54E200BEEFF4 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD073210B54E100BEEFF4 /* ViewController.swift */; };
|
||||
42ABD07C210B54E200BEEFF4 /* Base.lproj in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD074210B54E100BEEFF4 /* Base.lproj */; };
|
||||
42ABD07E210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD076210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist */; };
|
||||
42ABD07F210B54E200BEEFF4 /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */; };
|
||||
870F4E4321CAC07300B9233B /* SkeletonConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 870F4E4221CAC07300B9233B /* SkeletonConfig.swift */; };
|
||||
870F4E4421CAC07300B9233B /* SkeletonConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 870F4E4221CAC07300B9233B /* SkeletonConfig.swift */; };
|
||||
872D5A5621C177E20037D763 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5521C177E20037D763 /* UIView+Extension.swift */; };
|
||||
872D5A5721C177E20037D763 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5521C177E20037D763 /* UIView+Extension.swift */; };
|
||||
872D5A5C21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */; };
|
||||
872D5A5D21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */; };
|
||||
872D5A5F21C24F8E0037D763 /* UITextView+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */; };
|
||||
872D5A6021C24F8E0037D763 /* UITextView+Multiline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */; };
|
||||
8748240D20C6A88A00E92179 /* UIView+IBInspectable.swift in Headers */ = {isa = PBXBuildFile; fileRef = F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
8748240E20C6A88E00E92179 /* ContainsMultilineText.swift in Headers */ = {isa = PBXBuildFile; fileRef = F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
877EFA4521BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */; };
|
||||
877EFA4621BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */; };
|
||||
877EFA4821BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */; };
|
||||
877EFA4921BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */; };
|
||||
8785E3A1211C9C9800CC9DFD /* SkeletonDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */; };
|
||||
8785E3A2211C9CA500CC9DFD /* SkeletonDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */; };
|
||||
8785E3A3211C9CE800CC9DFD /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88DEA97D1FCDBD1F006C80EF /* Constants.swift */; };
|
||||
@@ -138,7 +148,6 @@
|
||||
17DD0E00207FB27400C56334 /* SkeletonView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SkeletonView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
17DD0E1A207FB2C200C56334 /* SkeletonView-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SkeletonView-tvOS.plist"; sourceTree = "<group>"; };
|
||||
17DD0E1B207FB2C200C56334 /* SkeletonView-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SkeletonView-iOS.plist"; sourceTree = "<group>"; };
|
||||
3B83EE4520C41488005178A4 /* ContainsMultilineText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ContainsMultilineText.swift; path = Sources/Helpers/ContainsMultilineText.swift; sourceTree = "<group>"; };
|
||||
3B83EE4620C41488005178A4 /* UIView+IBInspectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "UIView+IBInspectable.swift"; path = "Sources/Extensions/UIView+IBInspectable.swift"; sourceTree = "<group>"; };
|
||||
42ABD06D210B548200BEEFF4 /* SkeletonViewExampleUICollectionView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SkeletonViewExampleUICollectionView.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
42ABD070210B54E100BEEFF4 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
@@ -149,6 +158,12 @@
|
||||
42ABD076210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SkeletonViewExampleCollectionview-Info.plist"; sourceTree = "<group>"; };
|
||||
42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
52D6D97C1BEFF229002C0205 /* SkeletonView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SkeletonView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
870F4E4221CAC07300B9233B /* SkeletonConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonConfig.swift; sourceTree = "<group>"; };
|
||||
872D5A5521C177E20037D763 /* UIView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = "<group>"; };
|
||||
872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Multiline.swift"; sourceTree = "<group>"; };
|
||||
872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+Multiline.swift"; sourceTree = "<group>"; };
|
||||
877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonLayerBuilder.swift; sourceTree = "<group>"; };
|
||||
877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonMultilineLayerBuilder.swift; sourceTree = "<group>"; };
|
||||
8785E3A0211C9C9800CC9DFD /* SkeletonDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonDebug.swift; sourceTree = "<group>"; };
|
||||
88DEA97D1FCDBD1F006C80EF /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
||||
8933C7841EB5B820000D00A4 /* SkeletonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkeletonView.swift; sourceTree = "<group>"; };
|
||||
@@ -224,25 +239,10 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
42ABD06F210B54BC00BEEFF4 /* Example UICollectionView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
42ABD070210B54E100BEEFF4 /* AppDelegate.swift */,
|
||||
42ABD072210B54E100BEEFF4 /* Assets.xcassets */,
|
||||
42ABD074210B54E100BEEFF4 /* Base.lproj */,
|
||||
42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */,
|
||||
42ABD071210B54E100BEEFF4 /* Main.storyboard */,
|
||||
42ABD073210B54E100BEEFF4 /* ViewController.swift */,
|
||||
);
|
||||
path = "Example UICollectionView";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
52D6D9721BEFF229002C0205 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3B83EE4520C41488005178A4 /* ContainsMultilineText.swift */,
|
||||
3B83EE4620C41488005178A4 /* UIView+IBInspectable.swift */,
|
||||
42ABD06F210B54BC00BEEFF4 /* Example UICollectionView */,
|
||||
F5F899F31FABA607002E8FDA /* Example */,
|
||||
8933C7811EB5B7E0000D00A4 /* Sources */,
|
||||
52D6D99C1BEFF38C002C0205 /* Configs */,
|
||||
@@ -271,6 +271,52 @@
|
||||
path = Configs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
872D5A5821C17D850037D763 /* CollectionView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
42ABD070210B54E100BEEFF4 /* AppDelegate.swift */,
|
||||
42ABD072210B54E100BEEFF4 /* Assets.xcassets */,
|
||||
42ABD074210B54E100BEEFF4 /* Base.lproj */,
|
||||
42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */,
|
||||
42ABD071210B54E100BEEFF4 /* Main.storyboard */,
|
||||
42ABD073210B54E100BEEFF4 /* ViewController.swift */,
|
||||
);
|
||||
path = CollectionView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
872D5A5921C17D980037D763 /* TableView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F5F899F41FABA607002E8FDA /* AppDelegate.swift */,
|
||||
88DEA97D1FCDBD1F006C80EF /* Constants.swift */,
|
||||
F5F899F61FABA607002E8FDA /* ViewController.swift */,
|
||||
F5F622441FACA338007C062A /* Cell.swift */,
|
||||
F5F899F81FABA607002E8FDA /* Main.storyboard */,
|
||||
F5F899FB1FABA607002E8FDA /* Assets.xcassets */,
|
||||
F5F899FD1FABA607002E8FDA /* LaunchScreen.storyboard */,
|
||||
);
|
||||
path = TableView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
872D5A5A21C24E7C0037D763 /* Multilines */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */,
|
||||
872D5A5B21C24EDD0037D763 /* UILabel+Multiline.swift */,
|
||||
872D5A5E21C24F8E0037D763 /* UITextView+Multiline.swift */,
|
||||
);
|
||||
path = Multilines;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
877EFA4321BEE9C40031FC00 /* Builders */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
877EFA4421BEE9D80031FC00 /* SkeletonLayerBuilder.swift */,
|
||||
877EFA4721BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift */,
|
||||
);
|
||||
path = Builders;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8785E39E211C9C6D00CC9DFD /* Appearance */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -290,6 +336,8 @@
|
||||
8933C7811EB5B7E0000D00A4 /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
872D5A5A21C24E7C0037D763 /* Multilines */,
|
||||
877EFA4321BEE9C40031FC00 /* Builders */,
|
||||
8785E39F211C9C7C00CC9DFD /* Debug */,
|
||||
8785E39E211C9C6D00CC9DFD /* Appearance */,
|
||||
F58A6E7020A8C87100612494 /* Recoverable */,
|
||||
@@ -302,6 +350,7 @@
|
||||
F5F899E81FAB9D2B002E8FDA /* SkeletonLayer.swift */,
|
||||
8933C7841EB5B820000D00A4 /* SkeletonView.swift */,
|
||||
F5D3FB0A209DCAA300003FCF /* SubviewsSkeletonables.swift */,
|
||||
870F4E4221CAC07300B9233B /* SkeletonConfig.swift */,
|
||||
);
|
||||
path = Sources;
|
||||
sourceTree = "<group>";
|
||||
@@ -363,6 +412,7 @@
|
||||
F5F622401FAC6E31007C062A /* UIColor+Skeleton.swift */,
|
||||
F5307E2D1FB0E5E400EE67C5 /* UIView+Frame.swift */,
|
||||
F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */,
|
||||
872D5A5521C177E20037D763 /* UIView+Extension.swift */,
|
||||
F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */,
|
||||
F5A5E8B3211DCE7000FD20D0 /* Int+Whitespaces.swift */,
|
||||
);
|
||||
@@ -373,7 +423,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F5F899D11FAB9630002E8FDA /* AssociationPolicy.swift */,
|
||||
F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */,
|
||||
F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */,
|
||||
F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */,
|
||||
);
|
||||
@@ -392,13 +441,8 @@
|
||||
F5F899F31FABA607002E8FDA /* Example */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F5F899F41FABA607002E8FDA /* AppDelegate.swift */,
|
||||
88DEA97D1FCDBD1F006C80EF /* Constants.swift */,
|
||||
F5F899F61FABA607002E8FDA /* ViewController.swift */,
|
||||
F5F622441FACA338007C062A /* Cell.swift */,
|
||||
F5F899F81FABA607002E8FDA /* Main.storyboard */,
|
||||
F5F899FB1FABA607002E8FDA /* Assets.xcassets */,
|
||||
F5F899FD1FABA607002E8FDA /* LaunchScreen.storyboard */,
|
||||
872D5A5921C17D980037D763 /* TableView */,
|
||||
872D5A5821C17D850037D763 /* CollectionView */,
|
||||
);
|
||||
path = Example;
|
||||
sourceTree = "<group>";
|
||||
@@ -435,7 +479,6 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3B83EE4720C41488005178A4 /* ContainsMultilineText.swift in Headers */,
|
||||
3B83EE4820C41488005178A4 /* UIView+IBInspectable.swift in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -524,7 +567,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0910;
|
||||
LastUpgradeCheck = 0930;
|
||||
LastUpgradeCheck = 1020;
|
||||
ORGANIZATIONNAME = SkeletonView;
|
||||
TargetAttributes = {
|
||||
17DD0DFF207FB27400C56334 = {
|
||||
@@ -532,24 +575,23 @@
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
42ABD05A210B548200BEEFF4 = {
|
||||
DevelopmentTeam = KWEMDK92F4;
|
||||
LastSwiftMigration = 0940;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
52D6D97B1BEFF229002C0205 = {
|
||||
CreatedOnToolsVersion = 7.1;
|
||||
LastSwiftMigration = 0800;
|
||||
LastSwiftMigration = 1000;
|
||||
};
|
||||
F5F899F11FABA607002E8FDA = {
|
||||
CreatedOnToolsVersion = 9.1;
|
||||
DevelopmentTeam = KWEMDK92F4;
|
||||
LastSwiftMigration = 1000;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "SkeletonView" */;
|
||||
compatibilityVersion = "Xcode 6.3";
|
||||
developmentRegion = English;
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
@@ -580,7 +622,6 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
42ABD07E210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist in Resources */,
|
||||
42ABD07A210B54E200BEEFF4 /* Assets.xcassets in Resources */,
|
||||
42ABD07C210B54E200BEEFF4 /* Base.lproj in Resources */,
|
||||
42ABD079210B54E200BEEFF4 /* Main.storyboard in Resources */,
|
||||
@@ -611,7 +652,9 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
872D5A5721C177E20037D763 /* UIView+Extension.swift in Sources */,
|
||||
17DD0E1F207FB32100C56334 /* RecursiveProtocol.swift in Sources */,
|
||||
872D5A5D21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */,
|
||||
17DD0E1E207FB32100C56334 /* PrepareForSkeletonProtocol.swift in Sources */,
|
||||
F51ED28520973CC9008B2434 /* SkeletonReusableCell.swift in Sources */,
|
||||
17DD0E17207FB28F00C56334 /* SkeletonLayer.swift in Sources */,
|
||||
@@ -621,6 +664,7 @@
|
||||
17DD0E0B207FB28900C56334 /* SkeletonTableViewProtocols.swift in Sources */,
|
||||
17DD0E0D207FB28900C56334 /* UITableView+CollectionSkeleton.swift in Sources */,
|
||||
17DD0E0E207FB28900C56334 /* UIView+CollectionSkeleton.swift in Sources */,
|
||||
877EFA4921BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */,
|
||||
F51ED28320973CBB008B2434 /* SkeletonCollectionDelegate.swift in Sources */,
|
||||
F58A6E6F20A8C66300612494 /* Recoverable.swift in Sources */,
|
||||
17DD0E18207FB28F00C56334 /* SkeletonView.swift in Sources */,
|
||||
@@ -633,11 +677,14 @@
|
||||
17DD0E14207FB28F00C56334 /* SkeletonAnimationBuilder.swift in Sources */,
|
||||
17DD0E11207FB28C00C56334 /* UIView+Frame.swift in Sources */,
|
||||
17DD0E15207FB28F00C56334 /* SkeletonAppearance.swift in Sources */,
|
||||
872D5A6021C24F8E0037D763 /* UITextView+Multiline.swift in Sources */,
|
||||
17DD0E10207FB28C00C56334 /* UIColor+Skeleton.swift in Sources */,
|
||||
F51ED28420973CC6008B2434 /* GenericCollectionView.swift in Sources */,
|
||||
17DD0E0A207FB28900C56334 /* SkeletonCollectionViewProtocols.swift in Sources */,
|
||||
17DD0E1C207FB32100C56334 /* AssociationPolicy.swift in Sources */,
|
||||
877EFA4621BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */,
|
||||
F5A5E8B5211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */,
|
||||
870F4E4421CAC07300B9233B /* SkeletonConfig.swift in Sources */,
|
||||
17DD0E1D207FB32100C56334 /* ContainsMultilineText.swift in Sources */,
|
||||
17DD0E0F207FB28C00C56334 /* CALayer+Extensions.swift in Sources */,
|
||||
17DD0E16207FB28F00C56334 /* SkeletonGradient.swift in Sources */,
|
||||
@@ -659,7 +706,9 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
872D5A5621C177E20037D763 /* UIView+Extension.swift in Sources */,
|
||||
F5F899D01FAA6A4D002E8FDA /* UIView+IBInspectable.swift in Sources */,
|
||||
872D5A5C21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */,
|
||||
F54CF5E42024CEB000330B0D /* UIView+CollectionSkeleton.swift in Sources */,
|
||||
F5307E371FB1076E00EE67C5 /* SkeletonTableViewProtocols.swift in Sources */,
|
||||
8933C7851EB5B820000D00A4 /* SkeletonView.swift in Sources */,
|
||||
@@ -669,6 +718,7 @@
|
||||
F54CF5E52024CEB000330B0D /* UITableView+CollectionSkeleton.swift in Sources */,
|
||||
F5307E321FB0F42F00EE67C5 /* RecursiveProtocol.swift in Sources */,
|
||||
F5F622431FAC81FD007C062A /* CALayer+Extensions.swift in Sources */,
|
||||
877EFA4821BEED760031FC00 /* SkeletonMultilineLayerBuilder.swift in Sources */,
|
||||
F5F899ED1FAB9F04002E8FDA /* CollectionSkeletonProtocol.swift in Sources */,
|
||||
F58A6E6E20A8C66300612494 /* Recoverable.swift in Sources */,
|
||||
F5307E2C1FAF6BC900EE67C5 /* SkeletonGradient.swift in Sources */,
|
||||
@@ -681,11 +731,14 @@
|
||||
F54CF5E62024CEB000330B0D /* UICollectionView+CollectionSkeleton.swift in Sources */,
|
||||
F56B94461FAE20AF0095662F /* PrepareForSkeletonProtocol.swift in Sources */,
|
||||
F5307E391FB1078E00EE67C5 /* SkeletonCollectionViewProtocols.swift in Sources */,
|
||||
872D5A5F21C24F8E0037D763 /* UITextView+Multiline.swift in Sources */,
|
||||
F51DF873206E91FB00D23301 /* GenericCollectionView.swift in Sources */,
|
||||
F5307E3B1FB123C100EE67C5 /* ContainsMultilineText.swift in Sources */,
|
||||
F5F899E91FAB9D2B002E8FDA /* SkeletonLayer.swift in Sources */,
|
||||
F5307E2E1FB0E5E400EE67C5 /* UIView+Frame.swift in Sources */,
|
||||
877EFA4521BEE9D80031FC00 /* SkeletonLayerBuilder.swift in Sources */,
|
||||
F5A5E8B4211DCE7000FD20D0 /* Int+Whitespaces.swift in Sources */,
|
||||
870F4E4321CAC07300B9233B /* SkeletonConfig.swift in Sources */,
|
||||
F51DF871206E91B300D23301 /* SkeletonReusableCell.swift in Sources */,
|
||||
F51DF879206E9F5500D23301 /* SkeletonCollectionDelegate.swift in Sources */,
|
||||
F51DE1091FBF70A70037919A /* SkeletonAnimationBuilder.swift in Sources */,
|
||||
@@ -764,7 +817,7 @@
|
||||
SDKROOT = appletvos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 3;
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
};
|
||||
@@ -796,7 +849,7 @@
|
||||
SDKROOT = appletvos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 3;
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
};
|
||||
@@ -815,16 +868,16 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = KWEMDK92F4;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = "SkeletonViewExample copy-Info.plist";
|
||||
INFOPLIST_FILE = "Configs/SkeletonViewExampleCollectionview-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -842,15 +895,15 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = KWEMDK92F4;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = "SkeletonViewExample copy-Info.plist";
|
||||
INFOPLIST_FILE = "Configs/SkeletonViewExampleCollectionview-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
@@ -859,6 +912,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
@@ -907,7 +961,7 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@@ -918,6 +972,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
@@ -958,7 +1013,7 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
@@ -987,7 +1042,7 @@
|
||||
PRODUCT_NAME = SkeletonView;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -1011,7 +1066,7 @@
|
||||
PRODUCT_NAME = SkeletonView;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -1027,7 +1082,7 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = KWEMDK92F4;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/Configs/SkeletonViewExampleInfo.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
@@ -1035,7 +1090,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -1052,7 +1107,7 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = KWEMDK92F4;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/Configs/SkeletonViewExampleInfo.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
@@ -1060,7 +1115,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.juanpecatalan.SkeletonViewExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0930"
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -29,6 +29,15 @@
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "52D6D97B1BEFF229002C0205"
|
||||
BuildableName = "SkeletonView.framework"
|
||||
BlueprintName = "SkeletonView-iOS"
|
||||
ReferencedContainer = "container:SkeletonView.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0930"
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F5F899F11FABA607002E8FDA"
|
||||
BuildableName = "SkeletonViewExample.app"
|
||||
BlueprintName = "SkeletonViewExample"
|
||||
ReferencedContainer = "container:SkeletonView.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F5F899F11FABA607002E8FDA"
|
||||
BuildableName = "SkeletonViewExample.app"
|
||||
BlueprintName = "SkeletonViewExample"
|
||||
ReferencedContainer = "container:SkeletonView.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F5F899F11FABA607002E8FDA"
|
||||
BuildableName = "SkeletonViewExample.app"
|
||||
BlueprintName = "SkeletonViewExample"
|
||||
ReferencedContainer = "container:SkeletonView.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F5F899F11FABA607002E8FDA"
|
||||
BuildableName = "SkeletonViewExample.app"
|
||||
BlueprintName = "SkeletonViewExample"
|
||||
ReferencedContainer = "container:SkeletonView.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "42ABD05A210B548200BEEFF4"
|
||||
BuildableName = "SkeletonViewExampleUICollectionView.app"
|
||||
BlueprintName = "SkeletonViewExampleUICollectionView"
|
||||
ReferencedContainer = "container:SkeletonView.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "42ABD05A210B548200BEEFF4"
|
||||
BuildableName = "SkeletonViewExampleUICollectionView.app"
|
||||
BlueprintName = "SkeletonViewExampleUICollectionView"
|
||||
ReferencedContainer = "container:SkeletonView.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "42ABD05A210B548200BEEFF4"
|
||||
BuildableName = "SkeletonViewExampleUICollectionView.app"
|
||||
BlueprintName = "SkeletonViewExampleUICollectionView"
|
||||
ReferencedContainer = "container:SkeletonView.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "42ABD05A210B548200BEEFF4"
|
||||
BuildableName = "SkeletonViewExampleUICollectionView.app"
|
||||
BlueprintName = "SkeletonViewExampleUICollectionView"
|
||||
ReferencedContainer = "container:SkeletonView.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -15,6 +15,7 @@ public enum SkeletonAppearance {
|
||||
public static var `default`: Appearance = SkeletonViewAppearance.shared
|
||||
}
|
||||
|
||||
// codebeat:disable[TOO_MANY_IVARS]
|
||||
class SkeletonViewAppearance: Appearance {
|
||||
|
||||
static var shared = SkeletonViewAppearance()
|
||||
@@ -31,3 +32,4 @@ class SkeletonViewAppearance: Appearance {
|
||||
|
||||
var multilineCornerRadius: Int = 0
|
||||
}
|
||||
// codebeat:enable[TOO_MANY_IVARS]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
/// Object that facilitates the creation of skeleton layers,
|
||||
/// based on the builder pattern
|
||||
class SkeletonLayerBuilder {
|
||||
|
||||
var skeletonType: SkeletonType?
|
||||
var colors: [UIColor] = []
|
||||
var holder: UIView?
|
||||
|
||||
func setSkeletonType(_ type: SkeletonType) -> SkeletonLayerBuilder {
|
||||
self.skeletonType = type
|
||||
return self
|
||||
}
|
||||
|
||||
func addColor(_ color: UIColor) -> SkeletonLayerBuilder {
|
||||
return addColors([color])
|
||||
}
|
||||
|
||||
func addColors(_ colors: [UIColor]) -> SkeletonLayerBuilder {
|
||||
self.colors.append(contentsOf: colors)
|
||||
return self
|
||||
}
|
||||
|
||||
func setHolder(_ holder: UIView) -> SkeletonLayerBuilder {
|
||||
self.holder = holder
|
||||
return self
|
||||
}
|
||||
|
||||
func build() -> SkeletonLayer? {
|
||||
guard let type = skeletonType,
|
||||
let holder = holder
|
||||
else { return nil }
|
||||
|
||||
return SkeletonLayer(withType: type,
|
||||
usingColors: colors,
|
||||
andSkeletonHolder: holder)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
/// Object that facilitates the creation of skeleton layers for multiline
|
||||
/// elements, based on the builder pattern
|
||||
class SkeletonMultilineLayerBuilder {
|
||||
|
||||
var skeletonType: SkeletonType?
|
||||
var index: Int?
|
||||
var width: CGFloat?
|
||||
var cornerRadius: Int?
|
||||
|
||||
func setSkeletonType(_ type: SkeletonType) -> SkeletonMultilineLayerBuilder {
|
||||
self.skeletonType = type
|
||||
return self
|
||||
}
|
||||
|
||||
func setIndex(_ index: Int) -> SkeletonMultilineLayerBuilder {
|
||||
self.index = index
|
||||
return self
|
||||
}
|
||||
|
||||
func setWidth(_ width: CGFloat) -> SkeletonMultilineLayerBuilder {
|
||||
self.width = width
|
||||
return self
|
||||
}
|
||||
|
||||
func setCornerRadius(_ radius: Int) -> SkeletonMultilineLayerBuilder {
|
||||
self.cornerRadius = radius
|
||||
return self
|
||||
}
|
||||
|
||||
func build() -> CALayer? {
|
||||
guard let type = skeletonType,
|
||||
let index = index,
|
||||
let width = width,
|
||||
let radius = cornerRadius
|
||||
else { return nil }
|
||||
|
||||
let layer = type.layer
|
||||
layer.anchorPoint = .zero
|
||||
layer.name = CALayer.skeletonSubLayersName
|
||||
layer.updateLayerFrame(for: index, width: width)
|
||||
|
||||
layer.cornerRadius = CGFloat(radius)
|
||||
layer.masksToBounds = true
|
||||
|
||||
return layer
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ protocol CollectionSkeleton {
|
||||
var estimatedNumberOfRows: Int { get }
|
||||
|
||||
func addDummyDataSource()
|
||||
func updateDummyDataSource()
|
||||
func removeDummyDataSource(reloadAfter: Bool)
|
||||
func disableUserInteraction()
|
||||
func enableUserInteraction()
|
||||
|
||||
@@ -13,6 +13,7 @@ public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
|
||||
func numSections(in collectionSkeletonView: UICollectionView) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
|
||||
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier
|
||||
func collectionSkeletonView(_ skeletonView: UICollectionView, supplementaryViewIdentifierOfKind: String, at indexPath: IndexPath) -> ReusableCellIdentifier?
|
||||
}
|
||||
|
||||
public extension SkeletonCollectionViewDataSource {
|
||||
@@ -21,6 +22,12 @@ public extension SkeletonCollectionViewDataSource {
|
||||
return skeletonView.estimatedNumberOfRows
|
||||
}
|
||||
|
||||
func collectionSkeletonView(_ skeletonView: UICollectionView,
|
||||
supplementaryViewIdentifierOfKind: String,
|
||||
at indexPath: IndexPath) -> ReusableCellIdentifier? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func numSections(in collectionSkeletonView: UICollectionView) -> Int { return 1 }
|
||||
}
|
||||
|
||||
|
||||
@@ -16,17 +16,17 @@ extension UICollectionView: CollectionSkeleton {
|
||||
}
|
||||
|
||||
var skeletonDataSource: SkeletonCollectionDataSource? {
|
||||
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
|
||||
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
|
||||
set {
|
||||
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource, newValue, AssociationPolicy.retain.objc)
|
||||
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDataSource)
|
||||
self.dataSource = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var skeletonDelegate: SkeletonCollectionDelegate? {
|
||||
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
|
||||
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
|
||||
set {
|
||||
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate, newValue, AssociationPolicy.retain.objc)
|
||||
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDelegate)
|
||||
self.delegate = newValue
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,14 @@ extension UICollectionView: CollectionSkeleton {
|
||||
reloadData()
|
||||
}
|
||||
|
||||
func updateDummyDataSource() {
|
||||
if (dataSource as? SkeletonCollectionDataSource) != nil {
|
||||
reloadData()
|
||||
} else {
|
||||
addDummyDataSource()
|
||||
}
|
||||
}
|
||||
|
||||
func removeDummyDataSource(reloadAfter: Bool) {
|
||||
guard let dataSource = self.dataSource as? SkeletonCollectionDataSource else { return }
|
||||
self.skeletonDataSource = nil
|
||||
|
||||
@@ -38,6 +38,7 @@ extension SkeletonCollectionDataSource: UITableViewDataSource {
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cellIdentifier = originalTableViewDataSource?.collectionSkeletonView(tableView, cellIdentifierForRowAt: indexPath) ?? ""
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
|
||||
skeletonCellIfContainerSkeletonIsActive(container: tableView, cell: cell)
|
||||
return cell
|
||||
}
|
||||
}
|
||||
@@ -56,6 +57,31 @@ extension SkeletonCollectionDataSource: UICollectionViewDataSource {
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let cellIdentifier = originalCollectionViewDataSource?.collectionSkeletonView(collectionView, cellIdentifierForItemAt: indexPath) ?? ""
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath)
|
||||
skeletonCellIfContainerSkeletonIsActive(container: collectionView, cell: cell)
|
||||
return cell
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView,
|
||||
viewForSupplementaryElementOfKind kind: String,
|
||||
at indexPath: IndexPath) -> UICollectionReusableView {
|
||||
|
||||
if let viewIdentifier = originalCollectionViewDataSource?.collectionSkeletonView(collectionView, supplementaryViewIdentifierOfKind: kind, at: indexPath) {
|
||||
|
||||
return collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: viewIdentifier, for: indexPath)
|
||||
}
|
||||
|
||||
return UICollectionReusableView()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension SkeletonCollectionDataSource {
|
||||
private func skeletonCellIfContainerSkeletonIsActive(container: UIView, cell: UIView) {
|
||||
guard container.isSkeletonActive,
|
||||
let skeletonConfig = container.currentSkeletonConfig else {
|
||||
return
|
||||
}
|
||||
|
||||
cell.showSkeleton(skeletonConfig: skeletonConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,17 +15,17 @@ extension UITableView: CollectionSkeleton {
|
||||
}
|
||||
|
||||
var skeletonDataSource: SkeletonCollectionDataSource? {
|
||||
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
|
||||
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
|
||||
set {
|
||||
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDataSource, newValue, AssociationPolicy.retain.objc)
|
||||
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDataSource)
|
||||
self.dataSource = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var skeletonDelegate: SkeletonCollectionDelegate? {
|
||||
get { return objc_getAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
|
||||
get { return ao_get(pkey: &CollectionAssociatedKeys.dummyDelegate) as? SkeletonCollectionDelegate }
|
||||
set {
|
||||
objc_setAssociatedObject(self, &CollectionAssociatedKeys.dummyDelegate, newValue, AssociationPolicy.retain.objc)
|
||||
ao_setOptional(newValue, pkey: &CollectionAssociatedKeys.dummyDelegate)
|
||||
self.delegate = newValue
|
||||
}
|
||||
}
|
||||
@@ -34,11 +34,21 @@ extension UITableView: CollectionSkeleton {
|
||||
guard let originalDataSource = self.dataSource as? SkeletonTableViewDataSource,
|
||||
!(originalDataSource is SkeletonCollectionDataSource)
|
||||
else { return }
|
||||
let dataSource = SkeletonCollectionDataSource(tableViewDataSource: originalDataSource, rowHeight: calculateRowHeight())
|
||||
let rowHeight = calculateRowHeight()
|
||||
let dataSource = SkeletonCollectionDataSource(tableViewDataSource: originalDataSource,
|
||||
rowHeight: rowHeight)
|
||||
self.skeletonDataSource = dataSource
|
||||
reloadData()
|
||||
}
|
||||
|
||||
func updateDummyDataSource() {
|
||||
if (dataSource as? SkeletonCollectionDataSource) != nil {
|
||||
reloadData()
|
||||
} else {
|
||||
addDummyDataSource()
|
||||
}
|
||||
}
|
||||
|
||||
func removeDummyDataSource(reloadAfter: Bool) {
|
||||
guard let dataSource = self.dataSource as? SkeletonCollectionDataSource else { return }
|
||||
restoreRowHeight()
|
||||
@@ -53,7 +63,7 @@ extension UITableView: CollectionSkeleton {
|
||||
}
|
||||
|
||||
private func calculateRowHeight() -> CGFloat {
|
||||
guard rowHeight == UITableViewAutomaticDimension else { return rowHeight }
|
||||
guard rowHeight == UITableView.automaticDimension else { return rowHeight }
|
||||
rowHeight = estimatedRowHeight
|
||||
return estimatedRowHeight
|
||||
}
|
||||
|
||||
@@ -15,6 +15,11 @@ extension UIView {
|
||||
collection.disableUserInteraction()
|
||||
}
|
||||
|
||||
func updateDummyDataSourceIfNeeded() {
|
||||
guard let collection = self as? CollectionSkeleton else { return }
|
||||
collection.updateDummyDataSource()
|
||||
}
|
||||
|
||||
func removeDummyDataSourceIfNeeded(reloadAfter reload: Bool = true) {
|
||||
guard let collection = self as? CollectionSkeleton else { return }
|
||||
collection.removeDummyDataSource(reloadAfter: reload)
|
||||
|
||||
@@ -40,18 +40,49 @@ extension CALayer {
|
||||
|
||||
func addMultilinesLayers(lines: Int, type: SkeletonType, lastLineFillPercent: Int, multilineCornerRadius: Int) {
|
||||
let numberOfSublayers = calculateNumLines(maxLines: lines)
|
||||
for index in 0..<numberOfSublayers {
|
||||
var width = bounds.width
|
||||
|
||||
if index == numberOfSublayers-1 && numberOfSublayers != 1 {
|
||||
width = width * CGFloat(lastLineFillPercent)/100;
|
||||
|
||||
let layerBuilder = SkeletonMultilineLayerBuilder()
|
||||
.setSkeletonType(type)
|
||||
.setCornerRadius(multilineCornerRadius)
|
||||
|
||||
(0..<numberOfSublayers).forEach { index in
|
||||
var width = getLineWidth(index: index, numberOfSublayers: numberOfSublayers, lastLineFillPercent: lastLineFillPercent)
|
||||
if index == numberOfSublayers - 1 && numberOfSublayers != 1 {
|
||||
width = width * CGFloat(lastLineFillPercent) / 100;
|
||||
}
|
||||
|
||||
if let layer = layerBuilder
|
||||
.setIndex(index)
|
||||
.setWidth(width)
|
||||
.build() {
|
||||
addSublayer(layer)
|
||||
}
|
||||
|
||||
let layer = SkeletonLayerFactory().makeMultilineLayer(withType: type, for: index, width: width, multilineCornerRadius: multilineCornerRadius)
|
||||
addSublayer(layer)
|
||||
}
|
||||
}
|
||||
|
||||
func updateMultilinesLayers(lastLineFillPercent: Int) {
|
||||
let currentSkeletonSublayers = skeletonSublayers
|
||||
let numberOfSublayers = currentSkeletonSublayers.count
|
||||
for (index, layer) in currentSkeletonSublayers.enumerated() {
|
||||
let width = getLineWidth(index: index, numberOfSublayers: numberOfSublayers, lastLineFillPercent: lastLineFillPercent)
|
||||
layer.updateLayerFrame(for: index, width: width)
|
||||
}
|
||||
}
|
||||
|
||||
private func getLineWidth(index: Int, numberOfSublayers: Int, lastLineFillPercent: Int) -> CGFloat {
|
||||
var width = bounds.width
|
||||
if index == numberOfSublayers - 1 && numberOfSublayers != 1 {
|
||||
width = width * CGFloat(lastLineFillPercent) / 100;
|
||||
}
|
||||
|
||||
return width
|
||||
}
|
||||
|
||||
func updateLayerFrame(for index: Int, width: CGFloat) {
|
||||
let spaceRequiredForEachLine = SkeletonAppearance.default.multilineHeight + SkeletonAppearance.default.multilineSpacing
|
||||
frame = CGRect(x: 0.0, y: CGFloat(index) * spaceRequiredForEachLine, width: width, height: SkeletonAppearance.default.multilineHeight)
|
||||
}
|
||||
|
||||
private func calculateNumLines(maxLines: Int) -> Int {
|
||||
let spaceRequitedForEachLine = SkeletonAppearance.default.multilineHeight + SkeletonAppearance.default.multilineSpacing
|
||||
var numberOfSublayers = Int(round(CGFloat(bounds.height)/CGFloat(spaceRequitedForEachLine)))
|
||||
@@ -68,7 +99,7 @@ public extension CALayer {
|
||||
pulseAnimation.fromValue = backgroundColor
|
||||
pulseAnimation.toValue = UIColor(cgColor: backgroundColor!).complementaryColor.cgColor
|
||||
pulseAnimation.duration = 1
|
||||
pulseAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
|
||||
pulseAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
|
||||
pulseAnimation.autoreverses = true
|
||||
pulseAnimation.repeatCount = .infinity
|
||||
return pulseAnimation
|
||||
@@ -86,7 +117,7 @@ public extension CALayer {
|
||||
let animGroup = CAAnimationGroup()
|
||||
animGroup.animations = [startPointAnim, endPointAnim]
|
||||
animGroup.duration = 1.5
|
||||
animGroup.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
|
||||
animGroup.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
|
||||
animGroup.repeatCount = .infinity
|
||||
|
||||
return animGroup
|
||||
|
||||
@@ -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,48 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
// codebeat:disable[TOO_MANY_IVARS]
|
||||
enum ViewAssociatedKeys {
|
||||
static var skeletonable = "skeletonable"
|
||||
static var status = "status"
|
||||
static var skeletonLayer = "layer"
|
||||
static var flowDelegate = "flowDelegate"
|
||||
static var isSkeletonAnimated = "isSkeletonAnimated"
|
||||
static var viewState = "viewState"
|
||||
static var currentSkeletonConfig = "currentSkeletonConfig"
|
||||
}
|
||||
// codebeat:enable[TOO_MANY_IVARS]
|
||||
|
||||
extension UIView {
|
||||
|
||||
enum Status {
|
||||
case on
|
||||
case off
|
||||
}
|
||||
|
||||
var flowDelegate: SkeletonFlowDelegate? {
|
||||
get { return ao_get(pkey: &ViewAssociatedKeys.flowDelegate) as? SkeletonFlowDelegate }
|
||||
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.flowDelegate) }
|
||||
}
|
||||
|
||||
var skeletonLayer: SkeletonLayer? {
|
||||
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonLayer) as? SkeletonLayer }
|
||||
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.skeletonLayer) }
|
||||
}
|
||||
|
||||
var currentSkeletonConfig: SkeletonConfig? {
|
||||
get { return ao_get(pkey: &ViewAssociatedKeys.currentSkeletonConfig) as? SkeletonConfig }
|
||||
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.currentSkeletonConfig) }
|
||||
}
|
||||
|
||||
var status: Status! {
|
||||
get { return ao_get(pkey: &ViewAssociatedKeys.status) as? Status ?? .off }
|
||||
set { ao_set(newValue ?? .off, pkey: &ViewAssociatedKeys.status) }
|
||||
}
|
||||
|
||||
var skeletonIsAnimated: Bool! {
|
||||
get { return ao_get(pkey: &ViewAssociatedKeys.isSkeletonAnimated) as? Bool ?? false }
|
||||
set { ao_set(newValue ?? false, pkey: &ViewAssociatedKeys.isSkeletonAnimated) }
|
||||
}
|
||||
}
|
||||
@@ -32,12 +32,12 @@ extension UIView {
|
||||
}
|
||||
|
||||
var maxWidthEstimated: CGFloat {
|
||||
let constraintsWidth = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutAttribute.width })
|
||||
let constraintsWidth = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutConstraint.Attribute.width })
|
||||
return max(between: frame.size.width, andContantsOf: constraintsWidth)
|
||||
}
|
||||
|
||||
var maxHeightEstimated: CGFloat {
|
||||
let constraintsHeight = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutAttribute.height })
|
||||
let constraintsHeight = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutConstraint.Attribute.height })
|
||||
return max(between: frame.size.height, andContantsOf: constraintsHeight)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +1,7 @@
|
||||
//
|
||||
// UIView+IBInspectable.swift
|
||||
// SkeletonView-iOS
|
||||
//
|
||||
// Created by Juanpe Catalán on 01/11/2017.
|
||||
// Copyright © 2017 SkeletonView. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
private enum AssociatedKeys {
|
||||
static var skeletonable = "skeletonable"
|
||||
static var status = "status"
|
||||
static var skeletonLayer = "layer"
|
||||
static var flowDelegate = "flowDelegate"
|
||||
static var isSkeletonAnimated = "isSkeletonAnimated"
|
||||
static var viewState = "viewState"
|
||||
}
|
||||
|
||||
public extension UIView {
|
||||
|
||||
@IBInspectable
|
||||
@@ -28,43 +13,10 @@ public extension UIView {
|
||||
var isSkeletonActive: Bool {
|
||||
return status == .on || (subviewsSkeletonables.first(where: { $0.isSkeletonActive }) != nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
enum Status {
|
||||
case on
|
||||
case off
|
||||
}
|
||||
|
||||
var flowDelegate: SkeletonFlowDelegate? {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.flowDelegate) as? SkeletonFlowDelegate }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.flowDelegate, newValue, AssociationPolicy.retain.objc) }
|
||||
}
|
||||
|
||||
var skeletonLayer: SkeletonLayer? {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.skeletonLayer) as? SkeletonLayer }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.skeletonLayer, newValue, AssociationPolicy.retain.objc) }
|
||||
}
|
||||
|
||||
var status: Status! {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.status) as? Status ?? .off }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.status, newValue, AssociationPolicy.retain.objc) }
|
||||
}
|
||||
|
||||
var viewState: RecoverableViewState? {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.viewState) as? RecoverableViewState }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.viewState, newValue, AssociationPolicy.retain.objc) }
|
||||
}
|
||||
|
||||
var skeletonIsAnimated: Bool! {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.isSkeletonAnimated) as? Bool ?? false }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.isSkeletonAnimated, newValue, AssociationPolicy.retain.objc) }
|
||||
}
|
||||
|
||||
fileprivate var skeletonable: Bool! {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.skeletonable) as? Bool ?? false }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.skeletonable, newValue, AssociationPolicy.retain.objc) }
|
||||
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonable) as? Bool ?? false }
|
||||
set { ao_set(newValue ?? false, pkey: &ViewAssociatedKeys.skeletonable) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import UIKit
|
||||
extension UIView {
|
||||
|
||||
enum Constants {
|
||||
static let becomeActiveNotification = NSNotification.Name.UIApplicationDidBecomeActive
|
||||
static let enterForegroundNotification = NSNotification.Name.UIApplicationDidEnterBackground
|
||||
static let becomeActiveNotification = UIApplication.didBecomeActiveNotification
|
||||
static let enterForegroundNotification = UIApplication.didEnterBackgroundNotification
|
||||
static let needAnimatedSkeletonKey = "needAnimateSkeleton"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
//
|
||||
// AssociationPolicy.swift
|
||||
// SkeletonView-iOS
|
||||
//
|
||||
// Created by Juanpe Catalán on 02/11/2017.
|
||||
// Copyright © 2017 SkeletonView. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@@ -21,3 +15,32 @@ enum AssociationPolicy: UInt {
|
||||
return objc_AssociationPolicy(rawValue: rawValue)!
|
||||
}
|
||||
}
|
||||
|
||||
protocol AssociatedObjects: class { }
|
||||
|
||||
// transparent wrappers
|
||||
extension AssociatedObjects {
|
||||
|
||||
/// wrapper around `objc_getAssociatedObject`
|
||||
func ao_get(pkey: UnsafeRawPointer) -> Any? {
|
||||
return objc_getAssociatedObject(self, pkey)
|
||||
}
|
||||
|
||||
/// wrapper around `objc_setAssociatedObject`
|
||||
func ao_setOptional(_ value: Any?, pkey: UnsafeRawPointer, policy: AssociationPolicy = .retainNonatomic) {
|
||||
guard let value = value else { return }
|
||||
objc_setAssociatedObject(self, pkey, value, policy.objc)
|
||||
}
|
||||
|
||||
/// wrapper around `objc_setAssociatedObject`
|
||||
func ao_set(_ value: Any, pkey: UnsafeRawPointer, policy: AssociationPolicy = .retainNonatomic) {
|
||||
objc_setAssociatedObject(self, pkey, value, policy.objc)
|
||||
}
|
||||
|
||||
/// wrapper around 'objc_removeAssociatedObjects'
|
||||
func ao_removeAll() {
|
||||
objc_removeAssociatedObjects(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension NSObject: AssociatedObjects { }
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
//
|
||||
// ContainsMultilineText.swift
|
||||
// SkeletonView-iOS
|
||||
//
|
||||
// Created by Juanpe Catalán on 07/11/2017.
|
||||
// Copyright © 2017 SkeletonView. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
private enum AssociatedKeys {
|
||||
static var lastLineFillingPercent = "lastLineFillingPercent"
|
||||
static var multilineCornerRadius = "multilineCornerRadius"
|
||||
|
||||
}
|
||||
|
||||
protocol ContainsMultilineText {
|
||||
var numLines: Int { get }
|
||||
var lastLineFillingPercent: Int { get }
|
||||
var multilineCornerRadius: Int { get }
|
||||
}
|
||||
|
||||
extension ContainsMultilineText {
|
||||
var numLines: Int { return 0 }
|
||||
}
|
||||
|
||||
public extension UILabel {
|
||||
|
||||
@IBInspectable
|
||||
var lastLineFillPercent: Int {
|
||||
get { return lastLineFillingPercent }
|
||||
set { lastLineFillingPercent = min(newValue, 100) }
|
||||
}
|
||||
@IBInspectable
|
||||
var linesCornerRadius: Int {
|
||||
get { return multilineCornerRadius }
|
||||
set { multilineCornerRadius = min(newValue, 10) }
|
||||
}
|
||||
}
|
||||
|
||||
public extension UITextView {
|
||||
|
||||
@IBInspectable
|
||||
var lastLineFillPercent: Int {
|
||||
get { return lastLineFillingPercent }
|
||||
set { lastLineFillingPercent = min(newValue, 100) }
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var linesCornerRadius: Int {
|
||||
get { return multilineCornerRadius }
|
||||
set { multilineCornerRadius = min(newValue, 10) }
|
||||
}
|
||||
}
|
||||
|
||||
extension UILabel: ContainsMultilineText {
|
||||
var numLines: Int {
|
||||
return numberOfLines
|
||||
}
|
||||
|
||||
var lastLineFillingPercent: Int {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent) as? Int ?? SkeletonAppearance.default.multilineLastLineFillPercent }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent, newValue, AssociationPolicy.retain.objc) }
|
||||
}
|
||||
var multilineCornerRadius: Int {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.multilineCornerRadius) as? Int ?? SkeletonAppearance.default.multilineCornerRadius }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.multilineCornerRadius, newValue, AssociationPolicy.retain.objc) }
|
||||
}
|
||||
|
||||
}
|
||||
extension UITextView: ContainsMultilineText {
|
||||
|
||||
var lastLineFillingPercent: Int {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent) as? Int ?? SkeletonAppearance.default.multilineLastLineFillPercent }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent, newValue, AssociationPolicy.retain.objc) }
|
||||
}
|
||||
|
||||
var multilineCornerRadius: Int {
|
||||
get { return objc_getAssociatedObject(self, &AssociatedKeys.multilineCornerRadius) as? Int ?? SkeletonAppearance.default.multilineCornerRadius }
|
||||
set { objc_setAssociatedObject(self, &AssociatedKeys.multilineCornerRadius, newValue, AssociationPolicy.retain.objc) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright © 2017 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
enum MultilineAssociatedKeys {
|
||||
static var lastLineFillingPercent = "lastLineFillingPercent"
|
||||
static var multilineCornerRadius = "multilineCornerRadius"
|
||||
}
|
||||
|
||||
protocol ContainsMultilineText {
|
||||
var numLines: Int { get }
|
||||
var lastLineFillingPercent: Int { get }
|
||||
var multilineCornerRadius: Int { get }
|
||||
}
|
||||
|
||||
extension ContainsMultilineText {
|
||||
var numLines: Int { return 0 }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UILabel {
|
||||
@IBInspectable
|
||||
var lastLineFillPercent: Int {
|
||||
get { return lastLineFillingPercent }
|
||||
set { lastLineFillingPercent = min(newValue, 100) }
|
||||
}
|
||||
@IBInspectable
|
||||
var linesCornerRadius: Int {
|
||||
get { return multilineCornerRadius }
|
||||
set { multilineCornerRadius = min(newValue, 10) }
|
||||
}
|
||||
}
|
||||
|
||||
extension UILabel: ContainsMultilineText {
|
||||
var numLines: Int {
|
||||
return numberOfLines
|
||||
}
|
||||
|
||||
var lastLineFillingPercent: Int {
|
||||
get { return ao_get(pkey: &MultilineAssociatedKeys.lastLineFillingPercent) as? Int ?? SkeletonAppearance.default.multilineLastLineFillPercent }
|
||||
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.lastLineFillingPercent) }
|
||||
}
|
||||
|
||||
var multilineCornerRadius: Int {
|
||||
get { return ao_get(pkey: &MultilineAssociatedKeys.multilineCornerRadius) as? Int ?? SkeletonAppearance.default.multilineCornerRadius }
|
||||
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.multilineCornerRadius) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UITextView {
|
||||
|
||||
@IBInspectable
|
||||
var lastLineFillPercent: Int {
|
||||
get { return lastLineFillingPercent }
|
||||
set { lastLineFillingPercent = min(newValue, 100) }
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var linesCornerRadius: Int {
|
||||
get { return multilineCornerRadius }
|
||||
set { multilineCornerRadius = min(newValue, 10) }
|
||||
}
|
||||
}
|
||||
|
||||
extension UITextView: ContainsMultilineText {
|
||||
var lastLineFillingPercent: Int {
|
||||
get {
|
||||
let defaultValue = SkeletonAppearance.default.multilineLastLineFillPercent
|
||||
return ao_get(pkey: &MultilineAssociatedKeys.lastLineFillingPercent) as? Int ?? defaultValue
|
||||
}
|
||||
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.lastLineFillingPercent) }
|
||||
}
|
||||
|
||||
var multilineCornerRadius: Int {
|
||||
get {
|
||||
let defaultValue = SkeletonAppearance.default.multilineCornerRadius
|
||||
return ao_get(pkey: &MultilineAssociatedKeys.multilineCornerRadius) as? Int ?? defaultValue
|
||||
}
|
||||
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.multilineCornerRadius) }
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,11 @@ protocol Recoverable {
|
||||
}
|
||||
|
||||
extension UIView: Recoverable {
|
||||
|
||||
var viewState: RecoverableViewState? {
|
||||
get { return ao_get(pkey: &ViewAssociatedKeys.viewState) as? RecoverableViewState }
|
||||
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.viewState) }
|
||||
}
|
||||
|
||||
@objc func saveViewState() {
|
||||
viewState = RecoverableViewState(view: self)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright © 2018 SkeletonView. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
/// Used to store all config needed to activate the skeleton layer.
|
||||
struct SkeletonConfig {
|
||||
/// Type of skeleton layer
|
||||
let type: SkeletonType
|
||||
/// Colors used in skeleton layer
|
||||
let colors: [UIColor]
|
||||
/// If type is gradient, which gradient direction
|
||||
let gradientDirection: GradientDirection?
|
||||
/// Specify if skeleton is animated or not
|
||||
let animated: Bool
|
||||
/// Used to execute a custom animation
|
||||
let animation: SkeletonLayerAnimation?
|
||||
|
||||
init(
|
||||
type: SkeletonType,
|
||||
colors: [UIColor],
|
||||
gradientDirection: GradientDirection? = nil,
|
||||
animated: Bool = false,
|
||||
animation: SkeletonLayerAnimation? = nil
|
||||
) {
|
||||
self.type = type
|
||||
self.colors = colors
|
||||
self.gradientDirection = gradientDirection
|
||||
self.animated = animated
|
||||
self.animation = animation
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,12 @@ import UIKit
|
||||
protocol SkeletonFlowDelegate {
|
||||
func willBeginShowingSkeletons(withRootView rootView: UIView)
|
||||
func didShowSkeletons(withRootView rootView: UIView)
|
||||
func willBeginUpdatingSkeletons(withRootView rootView: UIView)
|
||||
func didUpdateSkeletons(withRootView rootView: UIView)
|
||||
func willBeginLayingSkeletonsIfNeeded(withRootView: UIView)
|
||||
func didLayoutSkeletonsIfNeeded(withRootView: UIView)
|
||||
func willBeginHidingSkeletons(withRootView rootView: UIView)
|
||||
func didHideSkeletons(withRootView rootView: UIView)
|
||||
|
||||
}
|
||||
|
||||
class SkeletonFlowHandler: SkeletonFlowDelegate {
|
||||
@@ -20,6 +23,18 @@ class SkeletonFlowHandler: SkeletonFlowDelegate {
|
||||
printSkeletonHierarchy(in: rootView)
|
||||
}
|
||||
|
||||
func willBeginUpdatingSkeletons(withRootView rootView: UIView) {
|
||||
}
|
||||
|
||||
func didUpdateSkeletons(withRootView rootView: UIView) {
|
||||
}
|
||||
|
||||
func willBeginLayingSkeletonsIfNeeded(withRootView: UIView) {
|
||||
}
|
||||
|
||||
func didLayoutSkeletonsIfNeeded(withRootView: UIView) {
|
||||
}
|
||||
|
||||
func willBeginHidingSkeletons(withRootView rootView: UIView) {
|
||||
rootView.removeAppNoticationsObserver()
|
||||
}
|
||||
|
||||
+17
-20
@@ -8,26 +8,6 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SkeletonLayerFactory {
|
||||
|
||||
func makeSkeletonLayer(withType type: SkeletonType, usingColors colors: [UIColor], andHolder holder: UIView) -> SkeletonLayer {
|
||||
return SkeletonLayer(withType: type, usingColors: colors, andSkeletonHolder: holder)
|
||||
}
|
||||
|
||||
func makeMultilineLayer(withType type: SkeletonType, for index: Int, width: CGFloat, multilineCornerRadius: Int) -> CALayer {
|
||||
let spaceRequiredForEachLine = SkeletonAppearance.default.multilineHeight + SkeletonAppearance.default.multilineSpacing
|
||||
let layer = type.layer
|
||||
layer.anchorPoint = .zero
|
||||
layer.name = CALayer.skeletonSubLayersName
|
||||
layer.frame = CGRect(x: 0.0, y: CGFloat(index) * spaceRequiredForEachLine, width: width, height: SkeletonAppearance.default.multilineHeight)
|
||||
|
||||
layer.cornerRadius = CGFloat(multilineCornerRadius)
|
||||
layer.masksToBounds = true
|
||||
|
||||
return layer
|
||||
}
|
||||
}
|
||||
|
||||
public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation
|
||||
|
||||
public enum SkeletonType {
|
||||
@@ -75,6 +55,18 @@ struct SkeletonLayer {
|
||||
self.maskLayer.tint(withColors: colors)
|
||||
}
|
||||
|
||||
func update(usingColors colors: [UIColor]) {
|
||||
layoutIfNeeded()
|
||||
self.maskLayer.tint(withColors: colors)
|
||||
}
|
||||
|
||||
func layoutIfNeeded() {
|
||||
if let bounds = self.holder?.maxBoundsEstimated {
|
||||
self.maskLayer.bounds = bounds
|
||||
}
|
||||
updateMultilinesIfNeeded()
|
||||
}
|
||||
|
||||
func removeLayer() {
|
||||
maskLayer.removeFromSuperlayer()
|
||||
}
|
||||
@@ -83,6 +75,11 @@ struct SkeletonLayer {
|
||||
guard let multiLineView = holder as? ContainsMultilineText else { return }
|
||||
maskLayer.addMultilinesLayers(lines: multiLineView.numLines, type: type, lastLineFillPercent: multiLineView.lastLineFillingPercent, multilineCornerRadius: multiLineView.multilineCornerRadius)
|
||||
}
|
||||
|
||||
func updateMultilinesIfNeeded() {
|
||||
guard let multiLineView = holder as? ContainsMultilineText else { return }
|
||||
maskLayer.updateMultilinesLayers(lastLineFillPercent: multiLineView.lastLineFillingPercent)
|
||||
}
|
||||
}
|
||||
|
||||
extension SkeletonLayer {
|
||||
|
||||
+133
-27
@@ -5,19 +5,49 @@ import UIKit
|
||||
public extension UIView {
|
||||
|
||||
func showSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor) {
|
||||
showSkeleton(withType: .solid, usingColors: [color])
|
||||
let config: SkeletonConfig = SkeletonConfig(type: .solid, colors: [color])
|
||||
showSkeleton(skeletonConfig: config)
|
||||
}
|
||||
|
||||
func showGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient) {
|
||||
showSkeleton(withType: .gradient, usingColors: gradient.colors)
|
||||
let config: SkeletonConfig = SkeletonConfig(type: .gradient, colors: gradient.colors)
|
||||
showSkeleton(skeletonConfig: config)
|
||||
}
|
||||
|
||||
func showAnimatedSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor, animation: SkeletonLayerAnimation? = nil) {
|
||||
showSkeleton(withType: .solid, usingColors: [color], animated: true, animation: animation)
|
||||
let config: SkeletonConfig = SkeletonConfig(type: .solid, colors: [color], animated: true, animation: animation)
|
||||
showSkeleton(skeletonConfig: config)
|
||||
}
|
||||
|
||||
func showAnimatedGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient, animation: SkeletonLayerAnimation? = nil) {
|
||||
showSkeleton(withType: .gradient, usingColors: gradient.colors, animated: true, animation: animation)
|
||||
let config: SkeletonConfig = SkeletonConfig(type: .gradient, colors: gradient.colors, animated: true, animation: animation)
|
||||
showSkeleton(skeletonConfig: config)
|
||||
}
|
||||
|
||||
func updateSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor) {
|
||||
let config: SkeletonConfig = SkeletonConfig(type: .solid, colors: [color])
|
||||
updateSkeleton(skeletonConfig: config)
|
||||
}
|
||||
|
||||
func updateGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient) {
|
||||
let config: SkeletonConfig = SkeletonConfig(type: .gradient, colors: gradient.colors)
|
||||
updateSkeleton(skeletonConfig: config)
|
||||
}
|
||||
|
||||
func updateAnimatedSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor, animation: SkeletonLayerAnimation? = nil) {
|
||||
let config: SkeletonConfig = SkeletonConfig(type: .solid, colors: [color], animated: true, animation: animation)
|
||||
updateSkeleton(skeletonConfig: config)
|
||||
}
|
||||
|
||||
func updateAnimatedGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient, animation: SkeletonLayerAnimation? = nil) {
|
||||
let config: SkeletonConfig = SkeletonConfig(type: .gradient, colors: gradient.colors, animated: true, animation: animation)
|
||||
updateSkeleton(skeletonConfig: config)
|
||||
}
|
||||
|
||||
func layoutSkeletonIfNeeded() {
|
||||
guard let flowDelegate = flowDelegate else { return }
|
||||
flowDelegate.willBeginLayingSkeletonsIfNeeded(withRootView: self)
|
||||
recursiveLayoutSkeletonIfNeeded(root: self)
|
||||
}
|
||||
|
||||
func hideSkeleton(reloadDataAfter reload: Bool = true) {
|
||||
@@ -42,33 +72,88 @@ public extension UIView {
|
||||
|
||||
extension UIView {
|
||||
|
||||
func showSkeleton(withType type: SkeletonType = .solid, usingColors colors: [UIColor], animated: Bool = false, animation: SkeletonLayerAnimation? = nil) {
|
||||
skeletonIsAnimated = animated
|
||||
func showSkeleton(skeletonConfig config: SkeletonConfig) {
|
||||
skeletonIsAnimated = config.animated
|
||||
flowDelegate = SkeletonFlowHandler()
|
||||
flowDelegate?.willBeginShowingSkeletons(withRootView: self)
|
||||
recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation, root: self)
|
||||
recursiveShowSkeleton(skeletonConfig: config, root: self)
|
||||
}
|
||||
|
||||
fileprivate func recursiveShowSkeleton(withType type: SkeletonType, usingColors colors: [UIColor], animated: Bool, animation: SkeletonLayerAnimation?, root: UIView? = nil) {
|
||||
addDummyDataSourceIfNeeded()
|
||||
|
||||
fileprivate func recursiveShowSkeleton(skeletonConfig config: SkeletonConfig, root: UIView? = nil) {
|
||||
layoutIfNeeded()
|
||||
|
||||
currentSkeletonConfig = config
|
||||
|
||||
addDummyDataSourceIfNeeded()
|
||||
subviewsSkeletonables.recursiveSearch(leafBlock: {
|
||||
guard !isSkeletonActive else { return }
|
||||
isUserInteractionEnabled = false
|
||||
saveViewState()
|
||||
(self as? PrepareForSkeleton)?.prepareViewForSkeleton()
|
||||
addSkeletonLayer(withType: type, usingColors: colors, animated: animated, animation: animation)
|
||||
}) { subview in
|
||||
subview.recursiveShowSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation)
|
||||
showSkeletonIfNotActive(skeletonConfig: config)
|
||||
}){ subview in
|
||||
subview.recursiveShowSkeleton(skeletonConfig: config)
|
||||
}
|
||||
|
||||
if let root = root {
|
||||
flowDelegate?.didShowSkeletons(withRootView: root)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fileprivate func showSkeletonIfNotActive(skeletonConfig config: SkeletonConfig) {
|
||||
guard !self.isSkeletonActive else { return }
|
||||
self.isUserInteractionEnabled = false
|
||||
self.saveViewState()
|
||||
(self as? PrepareForSkeleton)?.prepareViewForSkeleton()
|
||||
self.addSkeletonLayer(skeletonConfig: config)
|
||||
}
|
||||
|
||||
func updateSkeleton(skeletonConfig config: SkeletonConfig) {
|
||||
guard let flowDelegate = flowDelegate else { return }
|
||||
skeletonIsAnimated = config.animated
|
||||
flowDelegate.willBeginUpdatingSkeletons(withRootView: self)
|
||||
recursiveUpdateSkeleton(skeletonConfig: config, root: self)
|
||||
}
|
||||
|
||||
fileprivate func recursiveUpdateSkeleton(skeletonConfig config: SkeletonConfig, root: UIView? = nil) {
|
||||
layoutIfNeeded()
|
||||
|
||||
currentSkeletonConfig = config
|
||||
|
||||
updateDummyDataSourceIfNeeded()
|
||||
subviewsSkeletonables.recursiveSearch(leafBlock: {
|
||||
guard isSkeletonActive else { return }
|
||||
|
||||
if skeletonLayer?.type != config.type {
|
||||
hideSkeleton()
|
||||
}
|
||||
|
||||
if isSkeletonActive {
|
||||
updateSkeletonLayer(skeletonConfig: config)
|
||||
} else {
|
||||
showSkeletonIfNotActive(skeletonConfig: config)
|
||||
}
|
||||
}) { subview in
|
||||
subview.recursiveUpdateSkeleton(skeletonConfig: config)
|
||||
}
|
||||
|
||||
if let root = root {
|
||||
flowDelegate?.didUpdateSkeletons(withRootView: root)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func recursiveLayoutSkeletonIfNeeded(root: UIView? = nil) {
|
||||
layoutIfNeeded()
|
||||
|
||||
subviewsSkeletonables.recursiveSearch(leafBlock: {
|
||||
layoutSkeletonLayerIfNeeded()
|
||||
}) { subview in
|
||||
subview.recursiveLayoutSkeletonIfNeeded()
|
||||
}
|
||||
|
||||
if let root = root {
|
||||
flowDelegate?.didLayoutSkeletonsIfNeeded(withRootView: root)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func recursiveHideSkeleton(reloadDataAfter reload: Bool, root: UIView? = nil) {
|
||||
removeDummyDataSourceIfNeeded()
|
||||
removeDummyDataSourceIfNeeded(reloadAfter: reload)
|
||||
isUserInteractionEnabled = true
|
||||
subviewsSkeletonables.recursiveSearch(leafBlock: {
|
||||
recoverViewState(forced: false)
|
||||
@@ -76,6 +161,7 @@ extension UIView {
|
||||
}) { subview in
|
||||
subview.recursiveHideSkeleton(reloadDataAfter: reload)
|
||||
}
|
||||
|
||||
if let root = root {
|
||||
flowDelegate?.didHideSkeletons(withRootView: root)
|
||||
}
|
||||
@@ -98,20 +184,40 @@ extension UIView {
|
||||
|
||||
extension UIView {
|
||||
|
||||
func addSkeletonLayer(withType type: SkeletonType, usingColors colors: [UIColor], gradientDirection direction: GradientDirection? = nil, animated: Bool, animation: SkeletonLayerAnimation? = nil) {
|
||||
self.skeletonLayer = SkeletonLayerFactory().makeSkeletonLayer(withType: type, usingColors: colors, andHolder: self)
|
||||
layer.insertSublayer(skeletonLayer!.contentLayer, at: UInt32.max)
|
||||
if animated { skeletonLayer!.start(animation) }
|
||||
func addSkeletonLayer(skeletonConfig config: SkeletonConfig) {
|
||||
guard let skeletonLayer = SkeletonLayerBuilder()
|
||||
.setSkeletonType(config.type)
|
||||
.addColors(config.colors)
|
||||
.setHolder(self)
|
||||
.build()
|
||||
else { return }
|
||||
|
||||
self.skeletonLayer = skeletonLayer
|
||||
layer.insertSublayer(skeletonLayer.contentLayer, at: UInt32.max)
|
||||
if config.animated { skeletonLayer.start(config.animation) }
|
||||
status = .on
|
||||
}
|
||||
|
||||
func updateSkeletonLayer(skeletonConfig config: SkeletonConfig) {
|
||||
guard let skeletonLayer = skeletonLayer else { return }
|
||||
skeletonLayer.update(usingColors: config.colors)
|
||||
if config.animated { skeletonLayer.start(config.animation) }
|
||||
else { skeletonLayer.stopAnimation() }
|
||||
}
|
||||
|
||||
func layoutSkeletonLayerIfNeeded() {
|
||||
guard let skeletonLayer = skeletonLayer else { return }
|
||||
skeletonLayer.layoutIfNeeded()
|
||||
}
|
||||
|
||||
func removeSkeletonLayer() {
|
||||
guard isSkeletonActive,
|
||||
let layer = skeletonLayer else { return }
|
||||
layer.stopAnimation()
|
||||
layer.removeLayer()
|
||||
skeletonLayer = nil
|
||||
let skeletonLayer = skeletonLayer else { return }
|
||||
skeletonLayer.stopAnimation()
|
||||
skeletonLayer.removeLayer()
|
||||
self.skeletonLayer = nil
|
||||
status = .off
|
||||
currentSkeletonConfig = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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).
|
||||
Reference in New Issue
Block a user