Compare commits

...

33 Commits

Author SHA1 Message Date
Juanpe Catalán b90e97592a feat: BUMP version 1.3 2018-08-02 17:32:31 +02:00
Juanpe Catalán 9fbaaa02f5 feat: Update README and CHANGELOG file 2018-08-02 17:32:23 +02:00
Juanpe Catalán 3f7f2ec95c feat: Remove useless code 2018-08-02 16:33:20 +02:00
Juanpe Catalán 26da7e3fc0 feat: Rename SkeletonAppearance 2018-08-02 16:32:57 +02:00
Juanpe Catalán 804390e98a feat: Create SkeletonViewAppearance 2018-08-02 16:18:41 +02:00
Juanpe Catalán 3bf613da0d Merge pull request #84 from reececomo/83-Customisable-Defaults
#83 Customisable Defaults
2018-08-02 15:41:40 +02:00
Reece Como af91af3716 #83 Customisable Defaults 2018-08-02 16:08:31 +08:00
Juanpe Catalán 2fe2323cc1 Merge pull request #42 from kjoneandrei/feature/fix_skeleton_collectionView
Fix: Skeleton loading for UICollectionView
2018-07-31 09:54:00 +02:00
anho 024171a993 fixed collectionview xib and skeleton 2018-07-27 15:28:59 +02:00
Juanpe Catalán 726817cfe5 feat: Add credits 2018-07-27 13:05:14 +02:00
Juanpe Catalán 9684817956 feat: Change README path 2018-07-27 13:00:36 +02:00
Juanpe Catalán 3a847ece3d fix: Link to Chinese README file 2018-07-27 12:43:11 +02:00
Juanpe Catalán a0fb735c3e feat: Add Chinese README file 2018-07-27 12:38:16 +02:00
Juanpe Catalán c2720898ac feat: BUMP version 1.2.3 2018-07-25 08:52:15 +02:00
Juanpe Catalán 9e395c7747 feat: Update CHANGELOG file 2018-07-25 08:50:08 +02:00
Juanpe Catalán b8aa587af6 Merge pull request #78 from hisaac/master
fix: Typos in README and method signature
2018-07-24 15:13:51 +02:00
Juanpe Catalán 349f7eb48e Merge pull request #77 from giantramen/master
feat: Use Stack View to Calculate Bounds
2018-07-24 11:59:13 +02:00
Juanpe Catalán 4f874a84f0 feat: Bump version 1.2.2 2018-07-24 08:34:26 +02:00
Juanpe Catalán a6de5342a4 Merge branch 'master' of https://github.com/Juanpe/SkeletonView 2018-07-24 08:27:42 +02:00
Juanpe Catalán 404da95cbe feat: New branding 2018-07-24 08:27:39 +02:00
Isaac Halvorson ebe7f3e463 Add default implementation of old method and deprecation message 2018-07-19 10:03:47 -05:00
Isaac Halvorson ad5cf2a21f Fix a couple small typos in README and a method signature 2018-07-19 09:37:15 -05:00
Kamin, Grant 7afe87160a Use Stack View to Calculate Bounds
Labels inside of stack views are not aligned properly
because we were assuming leading alignment.
Calculating the origin based off of the stack view alignment
instead.
2018-07-06 09:04:10 -05:00
Juanpe Catalán 7c7be7c80c Merge pull request #73 from giantramen/master
fix: UIStackView Label Issue #23
2018-06-26 16:25:59 +02:00
giantramen ff42bd02e2 Fix UIStackView Label Issue
Fixes issue where SkeletonView sets the text of each label to nil, UIStackView sets the height of each label to 0 and lays them out on top of each other.
2018-06-26 09:20:49 -05:00
Juanpe Catalán 0136c8576c feat: Update README 2018-06-06 10:36:19 +02:00
Juanpe Catalán 76c526385d Merge branch 'master' of https://github.com/Juanpe/SkeletonView 2018-06-06 10:20:16 +02:00
Juanpe Catalán 2b633e5ab6 BUMP version 1.2.1 2018-06-06 10:20:12 +02:00
Juanpe Catalán 5c19df67df Merge pull request #68 from eduardbosch/patch-1
Revert wrong README change
2018-06-06 10:00:59 +02:00
Juanpe Catalán b1778542d7 Merge pull request #69 from eduardbosch/patch-2
Fix tipo and remove space
2018-06-06 10:00:30 +02:00
Eduard Bosch Bertran ad5c87eea2 Fix tipo and remove space 2018-06-06 09:32:13 +02:00
Eduard Bosch Bertran 09813e530c Revert wrong README change
I've just reverted a missing ` in the README in this commit https://github.com/Juanpe/SkeletonView/commit/c06ae2d72c640a8fddf520991f3a3b274ad91ffd
2018-06-06 09:28:49 +02:00
Juanpe Catalán 23489159dd Update CHANGELOG.md 2018-06-05 17:51:11 +02:00
38 changed files with 1435 additions and 83 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 10 KiB

+39
View File
@@ -1,6 +1,45 @@
# Change Log
All notable changes to this project will be documented in this file
## [Custom defaults (1.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.3)
### New
- Default values customizables. Now you can set the default values of Skeleton appearance.(thanks @reececomo)
- issues: [[#50](https://github.com/Juanpe/SkeletonView/issues/50), [#83](https://github.com/Juanpe/SkeletonView/issues/83)]
### Bug fixes
- Solved issue [#41](https://github.com/Juanpe/SkeletonView/issues/41). Now, Skeleton works if UICollectionView cell's Nib is registered in code. (thanks @kjoneandrei)
## [Typo (1.2.3)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.3)
### Fixes
- Fix typo in `SkeletonTableViewDataSource` protocol
### Improvements
- Now it takes in account the `UIStackView` to calculate the `SkeletonLayer` bounds (thanks @giantramen)
## [New face (1.2.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.2)
### New
- Rebranding
### Bug fixes
- Solved issue [#23](https://github.com/Juanpe/SkeletonView/issues/23). Problem with UIStackView. (thanks @giantramen)
## [State (1.2.1)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2.1)
### New
- You can set the corner radius multiline elements (thanks @B4V4-G)
- Save view state when skeleton appears and recovery when it is hidden (@juanpe)
### Bug fixes
- Solved issue [#51](https://github.com/Juanpe/SkeletonView/issues/51). Support inspectable properties when using Carthage. (thanks @eduardbosch)
## [On TV (1.2)](https://github.com/Juanpe/SkeletonView/releases/tag/1.2)
### New
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.2</string>
<string>1.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.2</string>
<string>1.3</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
@@ -0,0 +1,46 @@
//
// AppDelegate.swift
// SkeletonViewExampleUICollectionView
//
// Created by Andrei Hogea on 14/02/2018.
// Copyright © 2018 SkeletonView. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
@@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
@@ -0,0 +1,41 @@
//
// CollectionViewCell.swift
// SkeletonView-iOS
//
// Created by Andrei Hogea on 14/02/2018.
// Copyright © 2018 SkeletonView. All rights reserved.
//
import UIKit
import SkeletonView
class CollectionViewCell: UICollectionViewCell {
var label: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
isSkeletonable = true
createLabel()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func createLabel() {
label = UILabel()
label.isSkeletonable = true
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: centerXAnchor),
label.centerYAnchor.constraint(equalTo: centerYAnchor),
label.heightAnchor.constraint(equalToConstant: frame.height / 2),
label.widthAnchor.constraint(equalToConstant: frame.width / 2)
])
}
}
+12
View File
@@ -0,0 +1,12 @@
//
// Constants.swift
// SkeletonViewExampleUICollectionView
//
// Created by Andrei Hogea on 15/02/2018.
// Copyright © 2018 SkeletonView. All rights reserved.
//
import UIKit
let colors = [(UIColor.turquoise,"turquoise"), (UIColor.emerald,"emerald"), (UIColor.peterRiver,"peterRiver"), (UIColor.amethyst,"amethyst"),(UIColor.wetAsphalt,"wetAsphalt"), (UIColor.nephritis,"nephritis"), (UIColor.belizeHole,"belizeHole"), (UIColor.wisteria,"wisteria"), (UIColor.midnightBlue,"midnightBlue"), (UIColor.sunFlower,"sunFlower"), (UIColor.carrot,"carrot"), (UIColor.alizarin,"alizarin"),(UIColor.clouds,"clouds"), (UIColor.concrete,"concrete"), (UIColor.flatOrange,"flatOrange"), (UIColor.pumpkin,"pumpkin"), (UIColor.pomegranate,"pomegranate"), (UIColor.silver,"silver"), (UIColor.asbestos,"asbestos")]
+167
View File
@@ -0,0 +1,167 @@
<?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">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="qda-qV-vJk">
<objects>
<viewController id="irH-dz-xqL" customClass="ViewController" customModule="SkeletonView" 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>
<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"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="mGU-kn-rfE">
<size key="itemSize" width="50" height="50"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<cells/>
<connections>
<outlet property="dataSource" destination="irH-dz-xqL" id="wya-hE-ovQ"/>
</connections>
</collectionView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JjA-MK-YzZ">
<rect key="frame" x="0.0" y="527" width="375" height="140"/>
<subviews>
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="fMR-vj-7de">
<rect key="frame" x="20" y="23" width="140" height="29"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
<segment title="Solid"/>
<segment title="Gradient"/>
</segments>
<connections>
<action selector="changeSkeletonType:" destination="irH-dz-xqL" eventType="valueChanged" id="lfR-JV-DU4"/>
</connections>
</segmentedControl>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KBe-RM-BG8">
<rect key="frame" x="310" y="21" width="49" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<connections>
<action selector="changeAnimated:" destination="irH-dz-xqL" eventType="valueChanged" id="dlH-KK-iee"/>
</connections>
</switch>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Animated" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GSj-Ze-UIK">
<rect key="frame" x="211" y="28" width="91" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Color" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4Hm-fj-45V">
<rect key="frame" x="32" y="89" width="52" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="HBJ-nh-56V">
<rect key="frame" x="92" y="84" width="30" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aUR-Qo-gHK">
<rect key="frame" x="20" y="74" width="140" height="52"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<connections>
<action selector="btnChangeColorTouchUpInside:" destination="irH-dz-xqL" eventType="touchUpInside" id="Xca-QC-htl"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="140" id="QDV-wu-e3I"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="140" id="qR5-cz-YAm"/>
</constraints>
</view>
<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>
<constraint firstItem="eHI-ka-8vS" firstAttribute="trailing" secondItem="2Gq-Y8-1TU" secondAttribute="trailing" id="0dc-Vd-yJY"/>
<constraint firstItem="JjA-MK-YzZ" firstAttribute="leading" secondItem="2Gq-Y8-1TU" secondAttribute="leading" id="57i-UV-Wqd"/>
<constraint firstItem="eHI-ka-8vS" firstAttribute="leading" secondItem="2Gq-Y8-1TU" secondAttribute="leading" id="5tt-Ne-67Y"/>
<constraint firstItem="JjA-MK-YzZ" firstAttribute="bottom" secondItem="2Gq-Y8-1TU" secondAttribute="bottom" id="AAr-ke-R7M"/>
<constraint firstItem="JjA-MK-YzZ" firstAttribute="trailing" secondItem="2Gq-Y8-1TU" secondAttribute="trailing" id="DtS-9c-zBC"/>
<constraint firstItem="HKL-L0-T2w" firstAttribute="top" secondItem="eHI-ka-8vS" secondAttribute="bottom" id="Jgf-jS-PLT"/>
<constraint firstItem="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>
<connections>
<outlet property="avatarImage" destination="Ql9-Jy-aWM" id="VoL-by-ygR"/>
<outlet property="collectionView" destination="HKL-L0-T2w" id="HSe-j0-S5d"/>
<outlet property="colorSelectedView" destination="HBJ-nh-56V" id="Iiq-iY-Glj"/>
<outlet property="skeletonTypeSelector" destination="fMR-vj-7de" id="CgX-3A-weo"/>
<outlet property="switchAnimated" destination="KBe-RM-BG8" id="emU-g9-NHT"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="PkM-Y0-M5i" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-972" y="-209.14542728635683"/>
</scene>
</scenes>
<resources>
<image name="avatar" width="215" height="211"/>
</resources>
</document>
@@ -0,0 +1,179 @@
//
// ViewController.swift
// SkeletonViewExampleUICollectionView
//
// Created by Andrei Hogea on 14/02/2018.
// Copyright © 2018 SkeletonView. All rights reserved.
//
import UIKit
import SkeletonView
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView! {
didSet {
collectionView.isSkeletonable = true
collectionView.backgroundColor = .clear
collectionView.showsHorizontalScrollIndicator = false
collectionView.showsVerticalScrollIndicator = false
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
}
}
@IBOutlet weak var avatarImage: UIImageView! {
didSet {
avatarImage.layer.cornerRadius = avatarImage.frame.width/2
avatarImage.layer.masksToBounds = true
}
}
@IBOutlet weak var colorSelectedView: UIView! {
didSet {
colorSelectedView.layer.cornerRadius = 5
colorSelectedView.layer.masksToBounds = true
colorSelectedView.backgroundColor = SkeletonAppearance.default.tintColor
}
}
@IBOutlet weak var switchAnimated: UISwitch!
@IBOutlet weak var skeletonTypeSelector: UISegmentedControl!
var type: SkeletonType {
return skeletonTypeSelector.selectedSegmentIndex == 0 ? .solid : .gradient
}
override func viewDidLoad() {
super.viewDidLoad()
view.isSkeletonable = true
collectionView.prepareSkeleton(completion: { done in
self.view.showAnimatedSkeleton()
})
}
@IBAction func changeAnimated(_ sender: Any) {
if switchAnimated.isOn {
view.startSkeletonAnimation()
} else {
view.stopSkeletonAnimation()
}
}
@IBAction func changeSkeletonType(_ sender: Any) {
refreshSkeleton()
}
@IBAction func btnChangeColorTouchUpInside(_ sender: Any) {
showAlertPicker()
}
func refreshSkeleton() {
self.view.hideSkeleton()
if type == .gradient { showGradientSkeleton() }
else { showSolidSkeleton() }
}
func showSolidSkeleton() {
if switchAnimated.isOn {
view.showAnimatedSkeleton(usingColor: colorSelectedView.backgroundColor!)
} else {
view.showSkeleton(usingColor: colorSelectedView.backgroundColor!)
}
}
func showGradientSkeleton() {
let gradient = SkeletonGradient(baseColor: colorSelectedView.backgroundColor!)
if switchAnimated.isOn {
view.showAnimatedGradientSkeleton(usingGradient: gradient)
} else {
view.showGradientSkeleton(usingGradient: gradient)
}
}
func showAlertPicker() {
let alertView = UIAlertController(title: "Select color", message: "\n\n\n\n\n\n", preferredStyle: .alert)
let pickerView = UIPickerView(frame: CGRect(x: 0, y: 50, width: 260, height: 115))
pickerView.dataSource = self
pickerView.delegate = self
alertView.view.addSubview(pickerView)
let action = UIAlertAction(title: "OK", style: .default) { [unowned pickerView, unowned self] _ in
let row = pickerView.selectedRow(inComponent: 0)
self.colorSelectedView.backgroundColor = colors[row].0
self.refreshSkeleton()
}
alertView.addAction(action)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertView.addAction(cancelAction)
present(alertView, animated: false, completion: {
pickerView.frame.size.width = alertView.view.frame.size.width
})
}
}
// MARK: - UIPickerViewDelegate, UIPickerViewDataSource
extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return colors.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return colors[row].1
}
}
// MARK: - UICollectionViewDelegateFlowLayout
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width/3 - 10, height: view.frame.width/3 - 10)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
}
// MARK: - SkeletonCollectionViewDataSource
extension ViewController: SkeletonCollectionViewDataSource {
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "CollectionViewCell"
}
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
// MARK: - UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
return cell
}
}
+2 -2
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
@@ -213,7 +213,7 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-2481" y="-394"/>
<point key="canvasLocation" x="-2582" y="-400"/>
</scene>
</scenes>
<resources>
+2 -2
View File
@@ -29,7 +29,7 @@ class ViewController: UIViewController {
didSet {
colorSelectedView.layer.cornerRadius = 5
colorSelectedView.layer.masksToBounds = true
colorSelectedView.backgroundColor = SkeletonDefaultConfig.tintColor
colorSelectedView.backgroundColor = SkeletonAppearance.default.tintColor
}
}
@@ -136,7 +136,7 @@ extension ViewController: SkeletonTableViewDataSource {
return 9
}
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "CellIdentifier"
}
+34 -17
View File
@@ -1,4 +1,4 @@
![](Assets/header.jpg)
![](Assets/header2.jpg)
<p align="center">
<a href="https://travis-ci.org/Juanpe/SkeletonView">
@@ -31,6 +31,8 @@
</a>
</p>
🌎 Translations: [ [🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) by [@WhatsXie](https://twitter.com/WhatsXie) ]
Today almost all apps have async processes, such as Api requests, long running processes, etc. And while the processes are working, usually developers place a loading view to show users that something is going on.
```SkeletonView``` has been conceived to address this need, an elegant way to show users that something is happening and also prepare them to which contents he is waiting.
@@ -47,11 +49,12 @@ Enjoy it! 🙂
* [Collections](#-collections)
* [Multiline text](#-multiline-text)
* [Custom colors](#-custom-colors)
* [Appearance](#-appearance)
* [Custom animations](#-custom-animations)
* [Hierarchy](#-hierarchy)
* [Documentation](#-documentation)
* [Next steps](#-next-steps)
* [Contributed](#-contributed)
* [Contributing](#-contributing)
* [Mentions](#-mentions)
* [Author](#-author)
* [License](#-license)
@@ -77,8 +80,6 @@ Enjoy it! 🙂
To run the example project, clone the repo and run `SkeletonViewExample` target.
![](Assets/demoApp2.png)
## 📲 Installation
#### Using [CocoaPods](https://cocoapods.org)
@@ -173,7 +174,7 @@ If you want to show the skeleton in a ```UITableView```, you need to conform to
public protocol SkeletonTableViewDataSource: UITableViewDataSource {
func numSections(in collectionSkeletonView: UITableView) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
}
```
As you can see, this protocol inherits from ```UITableViewDataSource```, so you can replace this protocol with the skeleton protocol.
@@ -193,12 +194,12 @@ func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection s
There is only one method you need to implement to let Skeleton know the cell identifier. This method doesn't have default implementation:
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
```
**Example**
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "CellIdentifier"
}
```
@@ -231,12 +232,14 @@ Besides, you can decide how many lines you want. If ```numberOfLines``` is set
##### 🎛 Customize
You can set some properties for multilines elements.
- **Filling percent** of the last line.
- values: `0...100`
- default: `70%`
- **Corner radius** of lines. (**NEW**)
- values: `0...10
- default: `0`
| Property | Values | Default | Preview
| ------- | ------- |------- | -------
| **Filling percent** of the last line. | `0...100` | `70%` | ![](Assets/multiline_lastline.png)
| **Corner radius** of lines. (**NEW**) | `0...10` | `0` | ![](Assets/multiline_corner.png)
To modify the percent or radius **using code**, set the properties:
```swift
@@ -271,9 +274,22 @@ Besides, ```SkeletonView``` features 20 flat colors 🤙🏼
![](Assets/flatcolors.png)
###### Image captured from website [https://flatuicolors.com](https://flatuicolors.com)
### 🦋 Appearance
**NEW** The skeletons have a default appearance. So, when you don't specify the color, gradient or multilines properties, `SkeletonView` uses the default values.
Default values:
- **tintColor**: UIColor
- **gradient**: SkeletonGradient
- **multilineHeight**: CGFloat
- **multilineSpacing**: CGFloat
- **multilineLastLineFillPercent**: Int
- **multilineCornerRadius**: Int _0_
### 🤓 Custom animations
Now, ```SkeletonView``` has two built-in animations, *pulse* for solid skeletons and *sliding* for gradients.
```SkeletonView``` has two built-in animations, *pulse* for solid skeletons and *sliding* for gradients.
Besides, if you want to do your own skeleton animation, it's really easy.
@@ -295,7 +311,7 @@ view.showAnimatedSkeleton { (layer) -> CAAnimation in
}
```
**NEW** It's available ```SkeletonAnimationBuilder```. It's a builder to make ```SkeletonLayerAnimation```.
It's available ```SkeletonAnimationBuilder```. It's a builder to make ```SkeletonLayerAnimation```.
Today, you can create **sliding animations** for gradients, deciding the **direction** and setting the **duration** of the animation (default = 1.5s).
@@ -313,7 +329,7 @@ view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
|------- | -------
| .leftRight | ![](Assets/sliding_left_to_right.gif)
| .rightLeft | ![](Assets/sliding_right_to_left.gif)
| .topBottom | ![](Assets/sliding_top_to_bottom.gif)
| .topBottom | ![](Assets/sliding_top_to_bottom.gif)
| .bottomTop | ![](Assets/sliding_bottom_to_top.gif)
| .topLeftBottomRight | ![](Assets/sliding_topLeft_to_bottomRight.gif)
| .bottomRightTopLeft | ![](Assets/sliding_bottomRight_to_topLeft.gif)
@@ -350,11 +366,12 @@ Coming soon...😅
* [x] CollectionView compatible
* [x] tvOS compatible
* [x] Add recovery state
* [x] Custom default appearance
* [ ] Custom collections compatible
* [ ] Add animations when it shows/hides the skeletons
* [ ] MacOS and WatchOS compatible
## ❤️ Contributed
## ❤️ Contributing
This is an open source project, so feel free to contribute. How?
- Open an [issue](https://github.com/Juanpe/SkeletonView/issues/new).
- Send feedback via [email](mailto://juanpecatalan.com).
Executable
+419
View File
@@ -0,0 +1,419 @@
![](Assets/header2.jpg)
<p align="center">
<a href="https://travis-ci.org/Juanpe/SkeletonView">
<img src="https://img.shields.io/travis/Juanpe/SkeletonView.svg">
</a>
<a href="https://instagram.github.io/IGListKit/">
<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" />
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
</a>
<a href="https://github.com/Carthage/Carthage">
<img src="https://img.shields.io/badge/carthage-compatible-4BC51D.svg?style=flat" alt="Carthage" />
</a>
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/dt/SkeletonView.svg?style=flat" alt="CocoaPods downloads" />
</a>
<a href="https://twitter.com/JuanpeCatalan">
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
</a>
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=MJ4Y2D9DEX6FL&lc=ES&item_name=SkeletonView&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">
<img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Paypal" />
</a>
<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) ]
今天,几乎所有的应用程序都有异步流程,例如:Api请求、长时间运行的流程等。虽然流程正在运行,但通常开发人员会设置一个加载视图来向用户显示正在发生的事情。
```SkeletonView``` 已经构想出来满足这种需求,这是一种优雅的方式,向用户展示正在发生的事情,并为他们等待的内容做好准备。
好好享受! 🙂
* [特征](#-特征)
* [版本要求](#-版本要求)
* [示例项目](#-示例)
* [安装](#-安装)
* [Cocoapods](#使用-cocoapods)
* [Carthage](#使用-carthage)
* [如何使用](#-如何使用)
* [集合](#-集合)
* [多行文字](#-多行文字)
* [自定义颜色](#-自定义颜色)
* [自定义动画](#-自定义动画)
* [等级制度](#-等级制度)
* [文档](#-文档)
* [下一步](#-下一步)
* [特约](#-特约)
* [提及](#-提及)
* [作者](#-作者)
* [许可证](#-许可证)
## 🌟 特征
- [x] 使用方便
- [x] 支持所有 UIView
- [x] 完全可定制
- [x] 通用(iPhone和iPad
- [x] Interface Builder 友好
- [x] 简单的 Swift 语法
- [x] 轻量级可读代码库
### 📋 版本要求
* iOS 9.0+
* tvOS 9.0+
* Swift 4
### 🔮 示例
要运行示例项目,请克隆并运行 `SkeletonViewExample` 项目。
## 📲 安装
#### 使用 [CocoaPods](https://cocoapods.org)
使用 CocoaPods 编辑您的 Podfile 并指定依赖项:
```ruby
pod "SkeletonView"
```
#### 使用 [Carthage](https://github.com/carthage)
编辑您的 Cartfile 并指定依赖项:
```bash
github "Juanpe/SkeletonView"
```
## 🐒 如何使用
只需 **3** 个步骤即可使用 `SkeletonView`:
**1.** 在适当的位置导入SkeletonView
```swift
import SkeletonView
```
**2.** 现在,您可以通过两种设置方式实现 `SkeletonView` 效果
**使用纯代码:**
```swift
avatarImageView.isSkeletonable = true
```
**使用 IB/Storyboards**
![](Assets/storyboard.png)
**3.** 设置视图后,可以显示 **skeleton**. 并且您有 **4** 种效果可供选择:
```swift
(1) view.showSkeleton() // 固体
(2) view.showGradientSkeleton() // 渐变
(3) view.showAnimatedSkeleton() // 纯色动画
(4) view.showAnimatedGradientSkeleton() // 渐变动画
```
**Preview**
<table>
<tr>
<td width="25%">
<center>固体</center>
</td>
<td width="25%">
<center>渐变</center>
</td>
<td width="25%">
<center>纯色动画</center>
</td>
<td width="25%">
<center>渐变动画</center>
</td>
</tr>
<tr>
<td width="25%">
<img src="Assets/solid.png"></img>
</td>
<td width="25%">
<img src="Assets/gradient.png"></img>
</td>
<td width="25%">
<img src="Assets/solid_animated.gif"></img>
</td>
<td width="25%">
<img src="Assets/gradient_animated.gif"></img>
</td>
</tr>
</table>
> **重要!**
>>```SkeletonView``` 是递归的,所以如果你想在所有可骨架化的视图中显示骨架,你只需要在主容器视图中调用show方法。例如,使用UIViewControllers
### 🌿 集合
现在,```SkeletonView``` 兼容 ```UITableView``` 和 ```UICollectionView```。
###### UITableView
如果你要显示 skeleton 在一个 ```UITableView```上,你需要符合 ```SkeletonTableViewDataSource``` 协议。
``` swift
public protocol SkeletonTableViewDataSource: UITableViewDataSource {
func numSections(in collectionSkeletonView: UITableView) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
}
```
如您所见,此协议继承自 UITableViewDataSource,因此您可以使用骨架协议替换此协议。
该协议具有默认实现:
``` swift
func numSections(in collectionSkeletonView: UITableView) -> Int
// 默认值:1
```
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
// 默认值:
// 它计算填充整个tableview需要多少个单元格
```
为了让Skeleton知道单元标识符,您只需要实现一种方法。此方法没有默认实现:
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
```
**示例**
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "CellIdentifier"
}
```
> **重要!**
> 如果您使用可调整大小的单元格 (`tableView.rowHeight = UITableViewAutomaticDimension` ),则必须定义 `estimatedRowHeight`。
###### UICollectionView
要为 ```UICollectionView``` 设置效果, 您需要符合 ```SkeletonCollectionViewDataSource``` 协议。
``` swift
public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
func numSections(in collectionSkeletonView: UICollectionView) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier
}
```
其余操作与 ```UITableView``` 相同。
### 📰 多行文字
![](Assets/multilines2.png)
使用带有文本的元素时, ```SkeletonView``` 绘制线条以模拟文本。此外,您可以决定您想要多少行。如果 ```numberOfLines``` 设置为零,它将计算填充整个骨架所需的行数,并将绘制它。相反,如果将其设置为一,二或任何大于零的数字,它将只绘制此行数。
##### 🎛 定制
您可以为多行元素设置一些属性。
| 属性 | 值范围 | 默认 | 延时
| ------- | ------- |------- | -------
| **Filling percent** 最后一行的长度百分比 | `0...100` | `70%` | ![](Assets/multiline_lastline.png)
| **Corner radius** 条目圆角半径. (**新**) | `0...10` | `0` | ![](Assets/multiline_corner.png)
**纯代码**修改百分比或半径:
```swift
descriptionTextView.lastLineFillPercent = 50
descriptionTextView.linesCornerRadius = 5
```
或者,如果您更喜欢使用 **IB/Storyboard**:
![](Assets/multiline_customize.png)
### 🎨 自定义颜色
您可以决定 ```SkeletonView``` 的显示颜色。您只需要传递颜色或渐变的参数。
**使用纯色**
``` swift
view.showSkeleton(usingColor: UIColor.gray) // 固体效果
// 或者
view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0))
```
**使用渐变色**
``` swift
let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
view.showGradientSkeleton(usingGradient: gradient) // 梯度效果
```
此外, ```SkeletonView``` 附带的 20 种颜色 🤙🏼
```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...```
![](Assets/flatcolors.png)
###### 从网站 [https://flatuicolors.com](https://flatuicolors.com)捕获的图像
### 🤓 自定义动画
现在,```SkeletonView``` 有两个内置动画,*pulse* 脉冲效果和 *sliding* 渐变滑动效果。
此外,如果你想做自己的 skeleton 动画,那真的很容易。
Skeleton 提供了 `showAnimatedSkeleton` 一个具有 ```SkeletonLayerAnimation``` 闭包的功能,您可以在其中定义自定义动画。
```swift
public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation
```
您可以像这样调用函数:
```swift
view.showAnimatedSkeleton { (layer) -> CAAnimation in
let animation = CAAnimation()
// 在这里自定义你的动画
return animation
}
```
**新** 它可用 ```SkeletonAnimationBuilder```。这是一个 ```SkeletonLayerAnimation```的衍生。
今天,您可以为渐变创建 **滑动动画**,确定 **方向** 并设置动画的 **持续时间** (默认值 = 1.5s)。
```swift
// func makeSlidingAnimation(withDirection direction: GradientDirection, duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation
let animation = SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: .leftToRight)
view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
```
```GradientDirection``` 是一个枚举,在这种情况下:
| 方向 | 效果
|------- | -------
| .leftRight | ![](Assets/sliding_left_to_right.gif)
| .rightLeft | ![](Assets/sliding_right_to_left.gif)
| .topBottom | ![](Assets/sliding_top_to_bottom.gif)
| .bottomTop | ![](Assets/sliding_bottom_to_top.gif)
| .topLeftBottomRight | ![](Assets/sliding_topLeft_to_bottomRight.gif)
| .bottomRightTopLeft | ![](Assets/sliding_bottomRight_to_topLeft.gif)
> **😉 技巧!**
存在另一种创建滑动动画的方法,只需使用此快捷方式:
>>```let animation = GradientDirection.leftToRight.slidingAnimation()```
### 👨‍👧‍👦 等级制度
由于 ```SkeletonView``` 是递归的,我们希望 skeleton 效率高效, 我们希望尽快停止递归。因此,您必须将容器视图设置为 `Skeletonable` ,因为`skeletonable` 一旦视图不是 Skeletonable, Skeleton 将停止查找子视图,然后断开递归。
一图胜千言:
> 设置 ```ìsSkeletonable```= ☠️
| 分组 | 结果
|------- | -------
|![](Assets/no_skeletonable.png) | ![](Assets/no_skeletonables_result.png)
|![](Assets/container_no_skeletonable.png) | ![](Assets/no_skeletonables_result.png)
|![](Assets/container_skeletonable.png) | ![](Assets/container_skeletonable_result.png)
|![](Assets/all_skeletonables.png) | ![](Assets/all_skeletonables_result.png)
### 📚 文档
快出来...😅
## 📬 下一步
* [x] 设置多行元素中最后一行的填充百分比
* [x] 添加更多渐变动画
* [x] 支持可调整大小的单元
* [x] CollectionView 兼容
* [x] tvOS 兼容
* [x] 添加恢复状态
* [ ] 自定义集合兼容
* [ ] 在显示/隐藏骨架时添加动画
* [ ] MacOS 和 WatchOS兼容
## ❤️ 特约
这是一个开源项目,所以请随时贡献。怎么样?
- 打开一个 [issue](https://github.com/Juanpe/SkeletonView/issues/new)
- 反馈通过发送 [email](mailto://juanpecatalan.com)
- 提出您自己的修复和建议,并带有拉取的请求。
查看 [所有贡献者](https://github.com/Juanpe/SkeletonView/graphs/contributors)
###### 使用 [SwiftPlate](https://github.com/JohnSundell/SwiftPlate) 生成的项目
## 📢 提及
- [iOS Dev Weekly #327](https://iosdevweekly.com/issues/327#start)
- [Hacking with Swift Articles](https://www.hackingwithswift.com/articles/40/skeletonview-makes-loading-content-beautiful)
- [Top 10 Swift Articles November](https://medium.mybridge.co/swift-top-10-articles-for-the-past-month-v-nov-2017-dfed7861cd65)
- [30 Amazing iOS Swift Libraries (v2018)](https://medium.mybridge.co/30-amazing-ios-swift-libraries-for-the-past-year-v-2018-7cf15027eee9)
- [AppCoda Weekly #44](http://digest.appcoda.com/issues/appcoda-weekly-issue-44-81899)
- [iOS Cookies Newsletter #103](https://us11.campaign-archive.com/?u=cd1f3ed33c6527331d82107ba&id=48131a516d)
- [Swift Developments Newsletter #113](https://andybargh.com/swiftdevelopments-113/)
- [iOS Goodies #204](http://ios-goodies.com/post/167557280951/week-204)
- [Swift Weekly #96](http://digest.swiftweekly.com/issues/swift-weekly-issue-96-81759)
- [CocoaControls](https://www.cocoacontrols.com/controls/skeletonview)
- [Awesome iOS Newsletter #74](https://ios.libhunt.com/newsletter/74)
## 👨🏻‍💻 作者
[1.1]: http://i.imgur.com/tXSoThF.png
[1]: http://www.twitter.com/JuanpeCatalan
* Juanpe Catalán [![alt text][1.1]][1]
<a class="bmc-button" target="_blank" href="https://www.buymeacoffee.com/CDou4xtIK"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy me a coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;"><span style="margin-left:5px"></span></a>
## 👮🏻 许可证
```
MIT License
Copyright (c) 2017 Juanpe Catalán
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SkeletonView"
s.version = "1.2"
s.version = "1.3"
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.
+184 -9
View File
@@ -20,7 +20,7 @@
17DD0E12207FB28C00C56334 /* UIView+IBInspectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */; };
17DD0E13207FB28C00C56334 /* UIView+UIApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */; };
17DD0E14207FB28F00C56334 /* SkeletonAnimationBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */; };
17DD0E15207FB28F00C56334 /* SkeletonDefaultConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2F1FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift */; };
17DD0E15207FB28F00C56334 /* SkeletonAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2F1FB0EC9D00EE67C5 /* SkeletonAppearance.swift */; };
17DD0E16207FB28F00C56334 /* SkeletonGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2B1FAF6BC900EE67C5 /* SkeletonGradient.swift */; };
17DD0E17207FB28F00C56334 /* SkeletonLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F899E81FAB9D2B002E8FDA /* SkeletonLayer.swift */; };
17DD0E18207FB28F00C56334 /* SkeletonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* SkeletonView.swift */; };
@@ -31,6 +31,16 @@
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, ); }; };
42ABD078210B54E200BEEFF4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD070210B54E100BEEFF4 /* AppDelegate.swift */; };
42ABD079210B54E200BEEFF4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD071210B54E100BEEFF4 /* Main.storyboard */; };
42ABD07A210B54E200BEEFF4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD072210B54E100BEEFF4 /* Assets.xcassets */; };
42ABD07B210B54E200BEEFF4 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD073210B54E100BEEFF4 /* ViewController.swift */; };
42ABD07C210B54E200BEEFF4 /* Base.lproj in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD074210B54E100BEEFF4 /* Base.lproj */; };
42ABD07D210B54E200BEEFF4 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD075210B54E100BEEFF4 /* Constants.swift */; };
42ABD07E210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 42ABD076210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist */; };
42ABD07F210B54E200BEEFF4 /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42ABD077210B54E200BEEFF4 /* CollectionViewCell.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, ); }; };
88DEA97F1FCDBD78006C80EF /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88DEA97D1FCDBD1F006C80EF /* Constants.swift */; };
@@ -44,7 +54,7 @@
F51ED28520973CC9008B2434 /* SkeletonReusableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F51DF870206E91B300D23301 /* SkeletonReusableCell.swift */; };
F5307E2C1FAF6BC900EE67C5 /* SkeletonGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2B1FAF6BC900EE67C5 /* SkeletonGradient.swift */; };
F5307E2E1FB0E5E400EE67C5 /* UIView+Frame.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2D1FB0E5E400EE67C5 /* UIView+Frame.swift */; };
F5307E301FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2F1FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift */; };
F5307E301FB0EC9D00EE67C5 /* SkeletonAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E2F1FB0EC9D00EE67C5 /* SkeletonAppearance.swift */; };
F5307E321FB0F42F00EE67C5 /* RecursiveProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */; };
F5307E371FB1076E00EE67C5 /* SkeletonTableViewProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E361FB1076E00EE67C5 /* SkeletonTableViewProtocols.swift */; };
F5307E391FB1078E00EE67C5 /* SkeletonCollectionViewProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E381FB1078E00EE67C5 /* SkeletonCollectionViewProtocols.swift */; };
@@ -79,6 +89,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
42ABD05C210B548200BEEFF4 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 52D6D9731BEFF229002C0205 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 52D6D97B1BEFF229002C0205;
remoteInfo = "SkeletonView-iOS";
};
F5307E431FB3B84500EE67C5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 52D6D9731BEFF229002C0205 /* Project object */;
@@ -89,6 +106,17 @@
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
42ABD068210B548200BEEFF4 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
42ABD069210B548200BEEFF4 /* SkeletonView.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
F5307E451FB3B84600EE67C5 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@@ -108,6 +136,15 @@
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>"; };
42ABD071210B54E100BEEFF4 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
42ABD072210B54E100BEEFF4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
42ABD073210B54E100BEEFF4 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
42ABD074210B54E100BEEFF4 /* Base.lproj */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Base.lproj; sourceTree = "<group>"; };
42ABD075210B54E100BEEFF4 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
42ABD076210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SkeletonViewExampleCollectionview-Info.plist"; sourceTree = "<group>"; };
42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCell.swift; sourceTree = "<group>"; };
52D6D97C1BEFF229002C0205 /* SkeletonView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SkeletonView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
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>"; };
@@ -117,7 +154,7 @@
F51DF878206E9F5500D23301 /* SkeletonCollectionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonCollectionDelegate.swift; sourceTree = "<group>"; };
F5307E2B1FAF6BC900EE67C5 /* SkeletonGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonGradient.swift; sourceTree = "<group>"; };
F5307E2D1FB0E5E400EE67C5 /* UIView+Frame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Frame.swift"; sourceTree = "<group>"; };
F5307E2F1FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonDefaultConfig.swift; sourceTree = "<group>"; };
F5307E2F1FB0EC9D00EE67C5 /* SkeletonAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonAppearance.swift; sourceTree = "<group>"; };
F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecursiveProtocol.swift; sourceTree = "<group>"; };
F5307E361FB1076E00EE67C5 /* SkeletonTableViewProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonTableViewProtocols.swift; sourceTree = "<group>"; };
F5307E381FB1078E00EE67C5 /* SkeletonCollectionViewProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonCollectionViewProtocols.swift; sourceTree = "<group>"; };
@@ -156,6 +193,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
42ABD062210B548200BEEFF4 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
42ABD063210B548200BEEFF4 /* SkeletonView.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
52D6D9781BEFF229002C0205 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -174,11 +219,26 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
42ABD06F210B54BC00BEEFF4 /* Example UICollectionView */ = {
isa = PBXGroup;
children = (
42ABD070210B54E100BEEFF4 /* AppDelegate.swift */,
42ABD072210B54E100BEEFF4 /* Assets.xcassets */,
42ABD074210B54E100BEEFF4 /* Base.lproj */,
42ABD077210B54E200BEEFF4 /* CollectionViewCell.swift */,
42ABD075210B54E100BEEFF4 /* Constants.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 */,
@@ -193,6 +253,7 @@
52D6D97C1BEFF229002C0205 /* SkeletonView.framework */,
F5F899F21FABA607002E8FDA /* SkeletonViewExample.app */,
17DD0E00207FB27400C56334 /* SkeletonView.framework */,
42ABD06D210B548200BEEFF4 /* SkeletonViewExampleUICollectionView.app */,
);
name = Products;
sourceTree = "<group>";
@@ -214,7 +275,7 @@
F5307E341FB106A500EE67C5 /* Extensions */,
F5307E351FB106BF00EE67C5 /* Helpers */,
F51DE1081FBF70A70037919A /* SkeletonAnimationBuilder.swift */,
F5307E2F1FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift */,
F5307E2F1FB0EC9D00EE67C5 /* SkeletonAppearance.swift */,
F587FB83202CBFC8002DB5FE /* SkeletonFlow.swift */,
F5307E2B1FAF6BC900EE67C5 /* SkeletonGradient.swift */,
F5F899E81FAB9D2B002E8FDA /* SkeletonLayer.swift */,
@@ -330,6 +391,7 @@
F5F89A061FABA725002E8FDA /* Example */ = {
isa = PBXGroup;
children = (
42ABD076210B54E200BEEFF4 /* SkeletonViewExampleCollectionview-Info.plist */,
F5F89A001FABA607002E8FDA /* SkeletonViewExampleInfo.plist */,
);
name = Example;
@@ -377,6 +439,25 @@
productReference = 17DD0E00207FB27400C56334 /* SkeletonView.framework */;
productType = "com.apple.product-type.framework";
};
42ABD05A210B548200BEEFF4 /* SkeletonViewExampleUICollectionView */ = {
isa = PBXNativeTarget;
buildConfigurationList = 42ABD06A210B548200BEEFF4 /* Build configuration list for PBXNativeTarget "SkeletonViewExampleUICollectionView" */;
buildPhases = (
42ABD05D210B548200BEEFF4 /* Sources */,
42ABD062210B548200BEEFF4 /* Frameworks */,
42ABD064210B548200BEEFF4 /* Resources */,
42ABD068210B548200BEEFF4 /* Embed Frameworks */,
);
buildRules = (
);
dependencies = (
42ABD05B210B548200BEEFF4 /* PBXTargetDependency */,
);
name = SkeletonViewExampleUICollectionView;
productName = SkeletonViewExample;
productReference = 42ABD06D210B548200BEEFF4 /* SkeletonViewExampleUICollectionView.app */;
productType = "com.apple.product-type.application";
};
52D6D97B1BEFF229002C0205 /* SkeletonView-iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "SkeletonView-iOS" */;
@@ -428,9 +509,13 @@
CreatedOnToolsVersion = 9.3;
ProvisioningStyle = Automatic;
};
42ABD05A210B548200BEEFF4 = {
DevelopmentTeam = KWEMDK92F4;
LastSwiftMigration = 0940;
ProvisioningStyle = Automatic;
};
52D6D97B1BEFF229002C0205 = {
CreatedOnToolsVersion = 7.1;
DevelopmentTeam = KWEMDK92F4;
LastSwiftMigration = 0800;
};
F5F899F11FABA607002E8FDA = {
@@ -456,6 +541,7 @@
52D6D97B1BEFF229002C0205 /* SkeletonView-iOS */,
17DD0DFF207FB27400C56334 /* SkeletonView-tvOS */,
F5F899F11FABA607002E8FDA /* SkeletonViewExample */,
42ABD05A210B548200BEEFF4 /* SkeletonViewExampleUICollectionView */,
);
};
/* End PBXProject section */
@@ -468,6 +554,17 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
42ABD064210B548200BEEFF4 /* Resources */ = {
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 */,
);
runOnlyForDeploymentPostprocessing = 0;
};
52D6D97A1BEFF229002C0205 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -512,7 +609,7 @@
17DD0E12207FB28C00C56334 /* UIView+IBInspectable.swift in Sources */,
17DD0E14207FB28F00C56334 /* SkeletonAnimationBuilder.swift in Sources */,
17DD0E11207FB28C00C56334 /* UIView+Frame.swift in Sources */,
17DD0E15207FB28F00C56334 /* SkeletonDefaultConfig.swift in Sources */,
17DD0E15207FB28F00C56334 /* SkeletonAppearance.swift in Sources */,
17DD0E10207FB28C00C56334 /* UIColor+Skeleton.swift in Sources */,
F51ED28420973CC6008B2434 /* GenericCollectionView.swift in Sources */,
17DD0E0A207FB28900C56334 /* SkeletonCollectionViewProtocols.swift in Sources */,
@@ -523,6 +620,17 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
42ABD05D210B548200BEEFF4 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
42ABD07B210B54E200BEEFF4 /* ViewController.swift in Sources */,
42ABD078210B54E200BEEFF4 /* AppDelegate.swift in Sources */,
42ABD07F210B54E200BEEFF4 /* CollectionViewCell.swift in Sources */,
42ABD07D210B54E200BEEFF4 /* Constants.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
52D6D9771BEFF229002C0205 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -531,7 +639,7 @@
F54CF5E42024CEB000330B0D /* UIView+CollectionSkeleton.swift in Sources */,
F5307E371FB1076E00EE67C5 /* SkeletonTableViewProtocols.swift in Sources */,
8933C7851EB5B820000D00A4 /* SkeletonView.swift in Sources */,
F5307E301FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift in Sources */,
F5307E301FB0EC9D00EE67C5 /* SkeletonAppearance.swift in Sources */,
F58A6E6B20A8C54100612494 /* RecoverableViewState.swift in Sources */,
F54CF5E52024CEB000330B0D /* UITableView+CollectionSkeleton.swift in Sources */,
F5307E321FB0F42F00EE67C5 /* RecursiveProtocol.swift in Sources */,
@@ -572,6 +680,11 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
42ABD05B210B548200BEEFF4 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 52D6D97B1BEFF229002C0205 /* SkeletonView-iOS */;
targetProxy = 42ABD05C210B548200BEEFF4 /* PBXContainerItemProxy */;
};
F5307E441FB3B84500EE67C5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 52D6D97B1BEFF229002C0205 /* SkeletonView-iOS */;
@@ -663,6 +776,59 @@
};
name = Release;
};
42ABD06B210B548200BEEFF4 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = KWEMDK92F4;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "SkeletonViewExample copy-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;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
42ABD06C210B548200BEEFF4 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = KWEMDK92F4;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "SkeletonViewExample copy-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;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
52D6D98E1BEFF229002C0205 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -782,7 +948,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = KWEMDK92F4;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
@@ -807,7 +973,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = KWEMDK92F4;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
@@ -885,6 +1051,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
42ABD06A210B548200BEEFF4 /* Build configuration list for PBXNativeTarget "SkeletonViewExampleUICollectionView" */ = {
isa = XCConfigurationList;
buildConfigurations = (
42ABD06B210B548200BEEFF4 /* Debug */,
42ABD06C210B548200BEEFF4 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "SkeletonView" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>FILEHEADER</key>
<string> ___COPYRIGHT___</string>
</dict>
</plist>
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.3</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
@@ -52,3 +52,20 @@ extension UICollectionView: CollectionSkeleton {
extension UICollectionView: GenericCollectionView {
var scrollView: UIScrollView { return self }
}
public extension UICollectionView {
func prepareSkeleton(completion: @escaping (Bool) -> Void) {
guard let originalDataSource = self.dataSource as? SkeletonCollectionViewDataSource,
!(originalDataSource is SkeletonCollectionDataSource)
else { return }
let dataSource = SkeletonCollectionDataSource(collectionViewDataSource: originalDataSource, rowHeight: 0.0)
self.skeletonDataSource = dataSource
performBatchUpdates({
self.reloadData()
}) { (done) in
completion(done)
}
}
}
@@ -36,7 +36,7 @@ extension SkeletonCollectionDataSource: UITableViewDataSource {
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = originalTableViewDataSource?.collectionSkeletonView(tableView, cellIdenfierForRowAt: indexPath) ?? ""
let cellIdentifier = originalTableViewDataSource?.collectionSkeletonView(tableView, cellIdentifierForRowAt: indexPath) ?? ""
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
return cell
}
@@ -11,7 +11,7 @@ import UIKit
public protocol SkeletonTableViewDataSource: UITableViewDataSource {
func numSections(in collectionSkeletonView: UITableView) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
}
public extension SkeletonTableViewDataSource {
@@ -21,6 +21,13 @@ public extension SkeletonTableViewDataSource {
}
func numSections(in collectionSkeletonView: UITableView) -> Int { return 1 }
/// Keeping the misspelled version around until it can be deprecated
/// Right now, it just calls the new correctly spelled method and returns its result
@available(*, deprecated, renamed: "collectionSkeletonView(_:cellIdentifierForRowAt:)")
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return collectionSkeletonView(skeletonView, cellIdentifierForRowAt: indexPath)
}
}
public protocol SkeletonTableViewDelegate: UITableViewDelegate {
@@ -58,6 +58,3 @@ extension UITableView: CollectionSkeleton {
return estimatedRowHeight
}
}
+1 -1
View File
@@ -51,7 +51,7 @@ extension CALayer {
}
private func calculateNumLines(maxLines: Int) -> Int {
let spaceRequitedForEachLine = SkeletonDefaultConfig.multilineHeight + SkeletonDefaultConfig.multilineSpacing
let spaceRequitedForEachLine = SkeletonAppearance.default.multilineHeight + SkeletonAppearance.default.multilineSpacing
var numberOfSublayers = Int(round(CGFloat(bounds.height)/CGFloat(spaceRequitedForEachLine)))
if maxLines != 0, maxLines <= numberOfSublayers { numberOfSublayers = maxLines }
return numberOfSublayers
+12
View File
@@ -12,6 +12,18 @@ import UIKit
extension UIView {
var maxBoundsEstimated: CGRect {
if let parentStackView = (superview as? UIStackView) {
var origin: CGPoint = .zero
switch parentStackView.alignment {
case .center:
origin.x = maxWidthEstimated / 2
case .trailing:
origin.x = maxWidthEstimated
default:
break
}
return CGRect(origin: origin, size: maxSizeEstimated)
}
return CGRect(origin: .zero, size: maxSizeEstimated)
}
+4 -4
View File
@@ -59,11 +59,11 @@ extension UILabel: ContainsMultilineText {
}
var lastLineFillingPercent: Int {
get { return objc_getAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent) as? Int ?? SkeletonDefaultConfig.multilineLastLineFillPercent }
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 ?? SkeletonDefaultConfig.multilineCornerRadius }
get { return objc_getAssociatedObject(self, &AssociatedKeys.multilineCornerRadius) as? Int ?? SkeletonAppearance.default.multilineCornerRadius }
set { objc_setAssociatedObject(self, &AssociatedKeys.multilineCornerRadius, newValue, AssociationPolicy.retain.objc) }
}
@@ -71,12 +71,12 @@ extension UILabel: ContainsMultilineText {
extension UITextView: ContainsMultilineText {
var lastLineFillingPercent: Int {
get { return objc_getAssociatedObject(self, &AssociatedKeys.lastLineFillingPercent) as? Int ?? SkeletonDefaultConfig.multilineLastLineFillPercent }
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 ?? SkeletonDefaultConfig.multilineCornerRadius }
get { return objc_getAssociatedObject(self, &AssociatedKeys.multilineCornerRadius) as? Int ?? SkeletonAppearance.default.multilineCornerRadius }
set { objc_setAssociatedObject(self, &AssociatedKeys.multilineCornerRadius, newValue, AssociationPolicy.retain.objc) }
}
}
@@ -14,14 +14,14 @@ protocol PrepareForSkeleton {
extension UILabel: PrepareForSkeleton {
func prepareViewForSkeleton() {
text = nil
text = " "
resignFirstResponder()
}
}
extension UITextView: PrepareForSkeleton {
func prepareViewForSkeleton() {
text = nil
text = " "
resignFirstResponder()
}
}
+2 -2
View File
@@ -40,7 +40,7 @@ extension UILabel {
override func recoverViewState(forced: Bool) {
super.recoverViewState(forced: forced)
text = text == "" || forced ? viewState?.text : text
text = text == " " || forced ? viewState?.text : text
}
}
@@ -52,7 +52,7 @@ extension UITextView {
override func recoverViewState(forced: Bool) {
super.recoverViewState(forced: forced)
text = text == "" || forced ? viewState?.text : text
text = text == " " || forced ? viewState?.text : text
}
}
+33
View File
@@ -0,0 +1,33 @@
// Copyright © 2017 SkeletonView. All rights reserved.
import UIKit
public protocol Appearance {
var tintColor: UIColor { get set }
var gradient: SkeletonGradient { get set }
var multilineHeight: CGFloat { get set }
var multilineSpacing: CGFloat { get set }
var multilineLastLineFillPercent: Int { get set }
var multilineCornerRadius: Int { get set }
}
public enum SkeletonAppearance {
public static var `default`: Appearance = SkeletonViewAppearance.shared
}
class SkeletonViewAppearance: Appearance {
static var shared = SkeletonViewAppearance()
var tintColor: UIColor = .clouds
var gradient: SkeletonGradient = SkeletonGradient(baseColor: .clouds)
var multilineHeight: CGFloat = 15
var multilineSpacing: CGFloat = 10
var multilineLastLineFillPercent: Int = 70
var multilineCornerRadius: Int = 0
}
-24
View File
@@ -1,24 +0,0 @@
//
// SkeletonDefaultConfig.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 06/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
public enum SkeletonDefaultConfig {
public static let tintColor = UIColor.clouds
public static let gradient = SkeletonGradient(baseColor: tintColor)
public static let multilineHeight: CGFloat = 15
public static let multilineSpacing: CGFloat = 10
public static let multilineLastLineFillPercent = 70
public static let multilineCornerRadius = 0
}
+2 -2
View File
@@ -15,11 +15,11 @@ class SkeletonLayerFactory {
}
func makeMultilineLayer(withType type: SkeletonType, for index: Int, width: CGFloat, multilineCornerRadius: Int) -> CALayer {
let spaceRequiredForEachLine = SkeletonDefaultConfig.multilineHeight + SkeletonDefaultConfig.multilineSpacing
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: SkeletonDefaultConfig.multilineHeight)
layer.frame = CGRect(x: 0.0, y: CGFloat(index) * spaceRequiredForEachLine, width: width, height: SkeletonAppearance.default.multilineHeight)
layer.cornerRadius = CGFloat(multilineCornerRadius)
layer.masksToBounds = true
+4 -10
View File
@@ -1,28 +1,22 @@
//
// SkeletonView.swift
// SkeletonView
//
// Created by Juanpe Catalán on 01/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
public extension UIView {
func showSkeleton(usingColor color: UIColor = SkeletonDefaultConfig.tintColor) {
func showSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor) {
showSkeleton(withType: .solid, usingColors: [color])
}
func showGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonDefaultConfig.gradient) {
func showGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient) {
showSkeleton(withType: .gradient, usingColors: gradient.colors)
}
func showAnimatedSkeleton(usingColor color: UIColor = SkeletonDefaultConfig.tintColor, animation: SkeletonLayerAnimation? = nil) {
func showAnimatedSkeleton(usingColor color: UIColor = SkeletonAppearance.default.tintColor, animation: SkeletonLayerAnimation? = nil) {
showSkeleton(withType: .solid, usingColors: [color], animated: true, animation: animation)
}
func showAnimatedGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonDefaultConfig.gradient, animation: SkeletonLayerAnimation? = nil) {
func showAnimatedGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonAppearance.default.gradient, animation: SkeletonLayerAnimation? = nil) {
showSkeleton(withType: .gradient, usingColors: gradient.colors, animated: true, animation: animation)
}