feat: Initial commit. Generate project with SwiftPlate

This commit is contained in:
Juanpe Catalán
2017-11-07 20:10:53 +01:00
parent 4a5672db17
commit 7f18fdd6c8
34 changed files with 2104 additions and 0 deletions
Executable
+1
View File
@@ -0,0 +1 @@
4.0
Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

+24
View File
@@ -0,0 +1,24 @@
<?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>en</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>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</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.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>
+46
View File
@@ -0,0 +1,46 @@
//
// AppDelegate.swift
// SkeletonViewExample
//
// Created by Juanpe Catalán on 02/11/2017.
// Copyright © 2017 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,93 @@
{
"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"
}
],
"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>
+175
View File
@@ -0,0 +1,175 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="6sb-2E-JJO">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13527"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Navigation Controller-->
<scene sceneID="6Rq-zb-dR7">
<objects>
<navigationController id="6sb-2E-JJO" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="V26-bp-jcy">
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="yow-sC-oOs"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Kr9-9Q-ddd" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-1353" y="104"/>
</scene>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="SkeletonViewExample" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="120" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="UCB-SP-lQk">
<rect key="frame" x="0.0" y="250" width="375" height="373"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="separatorColor" red="0.1061807256" green="0.84678786989999999" blue="0.031482450150000001" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="CellIdentifier" rowHeight="120" id="2dN-Bd-tdy" customClass="Cell" customModule="SkeletonViewExample" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" width="375" height="120"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="2dN-Bd-tdy" id="7IN-F3-Mr6">
<rect key="frame" x="0.0" y="0.0" width="375" height="120"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="oiE-tt-nc2">
<rect key="frame" x="15" y="18" width="82" height="82"/>
<constraints>
<constraint firstAttribute="width" constant="82" id="4j0-PU-CmN"/>
<constraint firstAttribute="height" constant="82" id="iqE-Lc-FOj"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="15" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VhU-1t-AaI">
<rect key="frame" x="118" y="29" width="42" height="71"/>
<constraints>
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="242" id="GvN-JY-HZy"/>
<constraint firstAttribute="height" constant="71" id="fdd-WH-81G"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</label>
</subviews>
<constraints>
<constraint firstItem="oiE-tt-nc2" firstAttribute="leading" secondItem="7IN-F3-Mr6" secondAttribute="leadingMargin" id="1be-ak-AH1"/>
<constraint firstItem="oiE-tt-nc2" firstAttribute="top" secondItem="7IN-F3-Mr6" secondAttribute="topMargin" constant="7" id="EKn-ST-LDX"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="VhU-1t-AaI" secondAttribute="trailing" constant="20" symbolic="YES" id="t8i-V9-T4Z"/>
<constraint firstItem="VhU-1t-AaI" firstAttribute="leading" secondItem="oiE-tt-nc2" secondAttribute="trailing" constant="21" id="urT-3a-ebN"/>
<constraint firstItem="VhU-1t-AaI" firstAttribute="top" secondItem="7IN-F3-Mr6" secondAttribute="topMargin" constant="18" id="vkM-SY-4CT"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</tableViewCellContentView>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="avatar" destination="oiE-tt-nc2" id="Dkh-R5-Qhu"/>
<outlet property="label1" destination="VhU-1t-AaI" id="kUW-HV-KrD"/>
</connections>
</tableViewCell>
</prototypes>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="dataSource" destination="BYZ-38-t0r" id="Hxi-nC-gbY"/>
</connections>
</tableView>
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Rcz-zh-K7e">
<rect key="frame" x="0.0" y="623" width="375" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="Hxm-Ox-MuO"/>
</constraints>
<items>
<barButtonItem title="Hide" id="JKW-cs-E2w">
<connections>
<action selector="hideSkeletons:" destination="BYZ-38-t0r" id="goG-1b-3fb"/>
</connections>
</barButtonItem>
<barButtonItem style="plain" systemItem="flexibleSpace" id="ygH-1d-q1v"/>
<barButtonItem title="Show" id="y9U-4k-FHt">
<connections>
<action selector="show:" destination="BYZ-38-t0r" id="QVc-d0-HrF"/>
</connections>
</barButtonItem>
</items>
</toolbar>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="F9K-jU-100">
<rect key="frame" x="0.0" y="64" width="375" height="186"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" misplaced="YES" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="e9V-mk-xH0">
<rect key="frame" x="45" y="79" width="287" height="78"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="78" id="gF5-G1-lKI"/>
</constraints>
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </string>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</textView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="e9V-mk-xH0" firstAttribute="leading" secondItem="F9K-jU-100" secondAttribute="leading" constant="45" id="HvQ-HY-zYU"/>
<constraint firstItem="e9V-mk-xH0" firstAttribute="centerX" secondItem="F9K-jU-100" secondAttribute="centerX" constant="1" id="KcB-tG-NXa"/>
<constraint firstAttribute="height" constant="186" id="MIj-xq-gr1"/>
<constraint firstItem="e9V-mk-xH0" firstAttribute="top" secondItem="F9K-jU-100" secondAttribute="top" constant="79" id="Wcx-nZ-1lR"/>
<constraint firstAttribute="trailing" secondItem="e9V-mk-xH0" secondAttribute="trailing" constant="43" id="XbU-Og-rht"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="F9K-jU-100" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="1es-nY-bd3"/>
<constraint firstItem="Rcz-zh-K7e" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="7uf-M9-Ibb"/>
<constraint firstItem="UCB-SP-lQk" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="8pV-7S-yWG"/>
<constraint firstItem="F9K-jU-100" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="A3E-iv-1qp"/>
<constraint firstItem="Rcz-zh-K7e" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="Obb-Zg-0ah"/>
<constraint firstItem="Rcz-zh-K7e" firstAttribute="bottom" secondItem="6Tk-OE-BBY" secondAttribute="bottom" id="Qvg-qe-8CM"/>
<constraint firstItem="F9K-jU-100" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="VLb-cX-ZHC"/>
<constraint firstItem="UCB-SP-lQk" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="X0V-C2-fPc"/>
<constraint firstItem="UCB-SP-lQk" firstAttribute="top" secondItem="F9K-jU-100" secondAttribute="bottom" id="nNg-3V-Zz9"/>
<constraint firstItem="Rcz-zh-K7e" firstAttribute="top" secondItem="UCB-SP-lQk" secondAttribute="bottom" id="qKg-pA-2kg"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
<navigationItem key="navigationItem" id="BEI-dU-kr2"/>
<connections>
<outlet property="tableview" destination="UCB-SP-lQk" id="XV0-KX-lAN"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-458.39999999999998" y="103.89805097451276"/>
</scene>
</scenes>
</document>
+49
View File
@@ -0,0 +1,49 @@
//
// Cell.swift
// SkeletonViewExample
//
// Created by Juanpe Catalán on 03/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
class Cell: UITableViewCell {
@IBOutlet weak var avatar: UIImageView!
@IBOutlet weak var label1: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
func drawCell(row: Int) {
let urlString = "https://randomuser.me/api/portraits/men/\(row+10).jpg"
avatar.downloadedFrom(link: urlString)
}
}
extension UIImageView {
func downloadedFrom(url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {
contentMode = mode
showAnimatedSkeleton()
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() { () -> Void in
self.image = image
self.hideSkeleton()
}
}.resume()
}
func downloadedFrom(link: String, contentMode mode: UIViewContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
downloadedFrom(url: url, contentMode: mode)
}
}
+97
View File
@@ -0,0 +1,97 @@
//
// ViewController.swift
// SkeletonViewExample
//
// Created by Juanpe Catalán on 02/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
import SkeletonView
class ViewController: UIViewController {
@IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
view.isSkeletonable = true
// view.showAnimatedGradientSkeleton()
// view.showAnimatedSkeleton(usingColor: UIColor.sunFlower)
// view.showAnimatedGradientSkeleton(usingGradient: SkeletonGradient(baseColor: UIColor.red, secundaryColor: UIColor.green))
// view.showAnimatedGradientSkeleton(usingGradient: SkeletonGradient(baseColor: UIColor.belizeHole))
// sendrequest()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// view.showAnimatedGradientSkeleton()
view.showSkeleton()
}
func sendrequest() {
let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if let data = data {
do {
// Convert the data to JSON
let jsonSerialized = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
if let json = jsonSerialized, let url = json["url"], let explanation = json["explanation"] {
print(url)
print(explanation)
DispatchQueue.main.async {
// self.view.hideSkeleton(reloadDataAfter: false)
// self.tableview.reloadData()
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
} else if let error = error {
print(error.localizedDescription)
}
}
task.resume()
}
@IBAction func hideSkeletons(_ sender: Any) {
view.hideSkeleton()
}
@IBAction func show(_ sender: Any) {
// view.showAnimatedSkeleton(usingColor: UIColor.clouds)
view.showGradientSkeleton()
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.view.startSkeletonAnimation()
}
// view.showGradientSkeleton(withDirection: .rightLeft)
// view.showAnimatedGradientSkeleton()
}
}
extension ViewController: SkeletonUITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 9
}
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "CellIdentifier"
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellIdentifier", for: indexPath) as! Cell
cell.label1.text = "cell => \(indexPath.row)"
cell.drawCell(row: indexPath.row)
return cell
}
}
+22
View File
@@ -0,0 +1,22 @@
The MIT License (MIT)
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.
+21
View File
@@ -0,0 +1,21 @@
// swift-tools-version:4.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "SkeletonView",
products: [
.library(
name: "SkeletonView",
targets: ["SkeletonView"]),
],
targets: [
.target(
name: "SkeletonView",
dependencies: []),
.testTarget(
name: "SkeletonViewTests",
dependencies: ["SkeletonView"]),
]
)
+19
View File
@@ -0,0 +1,19 @@
Pod::Spec.new do |s|
s.name = "SkeletonView"
s.version = "0.1"
s.summary = ""
s.description = <<-DESC
Your description here.
DESC
s.homepage = ""
s.license = { :type => "MIT", :file => "LICENSE" }
s.author = { "Juanpe Catalán" => "juanpecm@gmail.com" }
s.social_media_url = ""
s.ios.deployment_target = "8.0"
s.osx.deployment_target = "10.9"
s.watchos.deployment_target = "2.0"
s.tvos.deployment_target = "9.0"
s.source = { :git => ".git", :tag => s.version.to_s }
s.source_files = "Sources/**/*"
s.frameworks = "Foundation"
end
+589
View File
@@ -0,0 +1,589 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 47;
objects = {
/* Begin PBXBuildFile section */
8933C7851EB5B820000D00A4 /* SkeletonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* SkeletonView.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 */; };
F5307E321FB0F42F00EE67C5 /* RecursiveProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */; };
F5307E371FB1076E00EE67C5 /* SkeletonUITableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E361FB1076E00EE67C5 /* SkeletonUITableViewDataSource.swift */; };
F5307E391FB1078E00EE67C5 /* SkeletonUICollectionViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E381FB1078E00EE67C5 /* SkeletonUICollectionViewDataSource.swift */; };
F5307E3B1FB123C100EE67C5 /* ContainsMultilineText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */; };
F56B94461FAE20AF0095662F /* PrepareForSkeletonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */; };
F5F622411FAC6E31007C062A /* UIColor+Skeleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F622401FAC6E31007C062A /* UIColor+Skeleton.swift */; };
F5F622431FAC81FD007C062A /* CALayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F622421FAC81FD007C062A /* CALayer+Extensions.swift */; };
F5F622451FACA338007C062A /* Cell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F622441FACA338007C062A /* Cell.swift */; };
F5F899D01FAA6A4D002E8FDA /* UIView+IBInspectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */; };
F5F899D21FAB9630002E8FDA /* AssociationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F899D11FAB9630002E8FDA /* AssociationPolicy.swift */; };
F5F899E91FAB9D2B002E8FDA /* SkeletonLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F899E81FAB9D2B002E8FDA /* SkeletonLayer.swift */; };
F5F899EB1FAB9DA3002E8FDA /* SkeletonCollectionDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F899EA1FAB9DA3002E8FDA /* SkeletonCollectionDataSource.swift */; };
F5F899ED1FAB9F04002E8FDA /* CollectionSkeletonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F899EC1FAB9F04002E8FDA /* CollectionSkeletonProtocol.swift */; };
F5F899F51FABA607002E8FDA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F899F41FABA607002E8FDA /* AppDelegate.swift */; };
F5F899F71FABA607002E8FDA /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F899F61FABA607002E8FDA /* ViewController.swift */; };
F5F899FA1FABA607002E8FDA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F5F899F81FABA607002E8FDA /* Main.storyboard */; };
F5F899FC1FABA607002E8FDA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F5F899FB1FABA607002E8FDA /* Assets.xcassets */; };
F5F899FF1FABA607002E8FDA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F5F899FD1FABA607002E8FDA /* LaunchScreen.storyboard */; };
F5F89A051FABA614002E8FDA /* SkeletonView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* SkeletonView.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
52D6D97C1BEFF229002C0205 /* SkeletonView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SkeletonView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8933C7841EB5B820000D00A4 /* SkeletonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkeletonView.swift; sourceTree = "<group>"; };
AD2FAA261CD0B6D800659CF4 /* SkeletonView.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = SkeletonView.plist; 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>"; };
F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecursiveProtocol.swift; sourceTree = "<group>"; };
F5307E361FB1076E00EE67C5 /* SkeletonUITableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonUITableViewDataSource.swift; sourceTree = "<group>"; };
F5307E381FB1078E00EE67C5 /* SkeletonUICollectionViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonUICollectionViewDataSource.swift; sourceTree = "<group>"; };
F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainsMultilineText.swift; sourceTree = "<group>"; };
F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrepareForSkeletonProtocol.swift; sourceTree = "<group>"; };
F5F622401FAC6E31007C062A /* UIColor+Skeleton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Skeleton.swift"; sourceTree = "<group>"; };
F5F622421FAC81FD007C062A /* CALayer+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+Extensions.swift"; sourceTree = "<group>"; };
F5F622441FACA338007C062A /* Cell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cell.swift; sourceTree = "<group>"; };
F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+IBInspectable.swift"; sourceTree = "<group>"; };
F5F899D11FAB9630002E8FDA /* AssociationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssociationPolicy.swift; sourceTree = "<group>"; };
F5F899E81FAB9D2B002E8FDA /* SkeletonLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonLayer.swift; sourceTree = "<group>"; };
F5F899EA1FAB9DA3002E8FDA /* SkeletonCollectionDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkeletonCollectionDataSource.swift; sourceTree = "<group>"; };
F5F899EC1FAB9F04002E8FDA /* CollectionSkeletonProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionSkeletonProtocol.swift; sourceTree = "<group>"; };
F5F899F21FABA607002E8FDA /* SkeletonViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SkeletonViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
F5F899F41FABA607002E8FDA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
F5F899F61FABA607002E8FDA /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
F5F899F91FABA607002E8FDA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
F5F899FB1FABA607002E8FDA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
F5F899FE1FABA607002E8FDA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
F5F89A001FABA607002E8FDA /* SkeletonViewExampleInfo.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = SkeletonViewExampleInfo.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
52D6D9781BEFF229002C0205 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
F5F899EF1FABA607002E8FDA /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F5F89A051FABA614002E8FDA /* SkeletonView.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
52D6D9721BEFF229002C0205 = {
isa = PBXGroup;
children = (
F5F899F31FABA607002E8FDA /* Example */,
8933C7811EB5B7E0000D00A4 /* Sources */,
52D6D99C1BEFF38C002C0205 /* Configs */,
52D6D97D1BEFF229002C0205 /* Products */,
F5F89A041FABA614002E8FDA /* Frameworks */,
);
sourceTree = "<group>";
};
52D6D97D1BEFF229002C0205 /* Products */ = {
isa = PBXGroup;
children = (
52D6D97C1BEFF229002C0205 /* SkeletonView.framework */,
F5F899F21FABA607002E8FDA /* SkeletonViewExample.app */,
);
name = Products;
sourceTree = "<group>";
};
52D6D99C1BEFF38C002C0205 /* Configs */ = {
isa = PBXGroup;
children = (
F5F89A061FABA725002E8FDA /* Example */,
DD7502721C68FC1B006590AF /* Frameworks */,
);
path = Configs;
sourceTree = "<group>";
};
8933C7811EB5B7E0000D00A4 /* Sources */ = {
isa = PBXGroup;
children = (
F5307E331FB1068500EE67C5 /* Collections */,
F5307E341FB106A500EE67C5 /* Extensions */,
F5307E351FB106BF00EE67C5 /* Helpers */,
F5307E2F1FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift */,
F5307E2B1FAF6BC900EE67C5 /* SkeletonGradient.swift */,
F5F899E81FAB9D2B002E8FDA /* SkeletonLayer.swift */,
8933C7841EB5B820000D00A4 /* SkeletonView.swift */,
);
path = Sources;
sourceTree = "<group>";
};
DD7502721C68FC1B006590AF /* Frameworks */ = {
isa = PBXGroup;
children = (
AD2FAA261CD0B6D800659CF4 /* SkeletonView.plist */,
);
name = Frameworks;
sourceTree = "<group>";
};
F5307E331FB1068500EE67C5 /* Collections */ = {
isa = PBXGroup;
children = (
F5F899EC1FAB9F04002E8FDA /* CollectionSkeletonProtocol.swift */,
F5F899EA1FAB9DA3002E8FDA /* SkeletonCollectionDataSource.swift */,
F5307E381FB1078E00EE67C5 /* SkeletonUICollectionViewDataSource.swift */,
F5307E361FB1076E00EE67C5 /* SkeletonUITableViewDataSource.swift */,
);
path = Collections;
sourceTree = "<group>";
};
F5307E341FB106A500EE67C5 /* Extensions */ = {
isa = PBXGroup;
children = (
F5F622421FAC81FD007C062A /* CALayer+Extensions.swift */,
F5F622401FAC6E31007C062A /* UIColor+Skeleton.swift */,
F5307E2D1FB0E5E400EE67C5 /* UIView+Frame.swift */,
F5F899CF1FAA6A4D002E8FDA /* UIView+IBInspectable.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
F5307E351FB106BF00EE67C5 /* Helpers */ = {
isa = PBXGroup;
children = (
F5F899D11FAB9630002E8FDA /* AssociationPolicy.swift */,
F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */,
F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */,
F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */,
);
path = Helpers;
sourceTree = "<group>";
};
F5F899F31FABA607002E8FDA /* Example */ = {
isa = PBXGroup;
children = (
F5F899F41FABA607002E8FDA /* AppDelegate.swift */,
F5F899F61FABA607002E8FDA /* ViewController.swift */,
F5F622441FACA338007C062A /* Cell.swift */,
F5F899F81FABA607002E8FDA /* Main.storyboard */,
F5F899FB1FABA607002E8FDA /* Assets.xcassets */,
F5F899FD1FABA607002E8FDA /* LaunchScreen.storyboard */,
);
path = Example;
sourceTree = "<group>";
};
F5F89A041FABA614002E8FDA /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
F5F89A061FABA725002E8FDA /* Example */ = {
isa = PBXGroup;
children = (
F5F89A001FABA607002E8FDA /* SkeletonViewExampleInfo.plist */,
);
name = Example;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
52D6D9791BEFF229002C0205 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
52D6D97B1BEFF229002C0205 /* SkeletonView-iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "SkeletonView-iOS" */;
buildPhases = (
52D6D9771BEFF229002C0205 /* Sources */,
52D6D9781BEFF229002C0205 /* Frameworks */,
52D6D9791BEFF229002C0205 /* Headers */,
52D6D97A1BEFF229002C0205 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "SkeletonView-iOS";
productName = SkeletonView;
productReference = 52D6D97C1BEFF229002C0205 /* SkeletonView.framework */;
productType = "com.apple.product-type.framework";
};
F5F899F11FABA607002E8FDA /* SkeletonViewExample */ = {
isa = PBXNativeTarget;
buildConfigurationList = F5F89A011FABA607002E8FDA /* Build configuration list for PBXNativeTarget "SkeletonViewExample" */;
buildPhases = (
F5F899EE1FABA607002E8FDA /* Sources */,
F5F899EF1FABA607002E8FDA /* Frameworks */,
F5F899F01FABA607002E8FDA /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = SkeletonViewExample;
productName = SkeletonViewExample;
productReference = F5F899F21FABA607002E8FDA /* SkeletonViewExample.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
52D6D9731BEFF229002C0205 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0910;
LastUpgradeCheck = 0900;
ORGANIZATIONNAME = SkeletonView;
TargetAttributes = {
52D6D97B1BEFF229002C0205 = {
CreatedOnToolsVersion = 7.1;
LastSwiftMigration = 0800;
};
F5F899F11FABA607002E8FDA = {
CreatedOnToolsVersion = 9.1;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "SkeletonView" */;
compatibilityVersion = "Xcode 6.3";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 52D6D9721BEFF229002C0205;
productRefGroup = 52D6D97D1BEFF229002C0205 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
52D6D97B1BEFF229002C0205 /* SkeletonView-iOS */,
F5F899F11FABA607002E8FDA /* SkeletonViewExample */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
52D6D97A1BEFF229002C0205 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
F5F899F01FABA607002E8FDA /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F5F899FF1FABA607002E8FDA /* LaunchScreen.storyboard in Resources */,
F5F899FC1FABA607002E8FDA /* Assets.xcassets in Resources */,
F5F899FA1FABA607002E8FDA /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
52D6D9771BEFF229002C0205 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F5F899D01FAA6A4D002E8FDA /* UIView+IBInspectable.swift in Sources */,
F5307E371FB1076E00EE67C5 /* SkeletonUITableViewDataSource.swift in Sources */,
8933C7851EB5B820000D00A4 /* SkeletonView.swift in Sources */,
F5307E301FB0EC9D00EE67C5 /* SkeletonDefaultConfig.swift in Sources */,
F5307E321FB0F42F00EE67C5 /* RecursiveProtocol.swift in Sources */,
F5F622431FAC81FD007C062A /* CALayer+Extensions.swift in Sources */,
F5F899ED1FAB9F04002E8FDA /* CollectionSkeletonProtocol.swift in Sources */,
F5307E2C1FAF6BC900EE67C5 /* SkeletonGradient.swift in Sources */,
F5F899EB1FAB9DA3002E8FDA /* SkeletonCollectionDataSource.swift in Sources */,
F5F622411FAC6E31007C062A /* UIColor+Skeleton.swift in Sources */,
F5F899D21FAB9630002E8FDA /* AssociationPolicy.swift in Sources */,
F56B94461FAE20AF0095662F /* PrepareForSkeletonProtocol.swift in Sources */,
F5307E391FB1078E00EE67C5 /* SkeletonUICollectionViewDataSource.swift in Sources */,
F5307E3B1FB123C100EE67C5 /* ContainsMultilineText.swift in Sources */,
F5F899E91FAB9D2B002E8FDA /* SkeletonLayer.swift in Sources */,
F5307E2E1FB0E5E400EE67C5 /* UIView+Frame.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F5F899EE1FABA607002E8FDA /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F5F899F71FABA607002E8FDA /* ViewController.swift in Sources */,
F5F899F51FABA607002E8FDA /* AppDelegate.swift in Sources */,
F5F622451FACA338007C062A /* Cell.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
F5F899F81FABA607002E8FDA /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
F5F899F91FABA607002E8FDA /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
F5F899FD1FABA607002E8FDA /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
F5F899FE1FABA607002E8FDA /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
52D6D98E1BEFF229002C0205 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
52D6D98F1BEFF229002C0205 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
52D6D9911BEFF229002C0205 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Configs/SkeletonView.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = "com.SkeletonView.SkeletonView-iOS";
PRODUCT_NAME = SkeletonView;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
};
name = Debug;
};
52D6D9921BEFF229002C0205 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Configs/SkeletonView.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.SkeletonView.SkeletonView-iOS";
PRODUCT_NAME = SkeletonView;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
};
name = Release;
};
F5F89A021FABA607002E8FDA /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "$(SRCROOT)/Configs/SkeletonViewExampleInfo.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_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
F5F89A031FABA607002E8FDA /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "$(SRCROOT)/Configs/SkeletonViewExampleInfo.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;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "SkeletonView" */ = {
isa = XCConfigurationList;
buildConfigurations = (
52D6D98E1BEFF229002C0205 /* Debug */,
52D6D98F1BEFF229002C0205 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "SkeletonView-iOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
52D6D9911BEFF229002C0205 /* Debug */,
52D6D9921BEFF229002C0205 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F5F89A011FABA607002E8FDA /* Build configuration list for PBXNativeTarget "SkeletonViewExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F5F89A021FABA607002E8FDA /* Debug */,
F5F89A031FABA607002E8FDA /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 52D6D9731BEFF229002C0205 /* Project object */;
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "52D6D97B1BEFF229002C0205"
BuildableName = "SkeletonView.framework"
BlueprintName = "SkeletonView-iOS"
ReferencedContainer = "container:SkeletonView.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "52D6D97B1BEFF229002C0205"
BuildableName = "SkeletonView.framework"
BlueprintName = "SkeletonView-iOS"
ReferencedContainer = "container:SkeletonView.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "52D6D97B1BEFF229002C0205"
BuildableName = "SkeletonView.framework"
BlueprintName = "SkeletonView-iOS"
ReferencedContainer = "container:SkeletonView.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,79 @@
//
// CollectionSkeleton.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 02/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
extension UIView {
func addDummyDataSourceIfNeeded() {
guard let collection = self as? CollectionSkeleton else { return }
collection.addDummyDataSource()
collection.disableScrolling()
}
func removeDummyDataSourceIfNeeded(reloadAfter reload: Bool = true) {
guard let collection = self as? CollectionSkeleton else { return }
collection.removeDummyDataSource(reloadAfter: reload)
collection.enableScrolling()
}
}
protocol CollectionSkeleton {
var skeletonDataSource: SkeletonCollectionDataSource? { get set }
func addDummyDataSource()
func removeDummyDataSource(reloadAfter: Bool)
func disableScrolling()
func enableScrolling()
}
private enum AssociatedKeys {
static var dummyDataSource = "dummyDataSource"
}
extension CollectionSkeleton where Self: UIScrollView {
func addDummyDataSource() {}
func removeDummyDataSource(reloadAfter: Bool) {}
func disableScrolling() { isScrollEnabled = false }
func enableScrolling() { isScrollEnabled = true }
}
extension UITableView: CollectionSkeleton {
var skeletonDataSource: SkeletonCollectionDataSource? {
get { return objc_getAssociatedObject(self, &AssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
set {
objc_setAssociatedObject(self, &AssociatedKeys.dummyDataSource, newValue, AssociationPolicy.retain.objc)
self.dataSource = newValue
}
}
func addDummyDataSource() {
guard let originalDataSource = self.dataSource as? SkeletonUITableViewDataSource,
!(originalDataSource is SkeletonCollectionDataSource)
else { return }
let dataSource = SkeletonCollectionDataSource(tableViewDataSource: originalDataSource)
self.skeletonDataSource = dataSource
reloadData()
}
func removeDummyDataSource(reloadAfter: Bool) {
guard let dataSource = self.dataSource as? SkeletonCollectionDataSource else { return }
self.skeletonDataSource = nil
self.dataSource = dataSource.originalTableViewDataSource
if reloadAfter { self.reloadData() }
}
}
extension UICollectionView: CollectionSkeleton {
var skeletonDataSource: SkeletonCollectionDataSource? {
get { return objc_getAssociatedObject(self, &AssociatedKeys.dummyDataSource) as? SkeletonCollectionDataSource }
set { objc_setAssociatedObject(self, &AssociatedKeys.dummyDataSource, newValue, AssociationPolicy.retain.objc) }
}
}
@@ -0,0 +1,57 @@
//
// SkeletonCollectionDataSource.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 02/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
public typealias ReusableCellIdentifier = String
class SkeletonCollectionDataSource: NSObject {
weak var originalTableViewDataSource: SkeletonUITableViewDataSource?
weak var originalCollectionViewDataSource: UICollectionViewDataSource?
convenience init(tableViewDataSource: SkeletonUITableViewDataSource? = nil, collectionViewDataSource: UICollectionViewDataSource? = nil) {
self.init()
self.originalTableViewDataSource = tableViewDataSource
self.originalCollectionViewDataSource = collectionViewDataSource
}
}
// MARK: - UITableViewDataSource
extension SkeletonCollectionDataSource: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return originalTableViewDataSource?.numSections(in:tableView) ?? 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return originalTableViewDataSource?.collectionSkeletonView(tableView, numberOfRowsInSection:section) ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = originalTableViewDataSource?.collectionSkeletonView(tableView, cellIdenfierForRowAt: indexPath) ?? ""
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
return cell
}
}
// MARK: - UICollectionViewDataSource
extension SkeletonCollectionDataSource: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return UICollectionViewCell()
}
}
@@ -0,0 +1,11 @@
//
// SkeletonUICollectionViewDataSource.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 06/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
protocol SkeletonUICollectionViewDataSource: UICollectionViewDataSource {}
@@ -0,0 +1,24 @@
//
// SkeletonUITableViewDataSource.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 06/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
public protocol SkeletonUITableViewDataSource: UITableViewDataSource {
func numSections(in collectionSkeletonView: UITableView) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, cellIdenfierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
}
public extension SkeletonUITableViewDataSource {
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Int(ceil(skeletonView.frame.height/skeletonView.rowHeight))
}
func numSections(in collectionSkeletonView: UITableView) -> Int { return 1 }
}
+103
View File
@@ -0,0 +1,103 @@
//
// SkeletonLayer+Animations.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 03/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
extension CALayer {
@objc func tint(withColors colors: [UIColor]) {
recursiveSearch(inArray: skeletonSublayers,
leafBlock: { backgroundColor = colors.first?.cgColor }) {
$0.tint(withColors: colors)
}
}
}
extension CAGradientLayer {
override func tint(withColors colors: [UIColor]) {
recursiveSearch(inArray: skeletonSublayers,
leafBlock: { self.colors = colors.map { $0.cgColor } }) {
$0.tint(withColors: colors)
}
}
}
// MARK: Skeleton sublayers
extension CALayer {
static let skeletonSubLayersName = "SkeletonSubLayersName"
var skeletonSublayers: [CALayer] {
return sublayers?.filter { $0.name == CALayer.skeletonSubLayersName } ?? [CALayer]()
}
func addMultilinesLayers(lines: Int, type: SkeletonType) {
let numberOfSublayers = calculateNumLines(maxLines: lines)
for index in 0..<numberOfSublayers {
let layer = SkeletonLayerFactory().makeMultilineLayer(withType: type, for: index, width: Int(bounds.width))
addSublayer(layer)
}
}
private func calculateNumLines(maxLines: Int) -> Int {
let spaceRequitedForEachLine = SkeletonDefaultConfig.multilineHeight + SkeletonDefaultConfig.multilineSpacing
var numberOfSublayers = Int(round(CGFloat(bounds.height)/CGFloat(spaceRequitedForEachLine)))
if maxLines != 0, maxLines <= numberOfSublayers { numberOfSublayers = maxLines }
return numberOfSublayers
}
}
// MARK: Animations
public extension CALayer {
enum Animations {
static var pulse: CAAnimation {
let pulseAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
pulseAnimation.duration = 1
pulseAnimation.fromValue = 1
pulseAnimation.toValue = 0.7
pulseAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
pulseAnimation.autoreverses = true
pulseAnimation.repeatCount = .infinity
return pulseAnimation
}
static var sliding: CAAnimation {
let startPointAnim = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.startPoint))
startPointAnim.fromValue = NSValue(cgPoint:CGPoint(x: -1, y: 0.5))
startPointAnim.toValue = NSValue(cgPoint:CGPoint(x:1, y: 0.5))
let endPointAnim = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.endPoint))
endPointAnim.fromValue = NSValue(cgPoint:CGPoint(x: 0, y: 0.5))
endPointAnim.toValue = NSValue(cgPoint:CGPoint(x:2, y: 0.5))
let animGroup = CAAnimationGroup()
animGroup.animations = [startPointAnim, endPointAnim]
animGroup.duration = 1.5
animGroup.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
animGroup.repeatCount = .infinity
return animGroup
}
}
func playAnimation(_ anim: SkeletonLayerAnimation, key: String) {
recursiveSearch(inArray: skeletonSublayers,
leafBlock: { add(anim(), forKey: key) }) {
$0.playAnimation(anim, key: key)
}
}
func stopAnimation(forKey key: String) {
recursiveSearch(inArray: skeletonSublayers,
leafBlock: { removeAnimation(forKey: key) }) {
$0.stopAnimation(forKey: key)
}
}
}
+63
View File
@@ -0,0 +1,63 @@
//
// UIColor+Skeleton.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 03/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
extension UIColor {
convenience init(_ hex: UInt) {
self.init(
red: CGFloat((hex & 0xFF0000) >> 16) / 255.0,
green: CGFloat((hex & 0x00FF00) >> 8) / 255.0,
blue: CGFloat(hex & 0x0000FF) / 255.0,
alpha: CGFloat(1.0)
)
}
public var lighter: UIColor {
return adjust(by: 1.2)
}
public var darker: UIColor {
return adjust(by: 0.96)
}
func adjust(by percent: CGFloat) -> UIColor {
var h: CGFloat = 0, s: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
getHue(&h, saturation: &s, brightness: &b, alpha: &a)
return UIColor(hue: h, saturation: s, brightness: b * percent, alpha: a)
}
func makeGradient() -> [UIColor] {
return [self, self.darker, self]
}
}
public extension UIColor {
static var greenSea = UIColor(0x16a085)
static var turquoise = UIColor(0x1abc9c)
static var emerald = UIColor(0x2ecc71)
static var peterRiver = UIColor(0x3498db)
static var amethyst = UIColor(0x9b59b6)
static var wetAsphalt = UIColor(0x34495e)
static var nephritis = UIColor(0x27ae60)
static var belizeHole = UIColor(0x2980b9)
static var wisteria = UIColor(0x8e44ad)
static var midnightBlue = UIColor(0x2c3e50)
static var sunFlower = UIColor(0xf1c40f)
static var carrot = UIColor(0xe67e22)
static var alizarin = UIColor(0xe74c3c)
static var clouds = UIColor(0xecf0f1)
static var concrete = UIColor(0x95a5a6)
static var flatOrange = UIColor(0xf39c12)
static var pumpkin = UIColor(0xd35400)
static var pomegranate = UIColor(0xc0392b)
static var silver = UIColor(0xbdc3c7)
static var asbestos = UIColor(0x7f8c8d)
}
+40
View File
@@ -0,0 +1,40 @@
//
// UIView+Frame.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 06/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
// MARK: Frame
extension UIView {
var maxBoundsEstimated: CGRect {
return CGRect(origin: .zero, size: maxSizeEstimated)
}
var maxSizeEstimated: CGSize {
return CGSize(width: maxWidthEstimated, height: maxHeightEstimated)
}
var maxWidthEstimated: CGFloat {
let constraintsWidth = constraints.filter({ $0.firstAttribute == NSLayoutAttribute.width })
return max(between: frame.size.width, andContantsOf: constraintsWidth)
}
var maxHeightEstimated: CGFloat {
let constraintsHeight = constraints.filter({ $0.firstAttribute == NSLayoutAttribute.height })
return max(between: frame.size.height, andContantsOf: constraintsHeight)
}
private func max(between value: CGFloat, andContantsOf constraints: [NSLayoutConstraint]) -> CGFloat {
let max = constraints.reduce(value, { max, constraint in
var tempMax = max
if constraint.constant > tempMax { tempMax = constraint.constant }
return tempMax
})
return max
}
}
@@ -0,0 +1,52 @@
//
// 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"
}
public extension UIView {
@IBInspectable
var isSkeletonable: Bool {
get { return skeletonable }
set { skeletonable = newValue }
}
var isSkeletonActive: Bool {
return status == .on
}
}
extension UIView {
enum Status {
case on
case off
}
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) }
}
private var skeletonable: Bool! {
get { return objc_getAssociatedObject(self, &AssociatedKeys.skeletonable) as? Bool ?? false }
set { objc_setAssociatedObject(self, &AssociatedKeys.skeletonable, newValue, AssociationPolicy.retain.objc) }
}
}
+23
View File
@@ -0,0 +1,23 @@
//
// AssociationPolicy.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 02/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import Foundation
//Partially copy/pasted from https://github.com/jameslintaylor/AssociatedObjects/blob/master/AssociatedObjects/AssociatedObjects.swift
enum AssociationPolicy: UInt {
// raw values map to objc_AssociationPolicy's raw values
case assign = 0
case copy = 771
case copyNonatomic = 3
case retain = 769
case retainNonatomic = 1
var objc: objc_AssociationPolicy {
return objc_AssociationPolicy(rawValue: rawValue)!
}
}
@@ -0,0 +1,24 @@
//
// ContainsMultilineText.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 07/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
protocol ContainsMultilineText {
var numLines: Int { get }
}
extension ContainsMultilineText {
var numLines: Int { return 0 }
}
extension UILabel: ContainsMultilineText {
var numLines: Int {
return numberOfLines
}
}
extension UITextView: ContainsMultilineText {}
@@ -0,0 +1,31 @@
//
// PrepareForSkeleton.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 04/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
protocol PrepareForSkeleton {
func prepareViewForSkeleton()
}
extension UILabel: PrepareForSkeleton {
func prepareViewForSkeleton() {
text = nil
}
}
extension UITextView: PrepareForSkeleton {
func prepareViewForSkeleton() {
text = nil
}
}
extension UIImageView: PrepareForSkeleton {
func prepareViewForSkeleton() {
image = nil
}
}
+37
View File
@@ -0,0 +1,37 @@
//
// HelperProtocols.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 06/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
typealias VoidBlock = () -> Void
typealias RecursiveBlock<T> = (T) -> Void
//MARK: Recursive
protocol Recursive {
associatedtype Element
func recursiveSearch(inArray array:[Element], leafBlock: VoidBlock, recursiveBlock: RecursiveBlock<Element>)
}
extension Recursive {
func recursiveSearch(inArray array:[Element], leafBlock: VoidBlock, recursiveBlock: RecursiveBlock<Element>) {
guard array.count > 0 else {
leafBlock()
return
}
array.forEach { recursiveBlock($0) }
}
}
extension UIView: Recursive {
typealias Element = UIView
}
extension CALayer: Recursive {
typealias Element = CALayer
}
+20
View File
@@ -0,0 +1,20 @@
//
// 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 = 15
public static let multilineSpacing = 10
}
+26
View File
@@ -0,0 +1,26 @@
//
// SkeletonGradient.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 05/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
public struct SkeletonGradient {
private var gradientColors: [UIColor]
public var colors: [UIColor] {
return gradientColors
}
public init(baseColor: UIColor, secondaryColor: UIColor? = nil) {
if let secondary = secondaryColor {
self.gradientColors = [baseColor, secondary, baseColor]
} else {
self.gradientColors = baseColor.makeGradient()
}
}
}
+96
View File
@@ -0,0 +1,96 @@
//
// SkeletonLayer.swift
// SkeletonView-iOS
//
// Created by Juanpe Catalán on 02/11/2017.
// Copyright © 2017 SkeletonView. All rights reserved.
//
import UIKit
class SkeletonLayerFactory {
func makeLayer(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: Int) -> CALayer {
let spaceRequitedForEachLine = SkeletonDefaultConfig.multilineHeight + SkeletonDefaultConfig.multilineSpacing
let layer = type.layer
layer.anchorPoint = .zero
layer.name = CALayer.skeletonSubLayersName
layer.frame = CGRect(x: 0, y: (index * spaceRequitedForEachLine), width: width, height: SkeletonDefaultConfig.multilineHeight)
return layer
}
}
public typealias SkeletonLayerAnimation = () -> CAAnimation
public enum SkeletonType {
case solid
case gradient
var layer: CALayer {
switch self {
case .solid:
return CALayer()
case .gradient:
return CAGradientLayer()
}
}
var layerAnimation: SkeletonLayerAnimation {
switch self {
case .solid:
return { CALayer.Animations.pulse }
case .gradient:
return { CALayer.Animations.sliding }
}
}
}
struct SkeletonLayer {
private var maskLayer: CALayer
private weak var holder: UIView?
var removeWhenStopped: Bool = true
var type: SkeletonType {
return maskLayer is CAGradientLayer ? .gradient : .solid
}
var contentLayer: CALayer {
return maskLayer
}
init(withType type: SkeletonType, usingColors colors: [UIColor], andSkeletonHolder holder: UIView) {
self.holder = holder
self.maskLayer = type.layer
self.maskLayer.anchorPoint = .zero
self.maskLayer.bounds = holder.maxBoundsEstimated
addMultilinesIfNeeded()
self.maskLayer.tint(withColors: colors)
}
func removeLayer() {
maskLayer.removeFromSuperlayer()
}
func addMultilinesIfNeeded() {
guard let multiLineView = holder as? ContainsMultilineText else { return }
maskLayer.addMultilinesLayers(lines: multiLineView.numLines, type: type)
}
}
extension SkeletonLayer {
func start(_ anim: SkeletonLayerAnimation? = nil) {
let animation = anim ?? type.layerAnimation
contentLayer.playAnimation(animation, key: "skeletonAnimation")
}
func stopAnimation() {
contentLayer.stopAnimation(forKey: "skeletonAnimation")
if removeWhenStopped { removeLayer() }
}
}
+123
View File
@@ -0,0 +1,123 @@
//
// 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) {
showSkeleton(withType: .solid, usingColors: [color])
}
func showGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonDefaultConfig.gradient) {
showSkeleton(withType: .gradient, usingColors: gradient.colors)
}
func showAnimatedSkeleton(usingColor color: UIColor = SkeletonDefaultConfig.tintColor, animation: SkeletonLayerAnimation? = nil) {
showSkeleton(withType: .solid, usingColors: [color], animated: true, animation: animation)
}
func showAnimatedGradientSkeleton(usingGradient gradient: SkeletonGradient = SkeletonDefaultConfig.gradient, animation: SkeletonLayerAnimation? = nil) {
showSkeleton(withType: .gradient, usingColors: gradient.colors, animated: true, animation: animation)
}
func hideSkeleton(reloadDataAfter reload: Bool = true) {
removeDummyDataSourceIfNeeded(reloadAfter: reload)
recursiveSearch(inArray: subviewsSkeletonables,
leafBlock: { removeSkeletonLayer() },
recursiveBlock: {
$0.hideSkeleton(reloadDataAfter: reload)
})
}
func startSkeletonAnimation(_ anim: SkeletonLayerAnimation? = nil) {
recursiveSearch(inArray: subviewsSkeletonables,
leafBlock: startSkeletonLayerAnimationBlock(anim)) {
$0.startSkeletonAnimation(anim)
}
}
func stopSkeletonAnimation() {
recursiveSearch(inArray: subviewsSkeletonables,
leafBlock: stopSkeletonLayerAnimationBlock) {
$0.stopSkeletonAnimation()
}
}
}
extension UIView {
func showSkeleton(withType type: SkeletonType = .solid, usingColors colors: [UIColor], animated: Bool = false, animation: SkeletonLayerAnimation? = nil) {
addDummyDataSourceIfNeeded()
recursiveSearch(inArray: subviewsSkeletonables,
leafBlock: {
guard !isSkeletonActive else { return }
(self as? PrepareForSkeleton)?.prepareViewForSkeleton()
addSkeletonLayer(withType: type, usingColors: colors, animated: animated, animation: animation)
}) {
$0.showSkeleton(withType: type, usingColors: colors, animated: animated, animation: animation)
}
}
private func startSkeletonLayerAnimationBlock(_ anim: SkeletonLayerAnimation? = nil) -> VoidBlock {
return {
guard let layer = self.skeletonLayer else { return }
layer.start(anim)
}
}
private var stopSkeletonLayerAnimationBlock: VoidBlock {
return {
guard let layer = self.skeletonLayer else { return }
layer.stopAnimation()
}
}
}
extension UIView {
@objc var subviewsSkeletonables: [UIView] {
return subviews.filter { $0.isSkeletonable }
}
}
extension UITableView {
override var subviewsSkeletonables: [UIView] {
return visibleCells.filter { $0.isSkeletonable }
}
}
extension UITableViewCell {
override var subviewsSkeletonables: [UIView] {
return contentView.subviews.filter { $0.isSkeletonable }
}
}
extension UIStackView {
override var subviewsSkeletonables: [UIView] {
return arrangedSubviews.filter { $0.isSkeletonable }
}
}
extension UIView {
func addSkeletonLayer(withType type: SkeletonType, usingColors colors: [UIColor], animated: Bool, animation: SkeletonLayerAnimation? = nil) {
self.skeletonLayer = SkeletonLayerFactory().makeLayer(withType: type, usingColors: colors, andHolder: self)
layer.insertSublayer(skeletonLayer!.contentLayer, at: UInt32.max)
if animated { skeletonLayer!.start(animation) }
status = .on
}
func removeSkeletonLayer() {
guard isSkeletonActive,
let layer = skeletonLayer else { return }
layer.stopAnimation()
skeletonLayer = nil
status = .off
}
}