Commit b4cfb21c authored by 刘俊宏's avatar 刘俊宏

pod更新

parent deeb7bb0
This source diff could not be displayed because it is too large. You can view the blob instead.
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "EAAA1AD3A8A1B59AB91319EE40752C6D"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "99313990C1D76A6D1D017868B6975CC8"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F798030A13A5E43528C860AD809CD390"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00D9B51B84230E50370AC70ADCF7F8AA"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B490E7485944099E16C9CBD79119D1D4"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E8572772A2B42B68FDF351C752B8A238"
BuildableName = "JZLocationConverter.framework"
BlueprintName = "JZLocationConverter"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E8022D22FAA6690B5E1C379C1BCE1491"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FD999FE212F29E29DEEA0A85E1DC4D4E"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6868056D761E163D10FDAF8CF1C4D9B8"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "162E649F50FEC62B61BDD87D1BD422B4"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8CEEFD1CB630CA790F0396E2E24A4C8C"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "19622742EBA51E823D6DAE3F8CDBFAD4"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6038CE6006EFBE9D905454CF01909C42"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5D8BB851D938AE8F1A461F95C1ABD69B"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D118A6A04828FD3CDA8640CD2B6796D2"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "91BFC2827E7E6E2B51AB2CFF58AC6317"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "930887923DC0E8AC2A3589A7987F2BDD"
......@@ -23,15 +23,14 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
......@@ -39,14 +38,17 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
......
......@@ -8,139 +8,101 @@
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>1</integer>
</dict>
<key>CryptoSwift.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>2</integer>
</dict>
<key>Dollar.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>3</integer>
</dict>
<key>ESTabBarController-swift.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>4</integer>
</dict>
<key>Hue.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>5</integer>
</dict>
<key>IQKeyboardManagerSwift.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>6</integer>
</dict>
<key>JZLocationConverter.xcscheme_^#shared#^_</key>
<key>JZLocationConverter.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>20</integer>
<key>isShown</key>
<false/>
</dict>
<key>Kingfisher.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>7</integer>
</dict>
<key>LGButton.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>8</integer>
</dict>
<key>MJRefresh.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>9</integer>
</dict>
<key>ObjectMapper.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>10</integer>
</dict>
<key>Pods-GeliBusinessPlatform.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>11</integer>
</dict>
<key>ReachabilitySwift.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>12</integer>
</dict>
<key>SkeletonView.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>13</integer>
</dict>
<key>SnapKit.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>14</integer>
</dict>
<key>SwiftDate.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>15</integer>
</dict>
<key>SwifterSwift.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>16</integer>
</dict>
<key>SwiftyJSON.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>17</integer>
</dict>
<key>ViewAnimator.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>18</integer>
</dict>
<key>WYAutoLayout.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>19</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
......
......@@ -5,120 +5,96 @@
<img src="https://github.com/Juanpe/SkeletonView/workflows/build/badge.svg">
</a>
<a href="https://codebeat.co/projects/github-com-juanpe-skeletonview-master"><img alt="codebeat badge" src="https://codebeat.co/badges/f854fdfd-31e5-4689-ba04-075d83653e60" /></a>
<a href="https://github.com/Juanpe/SkeletonView">
<img src="https://img.shields.io/cocoapods/p/SkeletonView.svg" alt="Platforms">
</a>
<img src="https://img.shields.io/badge/Swift-5-orange.svg" />
<a href="https://cocoapods.org/pods/SkeletonView">
<img src="https://img.shields.io/cocoapods/v/SkeletonView.svg" alt="CocoaPods" />
</a>
<a href="https://github.com/Carthage/Carthage">
<img src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat" alt="Carthage" />
</a>
<a href="https://github.com/apple/swift-package-manager">
<img src="https://img.shields.io/badge/SPM-compatible-brightgreen.svg" alt="SPM" />
</a>
<a href="https://twitter.com/JuanpeCatalan">
<img src="https://img.shields.io/badge/contact-@JuanpeCatalan-blue.svg?style=flat" alt="Twitter: @JuanpeCatalan" />
</a>
<br/>
<a href="https://gitter.im/SkeletonView/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge">
<img src="https://badges.gitter.im/SkeletonView/community.svg?style=flat" />
</a>
<a href="https://twitter.com/intent/tweet?text=Wow%20This%20library%20is%20awesome:&url=https%3A%2F%2Fgithub.com%2FJuanpe%2FSkeletonView">
<img src="https://img.shields.io/twitter/url/https/github.com/Juanpe/SkeletonView.svg?style=social" alt="License" />
</a>
<img src="http://img.shields.io/badge/dependency%20manager-swiftpm%2Bcocoapods%2Bcarthage-green" />
<img src="https://img.shields.io/badge/platforms-ios%2Btvos-green" />
<a href="https://badge.bow-swift.io/recipe?name=SkeletonView&description=An%20elegant%20way%20to%20show%20users%20that%20something%20is%20happening%20and%20also%20prepare%20them%20to%20which%20contents%20he%20is%20waiting&url=https://github.com/juanpe/skeletonview&owner=Juanpe&avatar=https://avatars0.githubusercontent.com/u/1409041?v=4&tag=1.8.7"><img src="https://raw.githubusercontent.com/bow-swift/bow-art/master/badges/nef-playgrounds-badge.svg" alt="SkeletonView Playground" style="height:20px"></a>
</p>
<p align="center">
<a href="#-features">Features</a>
<a href="#-guides">Guides</a>
<a href="#-installation">Installation</a>
<a href="#-usage">Usage</a>
<a href="#-miscellaneous">Miscellaneous</a>
<a href="#️-contributing">Contributing</a>
</p>
🌎 Translations: </br>
[🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) by [@WhatsXie](https://twitter.com/WhatsXie) </br>
[🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) by [@brunomunizaf](https://twitter.com/brunomuniz_af) </br>
[🇰🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_ko.md) by [@techinpark](https://twitter.com/techinpark)
**🌎 README is available in other languages: [🇪🇸](https://github.com/Juanpe/SkeletonView/blob/develop/README_es.md) . [🇨🇳](https://github.com/Juanpe/SkeletonView/blob/master/README_zh.md) . [🇧🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_pt-br.md) . [🇰🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_ko.md) . [🇫🇷](https://github.com/Juanpe/SkeletonView/blob/master/README_fr.md)**
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.
Today almost all apps have async processes, such as API requests, long running processes, etc. While the processes are working, usually developers place a loading view to show users that something is going on.
```SkeletonView``` has been conceived to address this need, an elegant way to show users that something is happening and also prepare them to which contents he is waiting.
**SkeletonView** has been conceived to address this need, an elegant way to show users that something is happening and also prepare them for which contents are waiting.
Enjoy it! 🙂
* [Features](#-features)
* [Guides](#-guides)
* [Installation](#-installation)
* [Cocoapods](#using-cocoapods)
* [Carthage](#using-carthage)
* [SPM](#using-swift-package-manager)
* [How to use](#-how-to-use)
* [Collections](#-collections)
* [Multiline text](#-multiline-text)
* [Custom colors](#-custom-colors)
* [Appearance](#-appearance)
* [Custom animations](#-custom-animations)
* [Transitions](#-transitions)
* [Hierarchy](#-hierarchy)
* [Debug](#-debug)
* [Documentation](#-documentation)
* [Supported OS & SDK Versions](#-supported-os--sdk-versions)
* [Next steps](#-next-steps)
* [Contributing](#-contributing)
* [Mentions](#-mentions)
* [Author](#-author)
* [License](#-license)
##
- [🌟 Features](#-features)
- [🎬 Guides](#-guides)
- [📲 Installation](#-installation)
- [🐒 Usage](#-usage)
- [🌿 Collections](#-collections)
- [🔠 Texts](#-texts)
- [🦋 Appearance](#-appearance)
- [🎨 Custom colors](#-custom-colors)
- [ 🏃‍♀️ Animations](#%EF%B8%8F-animations)
- [🏄 Transitions](#-transitions)
- [✨ Miscellaneous](#-miscellaneous)
- [❤️ Contributing](#️-contributing)
## 🌟 Features
- [x] Easy to use
- [x] All UIViews are skeletonables
- [x] Fully customizable
- [x] Universal (iPhone & iPad)
- [x] Interface Builder friendly
- [x] Simple Swift syntax
- [x] Lightweight readable codebase
* Easy to use
* All UIViews are skeletonables
* Fully customizable
* Universal (iPhone & iPad)
* Interface Builder friendly
* Simple Swift syntax
* Lightweight readable codebase
## 🎬 Guides
[<img src="Assets/thumb_getting_started.png">](https://youtu.be/75kgOhWsPNA)
| [![](https://img.youtube.com/vi/75kgOhWsPNA/maxresdefault.jpg)](https://youtu.be/75kgOhWsPNA)|[![](https://img.youtube.com/vi/MVCiM_VdxVA/maxresdefault.jpg)](https://youtu.be/MVCiM_VdxVA)|[![](https://img.youtube.com/vi/Qq3Evspeea8/maxresdefault.jpg)](https://youtu.be/Qq3Evspeea8)|[![](https://img.youtube.com/vi/ZOoPtBwDRT0/maxresdefault.jpg)](https://youtu.be/ZOoPtBwDRT0)|[![](https://img.youtube.com/vi/Zx1Pg1gPfxA/maxresdefault.jpg)](https://www.youtube.com/watch?v=Zx1Pg1gPfxA)
|:---: | :---: |:---: | :---: | :---:
|[**SkeletonView Guides - Getting started**](https://youtu.be/75kgOhWsPNA)|[**How to Create Loading View with Skeleton View in Swift 5.2**](https://youtu.be/MVCiM_VdxVA) by iKh4ever Studio|[**Create Skeleton Loading View in App (Swift 5) - Xcode 11, 2020**](https://youtu.be/Qq3Evspeea8) by iOS Academy| [**Add An Elegant Loading Animation in Swift***](https://youtu.be/ZOoPtBwDRT0) by Gary Tokman| [**Cómo crear una ANIMACIÓN de CARGA de DATOS en iOS**](https://www.youtube.com/watch?v=Zx1Pg1gPfxA) by MoureDev
## 📲 Installation
#### Using [CocoaPods](https://cocoapods.org)
## 📲 Installation
Edit your `Podfile` and specify the dependency:
* [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html):
```ruby
pod "SkeletonView"
pod 'SkeletonView'
```
#### Using [Carthage](https://github.com/carthage)
Edit your `Cartfile` and specify the dependency:
* [Carthage](https://github.com/Carthage/Carthage):
```bash
```ruby
github "Juanpe/SkeletonView"
```
#### Using [Swift Package Manager](https://github.com/apple/swift-package-manager)
Once you have your Swift package set up, adding `SkeletonView` as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`.
* [Swift Package Manager](https://swift.org/package-manager/):
```swift
dependencies: [
.package(url: "https://github.com/Juanpe/SkeletonView.git", from: "1.7.0")
]
dependencies: [
.package(url: "https://github.com/Juanpe/SkeletonView.git", from: "1.7.0")
]
```
## 🐒 How to use
## 🐒 Usage
Only **3** steps needed to use `SkeletonView`:
**1.** Import SkeletonView in proper place.
1️⃣ Import SkeletonView in proper place.
```swift
import SkeletonView
```
**2.** Now, set which views will be `skeletonables`. You achieve this in two ways:
2️⃣ Now, set which views will be `skeletonables`. You achieve this in two ways:
**Using code:**
```swift
......@@ -128,7 +104,7 @@ avatarImageView.isSkeletonable = true
![](Assets/storyboard.png)
**3.** Once you've set the views, you can show the **skeleton**. To do so, you have **4** choices:
3️⃣ Once you've set the views, you can show the **skeleton**. To do so, you have **4** choices:
```swift
(1) view.showSkeleton() // Solid
......@@ -170,41 +146,20 @@ avatarImageView.isSkeletonable = true
</tr>
</table>
> **IMPORTANT!**
>>```SkeletonView``` is recursive, so if you want show the skeleton in all skeletonable views, you only need to call the show method in the main container view. For example, with UIViewControllers
### Extra
#### Skeleton views layout
Sometimes skeleton layout may not fit your layout because the parent view bounds have changed. ~For example, rotating the device.~
You can relayout the skeleton views like so:
```swift
override func viewDidLayoutSubviews() {
view.layoutSkeletonIfNeeded()
}
```
⚠️⚠️ You shouldn't call this method. From *version 1.8.1* you don't need to call this method, the library does automatically. So, you can use this method *ONLY* in the cases when you need to update the layout of the skeleton manually.
#### Update skeleton configuration
> 📣 **IMPORTANT!**
>
> `SkeletonView` is recursive, so if you want show the skeleton in all skeletonable views, you only need to call the show method in the main container view. For example, with `UIViewControllers`.
You can change the skeleton configuration at any time like its colour, animation, etc. with the following methods:
```swift
(1) view.updateSkeleton() // Solid
(2) view.updateGradientSkeleton() // Gradient
(3) view.updateAnimatedSkeleton() // Solid animated
(4) view.updateAnimatedGradientSkeleton() // Gradient animated
```
### 🌿 Collections
Now, ```SkeletonView``` is compatible with ```UITableView``` and ```UICollectionView```.
```SkeletonView``` is compatible with ```UITableView``` and ```UICollectionView```.
#### UITableView
**UITableView**
If you want to show the skeleton in a ```UITableView```, you need to conform to ```SkeletonTableViewDataSource``` protocol.
......@@ -213,8 +168,6 @@ public protocol SkeletonTableViewDataSource: UITableViewDataSource {
func numSections(in collectionSkeletonView: UITableView) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier?
func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier?
}
```
As you can see, this protocol inherits from ```UITableViewDataSource```, so you can replace this protocol with the skeleton protocol.
......@@ -232,16 +185,6 @@ func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection s
// It calculates how many cells need to populate whole tableview
```
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier?
// Default: nil
```
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier?
// Default: nil
```
There is only one method you need to implement to let Skeleton know the cell identifier. This method doesn't have default implementation:
``` swift
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
......@@ -253,42 +196,51 @@ There is only one method you need to implement to let Skeleton know the cell ide
return "CellIdentifier"
}
```
Besides, you can skeletonize both the headers and footers. You need to conform to `SkeletonTableViewDelegate` protocol.
> **IMPORTANT!**
> If you are using resizable cells (`tableView.rowHeight = UITableViewAutomaticDimension` ), it's mandatory define the `estimatedRowHeight`.
👩🏼‍🏫 **How specify which elements are skeletonables?**
Here is an illustration that shows how you should specify which elements are skeletonables when you are using an `UITableView`:
```swift
public protocol SkeletonTableViewDelegate: UITableViewDelegate {
func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier? // default: nil
func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier? // default: nil
}
```
![](Assets/tableview_scheme.png)
> 📣 **IMPORTANT!**
>
> 1️⃣ If you are using resizable cells (**`tableView.rowHeight = UITableViewAutomaticDimension`**), it's mandatory define the **`estimatedRowHeight`**.
>
> 2️⃣ When you add elements in a **`UITableViewCell`** you should add it to **`contentView`** and not to the cell directly.
> ```swift
> self.contentView.addSubview(titleLabel) ✅
> self.addSubview(titleLabel) ❌
> ```
As you can see, we have to make skeletonable the tableview, the cell and the UI elements, but we don't need to set as skeletonable the `contentView`
#### UICollectionView
**UICollectionView**
For ```UICollectionView```, you need to conform to ```SkeletonCollectionViewDataSource``` protocol.
For `UICollectionView`, you need to conform to `SkeletonCollectionViewDataSource` protocol.
``` swift
public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
func numSections(in collectionSkeletonView: UICollectionView) -> Int
func numSections(in collectionSkeletonView: UICollectionView) -> Int // default: 1
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier
func collectionSkeletonView(_ skeletonView: UICollectionView, supplementaryViewIdentifierOfKind: String, at indexPath: IndexPath) -> ReusableCellIdentifier? // default: nil
}
```
The rest of the process is the same as ```UITableView```
### 📰 Multiline text
### 🔠 Texts
![](Assets/multilines2.png)
When using elements with text, ```SkeletonView``` draws lines to simulate text.
Besides, you can decide how many lines you want. If ```numberOfLines``` is set to zero, it will calculate how many lines needed to populate the whole skeleton and it will be drawn. Instead, if you set it to one, two or any number greater than zero, it will only draw this number of lines.
##### 🎛 Customize
You can set some properties for multilines elements.
......@@ -309,32 +261,10 @@ Or, if you prefer use **IB/Storyboard**:
![](Assets/multiline_customize.png)
### 🎨 Custom colors
You can decide which color the skeleton is tinted with. You only need to pass as a parameter the color or gradient you want.
**Using solid colors**
``` swift
view.showSkeleton(usingColor: UIColor.gray) // Solid
// or
view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0))
```
**Using gradients**
``` swift
let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
view.showGradientSkeleton(usingGradient: gradient) // Gradient
```
Besides, ```SkeletonView``` features 20 flat colors 🤙🏼
```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...```
![](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.
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
......@@ -353,7 +283,7 @@ Default values:
- *default: 0*
To get these default values you can use `SkeletonAppearance.default`. Using this property you can set the values as well:
```Swift
```swift
SkeletonAppearance.default.multilineHeight = 20
SkeletonAppearance.default.tintColor = .green
```
......@@ -365,9 +295,33 @@ You can also specifiy these line appearance properties on a per-label basis:
- **skeletonPaddingInsets**: UIEdgeInsets
### 🤓 Custom animations
### 🎨 Custom colors
You can decide which color the skeleton is tinted with. You only need to pass as a parameter the color or gradient you want.
**Using solid colors**
```swift
view.showSkeleton(usingColor: UIColor.gray) // Solid
// or
view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0))
```
**Using gradients**
``` swift
let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
view.showGradientSkeleton(usingGradient: gradient) // Gradient
```
Besides, **SkeletonView** features 20 flat colors 🤙🏼
```UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...```
![](Assets/flatcolors.png)
###### Image captured from website [https://flatuicolors.com](https://flatuicolors.com)
### 🏃‍♀️ Animations
```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.
......@@ -401,7 +355,7 @@ view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
```
```GradientDirection``` is an enum, with this cases:
```GradientDirection``` is an enum, with theses cases:
| Direction | Preview
|------- | -------
......@@ -413,14 +367,17 @@ view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
| .bottomRightTopLeft | ![](Assets/sliding_bottomRight_to_topLeft.gif)
> **😉 TRICK!**
Exist another way to create sliding animations, just using this shortcut:
>>```let animation = GradientDirection.leftToRight.slidingAnimation()```
>
> Exist another way to create sliding animations, just using this shortcut:
> ```swift
> let animation = GradientDirection.leftToRight.slidingAnimation()
> ```
### 🏄 Transitions
```SkeletonView``` has build-in transitions to **show** or **hide** the skeletons in a *smoother* way 🤙
**SkeletonView** has built-in transitions to **show** or **hide** the skeletons in a *smoother* way 🤙
To use the transition, simply add the ```transition``` parameter to your ```showSkeleton()``` or ```hideSkeleton()``` function with the transition time, like this:
......@@ -454,7 +411,11 @@ The default value is `crossDissolve(0.25)`
</table>
### 👨‍👧‍👦 Hierarchy
## ✨ Miscellaneous
**Hierarchy**
Since ```SkeletonView``` is recursive, and we want skeleton to be very efficient, we want to stop recursion as soon as possible. For this reason, you must set the container view as `Skeletonable`, because Skeleton will stop looking for `skeletonable` subviews as soon as a view is not Skeletonable, breaking then the recursion.
......@@ -465,7 +426,7 @@ In this example we have a `UIViewController` with a `ContainerView` and a `UITab
view.showSkeleton()
```
> ```ìsSkeletonable```= ☠️
> ```isSkeletonable```= ☠️
| Configuration | Result|
|:-------:|:-------:|
......@@ -476,10 +437,59 @@ view.showSkeleton()
|<img src="Assets/tableview_no_skeletonable.jpg" width="350"/> | <img src="Assets/tableview_no_skeletonable_result.png" height="350"/>|
|<img src="Assets/tableview_skeletonable.jpg" width="350"/> | <img src="Assets/tableview_skeletonable_result.png" height="350"/>|
**Hierarchy in collections**
Here is an illustration that shows how you should specify which elements are skeletonables when you are using an `UITableView`:
<img src="Assets/tableview_scheme.png" width="700px">
As you can see, we have to make skeletonable the tableview, the cell and the UI elements, but we don't need to set as skeletonable the `contentView`
**Skeleton views layout**
Sometimes skeleton layout may not fit your layout because the parent view bounds have changed. ~For example, rotating the device.~
You can relayout the skeleton views like so:
```swift
override func viewDidLayoutSubviews() {
view.layoutSkeletonIfNeeded()
}
```
> 📣 **IMPORTANT!**
>
> You shouldn't call this method. From **version 1.8.1** you don't need to call this method, the library does automatically. So, you can use this method **ONLY** in the cases when you need to update the layout of the skeleton manually.
**Update skeleton**
You can change the skeleton configuration at any time like its colour, animation, etc. with the following methods:
```swift
(1) view.updateSkeleton() // Solid
(2) view.updateGradientSkeleton() // Gradient
(3) view.updateAnimatedSkeleton() // Solid animated
(4) view.updateAnimatedGradientSkeleton() // Gradient animated
```
**Hiding views when the animation starts**
Sometimes you wanna hide some view when the animation starts, so there is a quick property that you can use to make this happen:
### 🔬 Debug
```swift
view.isHiddenWhenSkeletonIsActive = true // This works only when isSkeletonable = true
```
**Debug**
**NEW** In order to facilitate the debug tasks when something is not working fine. `SkeletonView` has some new tools.
To facilitate the debug tasks when something is not working fine. **`SkeletonView`** has some new tools.
First, `UIView` has available a new property with his skeleton info:
```swift
......@@ -501,30 +511,13 @@ Then, when the skeleton appears, you can see the view hierarchy in the Xcode con
<img src="Assets/hierarchy_output.png" />
</details>
### 📚 Documentation
Coming soon...😅
### 📋 Supported OS & SDK Versions
**Supported OS & SDK Versions**
* iOS 9.0+
* tvOS 9.0+
* Swift 5
## 📬 Next steps
* [x] Set the filling percent of the last line in multiline elements
* [x] Add more gradient animations
* [x] Supported resizable cells
* [x] CollectionView compatible
* [x] tvOS compatible
* [x] Add recovery state
* [x] Custom default appearance
* [x] Debug mode
* [x] Add animations when it shows/hides the skeletons
* [ ] Custom collections compatible
* [ ] MacOS and WatchOS compatible
## ❤️ Contributing
This is an open source project, so feel free to contribute. How?
......@@ -534,7 +527,8 @@ This is an open source project, so feel free to contribute. How?
See [all contributors](https://github.com/Juanpe/SkeletonView/graphs/contributors)
###### Project generated with [SwiftPlate](https://github.com/JohnSundell/SwiftPlate)
For more information, please read the [contributing guidelines](https://github.com/Juanpe/SkeletonView/blob/develop/CONTRIBUTING.md).
## 📢 Mentions
......@@ -555,13 +549,12 @@ See [all contributors](https://github.com/Juanpe/SkeletonView/graphs/contributor
## 👨🏻‍💻 Author
[1.1]: http://i.imgur.com/tXSoThF.png
[1]: http://www.twitter.com/JuanpeCatalan
* Juanpe Catalán [![alt text][1.1]][1]
[Juanpe Catalán](http://www.twitter.com/JuanpeCatalan)
<a class="bmc-button" target="_blank" href="https://www.buymeacoffee.com/CDou4xtIK"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy me a coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;"><span style="margin-left:5px"></span></a>
## 👮🏻 License
```
......
......@@ -22,7 +22,7 @@ class SkeletonViewAppearance: Appearance {
var tintColor: UIColor = .skeletonDefault
var gradient: SkeletonGradient = SkeletonGradient(baseColor: .skeletonDefault)
var gradient = SkeletonGradient(baseColor: .skeletonDefault)
var multilineHeight: CGFloat = 15
......
......@@ -9,25 +9,30 @@ class SkeletonLayerBuilder {
var colors: [UIColor] = []
var holder: UIView?
@discardableResult
func setSkeletonType(_ type: SkeletonType) -> SkeletonLayerBuilder {
self.skeletonType = type
return self
}
@discardableResult
func addColor(_ color: UIColor) -> SkeletonLayerBuilder {
return addColors([color])
}
@discardableResult
func addColors(_ colors: [UIColor]) -> SkeletonLayerBuilder {
self.colors.append(contentsOf: colors)
return self
}
@discardableResult
func setHolder(_ holder: UIView) -> SkeletonLayerBuilder {
self.holder = holder
return self
}
@discardableResult
func build() -> SkeletonLayer? {
guard let type = skeletonType,
let holder = holder
......
......@@ -12,41 +12,55 @@ class SkeletonMultilineLayerBuilder {
var cornerRadius: Int?
var multilineSpacing: CGFloat = SkeletonAppearance.default.multilineSpacing
var paddingInsets: UIEdgeInsets = .zero
var isRTL: Bool = false
@discardableResult
func setSkeletonType(_ type: SkeletonType) -> SkeletonMultilineLayerBuilder {
self.skeletonType = type
return self
}
@discardableResult
func setIndex(_ index: Int) -> SkeletonMultilineLayerBuilder {
self.index = index
return self
}
@discardableResult
func setHeight(_ height: CGFloat) -> SkeletonMultilineLayerBuilder {
self.height = height
return self
}
self.height = height
return self
}
@discardableResult
func setWidth(_ width: CGFloat) -> SkeletonMultilineLayerBuilder {
self.width = width
return self
}
@discardableResult
func setCornerRadius(_ radius: Int) -> SkeletonMultilineLayerBuilder {
self.cornerRadius = radius
return self
}
@discardableResult
func setMultilineSpacing(_ spacing: CGFloat) -> SkeletonMultilineLayerBuilder {
self.multilineSpacing = spacing
return self
}
@discardableResult
func setPadding(_ insets: UIEdgeInsets) -> SkeletonMultilineLayerBuilder {
self.paddingInsets = insets
return self
}
@discardableResult
func setIsRTL(_ isRTL: Bool) -> SkeletonMultilineLayerBuilder {
self.isRTL = isRTL
return self
}
func build() -> CALayer? {
guard let type = skeletonType,
......@@ -59,7 +73,11 @@ class SkeletonMultilineLayerBuilder {
let layer = type.layer
layer.anchorPoint = .zero
layer.name = CALayer.skeletonSubLayersName
layer.updateLayerFrame(for: index, size: CGSize(width: width, height: height), multilineSpacing: self.multilineSpacing, paddingInsets: paddingInsets)
layer.updateLayerFrame(for: index,
size: CGSize(width: width, height: height),
multilineSpacing: multilineSpacing,
paddingInsets: paddingInsets,
isRTL: isRTL)
layer.cornerRadius = CGFloat(radius)
layer.masksToBounds = true
......
......@@ -8,7 +8,6 @@
import UIKit
public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
func numSections(in collectionSkeletonView: UICollectionView) -> Int
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
......@@ -31,4 +30,3 @@ public extension SkeletonCollectionViewDataSource {
}
public protocol SkeletonCollectionViewDelegate: UICollectionViewDelegate { }
......@@ -11,7 +11,7 @@ import UIKit
extension UICollectionView: CollectionSkeleton {
var estimatedNumberOfRows: Int {
guard let flowlayout = collectionViewLayout as? UICollectionViewFlowLayout else { return 0 }
return Int(ceil(frame.height/flowlayout.itemSize.height))
return Int(ceil(frame.height / flowlayout.itemSize.height))
}
var skeletonDataSource: SkeletonCollectionDataSource? {
......@@ -70,7 +70,7 @@ public extension UICollectionView {
self.skeletonDataSource = dataSource
performBatchUpdates({
self.reloadData()
}) { (done) in
}) { done in
completion(done)
}
......
......@@ -63,9 +63,8 @@ extension SkeletonCollectionDataSource: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView,
viewForSupplementaryElementOfKind kind: String,
at indexPath: IndexPath) -> UICollectionReusableView {
if let viewIdentifier = originalCollectionViewDataSource?.collectionSkeletonView(collectionView, supplementaryViewIdentifierOfKind: kind, at: indexPath) {
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: viewIdentifier, for: indexPath)
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: viewIdentifier, for: indexPath)
skeletonViewIfContainerSkeletonIsActive(container: collectionView, view: view)
return view
}
......
......@@ -23,7 +23,6 @@ extension SkeletonCollectionDelegate: UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if let viewIdentifier = originalTableViewDelegate?.collectionSkeletonView(tableView, identifierForHeaderInSection: section),
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: viewIdentifier) {
skeletonViewIfContainerSkeletonIsActive(container: tableView, view: header)
return header
}
......@@ -34,7 +33,6 @@ extension SkeletonCollectionDelegate: UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
if let viewIdentifier = originalTableViewDelegate?.collectionSkeletonView(tableView, identifierForFooterInSection: section),
let footer = tableView.dequeueReusableHeaderFooterView(withIdentifier: viewIdentifier) {
skeletonViewIfContainerSkeletonIsActive(container: tableView, view: footer)
return footer
}
......
......@@ -12,7 +12,7 @@ public typealias ReusableHeaderFooterIdentifier = String
extension UITableView: CollectionSkeleton {
var estimatedNumberOfRows: Int {
return Int(ceil(frame.height/rowHeight))
return Int(ceil(frame.height / rowHeight))
}
var skeletonDataSource: SkeletonCollectionDataSource? {
......@@ -43,8 +43,7 @@ extension UITableView: CollectionSkeleton {
self.skeletonDataSource = dataSource
if let originalDelegate = self.delegate as? SkeletonTableViewDelegate,
!(originalDelegate is SkeletonCollectionDelegate)
{
!(originalDelegate is SkeletonCollectionDelegate) {
let delegate = SkeletonCollectionDelegate(tableViewDelegate: originalDelegate)
self.skeletonDelegate = delegate
}
......
// Copyright © 2018 SkeletonView. All rights reserved.
import Foundation
......@@ -10,6 +9,7 @@ enum SkeletonEnvironmentKey: String {
extension Dictionary {
subscript (_ key: SkeletonEnvironmentKey) -> Value? {
// swiftlint:disable:next force_cast
return self[key.rawValue as! Key]
}
}
......@@ -19,7 +19,7 @@ func printSkeletonHierarchy(in view: UIView) {
}
func skeletonLog(_ message: String) {
if let _ = ProcessInfo.processInfo.environment[.debugMode] {
if ProcessInfo.processInfo.environment[.debugMode] != nil {
print(message)
}
}
......@@ -28,7 +28,7 @@ extension UIView {
public var skeletonDescription: String {
var description = "<\(type(of: self)): \(Unmanaged.passUnretained(self).toOpaque())"
let subSkeletons = subviewsSkeletonables
if subSkeletons.count != 0 {
if !subSkeletons.isEmpty {
description += " | (\(subSkeletons.count)) subSkeletons"
}
if isSkeletonable {
......
......@@ -30,15 +30,23 @@ extension CAGradientLayer {
struct SkeletonMultilinesLayerConfig {
var lines: Int
var lineHeight: CGFloat? = nil
var lineHeight: CGFloat
var type: SkeletonType
var lastLineFillPercent: Int
var multilineCornerRadius: Int
var multilineSpacing: CGFloat
var paddingInsets: UIEdgeInsets
var isRTL: Bool
/// Returns padding insets taking into account if the RTL is activated
var calculatedPaddingInsets: UIEdgeInsets {
UIEdgeInsets(top: paddingInsets.top,
left: isRTL ? paddingInsets.right : paddingInsets.left,
bottom: paddingInsets.bottom,
right: isRTL ? paddingInsets.left : paddingInsets.right)
}
}
// MARK: Skeleton sublayers
extension CALayer {
static let skeletonSubLayersName = "SkeletonSubLayersName"
......@@ -48,9 +56,10 @@ extension CALayer {
}
func addMultilinesLayers(for config: SkeletonMultilinesLayerConfig) {
let numberOfSublayers = config.lines == 1 ? 1 : calculateNumLines(for: config)
var height = config.lineHeight ?? SkeletonAppearance.default.multilineHeight
if numberOfSublayers == 1 {
let numberOfSublayers = config.lines > 0 ? config.lines : calculateNumLines(for: config)
var height = config.lineHeight
if numberOfSublayers == 1 && SkeletonAppearance.default.renderSingleLineAsView {
height = bounds.height
}
......@@ -60,6 +69,7 @@ extension CALayer {
.setMultilineSpacing(config.multilineSpacing)
.setPadding(config.paddingInsets)
.setHeight(height)
.setIsRTL(config.isRTL)
(0..<numberOfSublayers).forEach { index in
let width = calculatedWidthForLine(at: index, totalLines: numberOfSublayers, lastLineFillPercent: config.lastLineFillPercent, paddingInsets: config.paddingInsets)
......@@ -76,16 +86,21 @@ extension CALayer {
let currentSkeletonSublayers = skeletonSublayers
let numberOfSublayers = currentSkeletonSublayers.count
let lastLineFillPercent = config.lastLineFillPercent
let paddingInsets = config.paddingInsets
let paddingInsets = config.calculatedPaddingInsets
let multilineSpacing = config.multilineSpacing
var height = config.lineHeight ?? SkeletonAppearance.default.multilineHeight
if numberOfSublayers == 1 {
var height = config.lineHeight
if numberOfSublayers == 1 && SkeletonAppearance.default.renderSingleLineAsView {
height = bounds.height
}
for (index, layer) in currentSkeletonSublayers.enumerated() {
let width = calculatedWidthForLine(at: index, totalLines: numberOfSublayers, lastLineFillPercent: lastLineFillPercent, paddingInsets: paddingInsets)
layer.updateLayerFrame(for: index, size: CGSize(width: width, height: height), multilineSpacing: multilineSpacing, paddingInsets: paddingInsets)
layer.updateLayerFrame(for: index,
size: CGSize(width: width, height: height),
multilineSpacing: multilineSpacing,
paddingInsets: paddingInsets,
isRTL: config.isRTL)
}
}
......@@ -97,16 +112,38 @@ extension CALayer {
return width
}
func updateLayerFrame(for index: Int, size: CGSize, multilineSpacing: CGFloat, paddingInsets: UIEdgeInsets) {
let spaceRequiredForEachLine = SkeletonAppearance.default.multilineHeight + multilineSpacing
frame = CGRect(x: paddingInsets.left, y: CGFloat(index) * spaceRequiredForEachLine + paddingInsets.top, width: size.width, height: size.height)
func updateLayerFrame(for index: Int, size: CGSize, multilineSpacing: CGFloat, paddingInsets: UIEdgeInsets, isRTL: Bool) {
let spaceRequiredForEachLine = size.height + multilineSpacing
let newFrame = CGRect(x: paddingInsets.left,
y: CGFloat(index) * spaceRequiredForEachLine + paddingInsets.top,
width: size.width,
height: size.height)
frame = flipRectForRTLIfNeeded(newFrame, isRTL: isRTL)
}
private func calculateNumLines(for config: SkeletonMultilinesLayerConfig) -> Int {
let requiredSpaceForEachLine = (config.lineHeight ?? SkeletonAppearance.default.multilineHeight) + config.multilineSpacing
var numberOfSublayers = Int(round(CGFloat(bounds.height - config.paddingInsets.top - config.paddingInsets.bottom)/CGFloat(requiredSpaceForEachLine)))
if config.lines != 0, config.lines <= numberOfSublayers { numberOfSublayers = config.lines }
return numberOfSublayers
private func calculateNumLines(for config: SkeletonMultilinesLayerConfig) -> Int {
let definedNumberOfLines = config.lines
let requiredSpaceForEachLine = config.lineHeight + config.multilineSpacing
let calculatedNumberOfLines = Int(round(CGFloat(bounds.height - config.paddingInsets.top - config.paddingInsets.bottom) / CGFloat(requiredSpaceForEachLine)))
guard calculatedNumberOfLines > 0 else {
return 1
}
if definedNumberOfLines > 0, definedNumberOfLines <= calculatedNumberOfLines {
return definedNumberOfLines
}
return calculatedNumberOfLines
}
private func flipRectForRTLIfNeeded(_ rect: CGRect, isRTL: Bool) -> CGRect {
var newRect = rect
if isRTL {
newRect.origin.x = (superlayer?.bounds.width ?? 0) - rect.origin.x - rect.width
}
return newRect
}
}
......@@ -115,6 +152,7 @@ public extension CALayer {
var pulse: CAAnimation {
let pulseAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.backgroundColor))
pulseAnimation.fromValue = backgroundColor
//swiftlint:disable:next force_unwrapping
pulseAnimation.toValue = UIColor(cgColor: backgroundColor!).complementaryColor.cgColor
pulseAnimation.duration = 1
pulseAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
......@@ -124,25 +162,6 @@ public extension CALayer {
return pulseAnimation
}
var sliding: CAAnimation {
let startPointAnim = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.startPoint))
startPointAnim.fromValue = CGPoint(x: -1, y: 0.5)
startPointAnim.toValue = CGPoint(x:1, y: 0.5)
let endPointAnim = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.endPoint))
endPointAnim.fromValue = CGPoint(x: 0, y: 0.5)
endPointAnim.toValue = CGPoint(x:2, y: 0.5)
let animGroup = CAAnimationGroup()
animGroup.animations = [startPointAnim, endPointAnim]
animGroup.duration = 1.5
animGroup.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
animGroup.repeatCount = .infinity
animGroup.isRemovedOnCompletion = false
return animGroup
}
func playAnimation(_ anim: SkeletonLayerAnimation, key: String, completion: (() -> Void)? = nil) {
skeletonSublayers.recursiveSearch(leafBlock: {
DispatchQueue.main.async { CATransaction.begin() }
......
......@@ -22,7 +22,7 @@ extension UIColor {
public var complementaryColor: UIColor {
if #available(iOS 13, tvOS 13, *) {
return UIColor { traitCollection in
return UIColor { _ in
return self.isLight() ? self.darker : self.lighter
}
} else {
......@@ -50,6 +50,7 @@ extension UIColor {
}
public extension UIColor {
// swiftlint:disable operator_usage_whitespace
static var greenSea = UIColor(0x16a085)
static var turquoise = UIColor(0x1abc9c)
static var emerald = UIColor(0x2ecc71)
......@@ -71,13 +72,16 @@ public extension UIColor {
static var pomegranate = UIColor(0xc0392b)
static var silver = UIColor(0xbdc3c7)
static var asbestos = UIColor(0x7f8c8d)
// swiftlint:enable operator_usage_whitespace
static var skeletonDefault: UIColor {
if #available(iOS 13, tvOS 13, *) {
return UIColor { traitCollection in
switch traitCollection.userInterfaceStyle {
case .dark: return .darkClouds
default: return .clouds
case .dark:
return .darkClouds
default:
return .clouds
}
}
} else {
......
......@@ -13,15 +13,13 @@ extension UITableView {
headerRect = rectForHeader(inSection: $1)
}
if headerRect != nil {
let visiblePartOfTableView: CGRect = CGRect(
x: contentOffset.x,
y: contentOffset.y,
width: bounds.size.width,
height: bounds.size.height
)
if let headerRect = headerRect {
let visiblePartOfTableView = CGRect(x: contentOffset.x,
y: contentOffset.y,
width: bounds.size.width,
height: bounds.size.height)
if (visiblePartOfTableView.intersects(headerRect!)) {
if visiblePartOfTableView.intersects(headerRect) {
return $0 + [$1]
}
}
......
// Copyright © 2020 SkeletonView. All rights reserved.
import UIKit
// MARK: Frame
extension UIView {
var widthConstraints: [NSLayoutConstraint] {
nonContentSizeLayoutConstraints.filter { $0.firstAttribute == NSLayoutConstraint.Attribute.width }
}
var heightConstraints: [NSLayoutConstraint] {
nonContentSizeLayoutConstraints.filter { $0.firstAttribute == NSLayoutConstraint.Attribute.height }
}
@discardableResult
func setHeight(equalToConstant constant: CGFloat) -> NSLayoutConstraint {
let heightConstraint = heightAnchor.constraint(equalToConstant: constant)
NSLayoutConstraint.activate([heightConstraint])
return heightConstraint
}
var nonContentSizeLayoutConstraints: [NSLayoutConstraint] {
constraints.filter({ "\(type(of: $0))" != "NSContentSizeLayoutConstraint" })
}
}
......@@ -5,6 +5,7 @@ import UIKit
// codebeat:disable[TOO_MANY_IVARS]
enum ViewAssociatedKeys {
static var skeletonable = "skeletonable"
static var hiddenWhenSkeletonIsActive = "hiddenWhenSkeletonIsActive"
static var status = "status"
static var skeletonLayer = "layer"
static var flowDelegate = "flowDelegate"
......@@ -12,6 +13,7 @@ enum ViewAssociatedKeys {
static var viewState = "viewState"
static var labelViewState = "labelViewState"
static var imageViewState = "imageViewState"
static var buttonViewState = "buttonViewState"
static var currentSkeletonConfig = "currentSkeletonConfig"
static var skeletonCornerRadius = "skeletonCornerRadius"
}
......@@ -38,13 +40,13 @@ extension UIView {
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.currentSkeletonConfig) }
}
var status: Status! {
var status: Status {
get { return ao_get(pkey: &ViewAssociatedKeys.status) as? Status ?? .off }
set { ao_set(newValue ?? .off, pkey: &ViewAssociatedKeys.status) }
set { ao_set(newValue, pkey: &ViewAssociatedKeys.status) }
}
var isSkeletonAnimated: Bool! {
var isSkeletonAnimated: Bool {
get { return ao_get(pkey: &ViewAssociatedKeys.isSkeletonAnimated) as? Bool ?? false }
set { ao_set(newValue ?? false, pkey: &ViewAssociatedKeys.isSkeletonAnimated) }
set { ao_set(newValue, pkey: &ViewAssociatedKeys.isSkeletonAnimated) }
}
}
......@@ -10,32 +10,38 @@ import UIKit
// MARK: Frame
extension UIView {
var maxBoundsEstimated: CGRect {
var definedMaxBounds: CGRect {
if let parentStackView = (superview as? UIStackView) {
var origin: CGPoint = .zero
switch parentStackView.alignment {
case .trailing:
origin.x = maxWidthEstimated
origin.x = definedMaxWidth
default:
break
}
return CGRect(origin: origin, size: maxSizeEstimated)
return CGRect(origin: origin, size: definedMaxSize)
}
return CGRect(origin: .zero, size: maxSizeEstimated)
return CGRect(origin: .zero, size: definedMaxSize)
}
var maxSizeEstimated: CGSize {
return CGSize(width: maxWidthEstimated, height: maxHeightEstimated)
var definedMaxSize: CGSize {
CGSize(width: definedMaxWidth, height: definedMaxHeight)
}
var maxWidthEstimated: CGFloat {
let constraintsWidth = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutConstraint.Attribute.width })
return max(between: frame.size.width, andContantsOf: constraintsWidth)
var definedMaxWidth: CGFloat {
max(between: frame.size.width, andContantsOf: widthConstraints)
}
var maxHeightEstimated: CGFloat {
let constraintsHeight = nonContentSizeLayoutConstraints.filter({ $0.firstAttribute == NSLayoutConstraint.Attribute.height })
return max(between: frame.size.height, andContantsOf: constraintsHeight)
var definedMaxHeight: CGFloat {
max(between: frame.size.height, andContantsOf: heightConstraints)
}
var isRTL: Bool {
if #available(iOS 10.0, *), #available(tvOS 10.0, *) {
return effectiveUserInterfaceLayoutDirection == .rightToLeft
} else {
return false
}
}
private func max(between value: CGFloat, andContantsOf constraints: [NSLayoutConstraint]) -> CGFloat {
......@@ -46,8 +52,4 @@ extension UIView {
})
return max
}
var nonContentSizeLayoutConstraints: [NSLayoutConstraint] {
return constraints.filter({ "\(type(of: $0))" != "NSContentSizeLayoutConstraint" })
}
}
......@@ -8,6 +8,12 @@ public extension UIView {
get { return skeletonable }
set { skeletonable = newValue }
}
@IBInspectable
var isHiddenWhenSkeletonIsActive: Bool {
get { return hiddenWhenSkeletonIsActive }
set { hiddenWhenSkeletonIsActive = newValue }
}
@IBInspectable
var skeletonCornerRadius: Float {
......@@ -16,17 +22,21 @@ public extension UIView {
}
var isSkeletonActive: Bool {
return status == .on || (subviewsSkeletonables.first(where: { $0.isSkeletonActive }) != nil)
return status == .on || subviewsSkeletonables.contains(where: { $0.isSkeletonActive })
}
private var skeletonable: Bool! {
private var skeletonable: Bool {
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonable) as? Bool ?? false }
set { ao_set(newValue ?? false, pkey: &ViewAssociatedKeys.skeletonable) }
set { ao_set(newValue, pkey: &ViewAssociatedKeys.skeletonable) }
}
private var hiddenWhenSkeletonIsActive: Bool {
get { return ao_get(pkey: &ViewAssociatedKeys.hiddenWhenSkeletonIsActive) as? Bool ?? false }
set { ao_set(newValue, pkey: &ViewAssociatedKeys.hiddenWhenSkeletonIsActive) }
}
private var skeletonableCornerRadius: Float! {
private var skeletonableCornerRadius: Float {
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonCornerRadius) as? Float ?? 0.0 }
set { ao_set(newValue ?? 0.0, pkey: &ViewAssociatedKeys.skeletonCornerRadius) }
set { ao_set(newValue, pkey: &ViewAssociatedKeys.skeletonCornerRadius) }
}
}
......@@ -12,6 +12,7 @@ enum AssociationPolicy: UInt {
case retainNonatomic = 1
var objc: objc_AssociationPolicy {
// swiftlint:disable:next force_unwrapping
return objc_AssociationPolicy(rawValue: rawValue)!
}
}
......
......@@ -12,15 +12,44 @@ extension UIView {
@objc func prepareViewForSkeleton() {
startTransition { [weak self] in
self?.backgroundColor = .clear
self?.isUserInteractionEnabled = false
}
}
}
extension UILabel {
var desiredHeightBasedOnNumberOfLines: CGFloat {
let lineHeight = multilineTextFont?.lineHeight ?? SkeletonAppearance.default.multilineHeight
let spaceNeededForEachLine = lineHeight * CGFloat(numberOfLines)
let spaceNeededForSpaces = skeletonLineSpacing * CGFloat(numberOfLines - 1)
let padding = paddingInsets.top + paddingInsets.bottom
return spaceNeededForEachLine + spaceNeededForSpaces + padding
}
func updateHeightConstraintsIfNeeded() {
guard numberOfLines > 1 else { return }
let desiredHeight = desiredHeightBasedOnNumberOfLines
if desiredHeight > definedMaxHeight {
backupHeightConstraints = heightConstraints
NSLayoutConstraint.deactivate(heightConstraints)
setHeight(equalToConstant: desiredHeight)
}
}
func restoreBackupHeightConstraints() {
guard !backupHeightConstraints.isEmpty else { return }
NSLayoutConstraint.deactivate(heightConstraints)
NSLayoutConstraint.activate(backupHeightConstraints)
backupHeightConstraints.removeAll()
}
override func prepareViewForSkeleton() {
backgroundColor = .clear
resignFirstResponder()
startTransition { [weak self] in
self?.updateHeightConstraintsIfNeeded()
self?.textColor = .clear
}
}
......@@ -44,3 +73,12 @@ extension UIImageView {
}
}
}
extension UIButton {
override func prepareViewForSkeleton() {
backgroundColor = .clear
startTransition { [weak self] in
self?.setTitle(nil, for: .normal)
}
}
}
......@@ -9,7 +9,7 @@ protocol IterableElement {}
extension UIView: IterableElement {}
extension CALayer: IterableElement {}
//MARK: Recursive
// MARK: Recursive
protocol Recursive {
associatedtype Element: IterableElement
func recursiveSearch(leafBlock: VoidBlock, recursiveBlock: RecursiveBlock<Element>)
......@@ -17,12 +17,10 @@ protocol Recursive {
extension Array: Recursive where Element: IterableElement {
func recursiveSearch(leafBlock: VoidBlock, recursiveBlock: RecursiveBlock<Element>) {
guard count > 0 else {
guard !isEmpty else {
leafBlock()
return
}
forEach { recursiveBlock($0) }
}
}
......@@ -5,7 +5,7 @@ import Foundation
extension DispatchQueue {
private static var _onceTracker = [String]()
class func once(token: String, block:()->Void) {
class func once(token: String, block: () -> Void) {
objc_sync_enter(self); defer { objc_sync_exit(self) }
guard !_onceTracker.contains(token) else { return }
......@@ -19,7 +19,7 @@ func swizzle(selector originalSelector: Selector, with swizzledSelector: Selecto
let swizzledMethod = class_getInstanceMethod(usingClass, swizzledSelector)
else { return }
if (class_addMethod(inClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))) {
if class_addMethod(inClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)) {
class_replaceMethod(inClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
......
......@@ -7,6 +7,7 @@ enum MultilineAssociatedKeys {
static var multilineCornerRadius = "multilineCornerRadius"
static var multilineSpacing = "multilineSpacing"
static var paddingInsets = "paddingInsets"
static var backupHeightConstraints = "backupHeightConstraints"
}
protocol ContainsMultilineText {
......@@ -17,7 +18,3 @@ protocol ContainsMultilineText {
var multilineSpacing: CGFloat { get }
var paddingInsets: UIEdgeInsets { get }
}
extension ContainsMultilineText {
var numLines: Int { return 0 }
}
......@@ -8,17 +8,19 @@ public extension UILabel {
get { return lastLineFillingPercent }
set { lastLineFillingPercent = min(newValue, 100) }
}
@IBInspectable
var linesCornerRadius: Int {
get { return multilineCornerRadius }
set { multilineCornerRadius = min(newValue, 10) }
}
@IBInspectable
var skeletonLineSpacing: CGFloat {
get { return multilineSpacing }
set { multilineSpacing = min(newValue, 10) }
}
@IBInspectable
var skeletonPaddingInsets: UIEdgeInsets {
get { return paddingInsets }
set { paddingInsets = newValue }
......@@ -53,4 +55,9 @@ extension UILabel: ContainsMultilineText {
get { return ao_get(pkey: &MultilineAssociatedKeys.paddingInsets) as? UIEdgeInsets ?? .zero }
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.paddingInsets) }
}
var backupHeightConstraints: [NSLayoutConstraint] {
get { return ao_get(pkey: &MultilineAssociatedKeys.backupHeightConstraints) as? [NSLayoutConstraint] ?? [] }
set { ao_set(newValue, pkey: &MultilineAssociatedKeys.backupHeightConstraints) }
}
}
......@@ -21,7 +21,6 @@ public extension UITextView {
set { multilineSpacing = min(newValue, 10) }
}
@IBInspectable
var skeletonPaddingInsets: UIEdgeInsets {
get { return paddingInsets }
set { paddingInsets = newValue }
......@@ -30,8 +29,12 @@ public extension UITextView {
extension UITextView: ContainsMultilineText {
var multilineTextFont: UIFont? {
return font
}
font
}
var numLines: Int {
-1
}
var lastLineFillingPercent: Int {
get {
......
......@@ -24,20 +24,21 @@ extension UIView: Recoverable {
}
@objc func recoverViewState(forced: Bool) {
guard let safeViewState = viewState else { return }
guard let storedViewState = viewState else { return }
startTransition { [weak self] in
self?.layer.cornerRadius = safeViewState.cornerRadius
self?.layer.masksToBounds = safeViewState.clipToBounds
self?.layer.cornerRadius = storedViewState.cornerRadius
self?.layer.masksToBounds = storedViewState.clipToBounds
self?.isUserInteractionEnabled = storedViewState.isUserInteractionsEnabled
if safeViewState.backgroundColor != self?.backgroundColor || forced {
self?.backgroundColor = safeViewState.backgroundColor
if self?.backgroundColor == .clear || forced {
self?.backgroundColor = storedViewState.backgroundColor
}
}
}
}
extension UILabel{
extension UILabel {
var labelState: RecoverableTextViewState? {
get { return ao_get(pkey: &ViewAssociatedKeys.labelViewState) as? RecoverableTextViewState }
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.labelViewState) }
......@@ -51,14 +52,18 @@ extension UILabel{
override func recoverViewState(forced: Bool) {
super.recoverViewState(forced: forced)
startTransition { [weak self] in
self?.textColor = self?.labelState?.textColor
self?.text = self?.labelState?.text
self?.isUserInteractionEnabled = self?.labelState?.isUserInteractionsEnabled ?? false
guard let storedLabelState = self?.labelState else { return }
self?.restoreBackupHeightConstraints()
if self?.textColor == .clear || forced {
self?.textColor = storedLabelState.textColor
}
}
}
}
extension UITextView{
extension UITextView {
var textState: RecoverableTextViewState? {
get { return ao_get(pkey: &ViewAssociatedKeys.labelViewState) as? RecoverableTextViewState }
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.labelViewState) }
......@@ -72,9 +77,11 @@ extension UITextView{
override func recoverViewState(forced: Bool) {
super.recoverViewState(forced: forced)
startTransition { [weak self] in
self?.textColor = self?.textState?.textColor
self?.text = self?.textState?.text
self?.isUserInteractionEnabled = self?.textState?.isUserInteractionsEnabled ?? false
guard let storedLabelState = self?.textState else { return }
if self?.textColor == .clear || forced {
self?.textColor = storedLabelState.textColor
}
}
}
}
......@@ -97,3 +104,24 @@ extension UIImageView {
}
}
}
extension UIButton {
var buttonState: RecoverableButtonViewState? {
get { return ao_get(pkey: &ViewAssociatedKeys.buttonViewState) as? RecoverableButtonViewState }
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.buttonViewState) }
}
override func saveViewState() {
super.saveViewState()
buttonState = RecoverableButtonViewState(view: self)
}
override func recoverViewState(forced: Bool) {
super.recoverViewState(forced: forced)
startTransition { [weak self] in
if self?.title(for: .normal) == nil {
self?.setTitle(self?.buttonState?.title, for: .normal)
}
}
}
}
......@@ -12,29 +12,25 @@ struct RecoverableViewState {
var backgroundColor: UIColor?
var cornerRadius: CGFloat
var clipToBounds: Bool
var isUserInteractionsEnabled: Bool
init(view: UIView) {
self.backgroundColor = view.backgroundColor
self.clipToBounds = view.layer.masksToBounds
self.cornerRadius = view.layer.cornerRadius
self.isUserInteractionsEnabled = view.isUserInteractionEnabled
}
}
struct RecoverableTextViewState {
var text: String?
var textColor: UIColor?
var isUserInteractionsEnabled: Bool
init(view: UILabel) {
self.textColor = view.textColor
self.text = view.text
self.isUserInteractionsEnabled = view.isUserInteractionEnabled
}
init(view: UITextView) {
self.textColor = view.textColor
self.text = view.text
self.isUserInteractionsEnabled = view.isUserInteractionEnabled
}
}
......@@ -45,3 +41,11 @@ struct RecoverableImageViewState {
self.image = view.image
}
}
struct RecoverableButtonViewState {
var title: String?
init(view: UIButton) {
self.title = view.titleLabel?.text
}
}
......@@ -18,42 +18,42 @@ public enum GradientDirection {
case topLeftBottomRight
case bottomRightTopLeft
public func slidingAnimation(duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation {
return SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: self, duration: duration)
public func slidingAnimation(duration: CFTimeInterval = 1.5, autoreverses: Bool = false) -> SkeletonLayerAnimation {
return SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: self, duration: duration, autoreverses: autoreverses)
}
// codebeat:disable[ABC]
var startPoint: GradientAnimationPoint {
switch self {
case .leftRight:
return (from: CGPoint(x:-1, y:0.5), to: CGPoint(x:1, y:0.5))
return (from: CGPoint(x: -1, y: 0.5), to: CGPoint(x: 1, y: 0.5))
case .rightLeft:
return (from: CGPoint(x:1, y:0.5), to: CGPoint(x:-1, y:0.5))
return (from: CGPoint(x: 1, y: 0.5), to: CGPoint(x: -1, y: 0.5))
case .topBottom:
return (from: CGPoint(x:0.5, y:-1), to: CGPoint(x:0.5, y:1))
return (from: CGPoint(x: 0.5, y: -1), to: CGPoint(x: 0.5, y: 1))
case .bottomTop:
return (from: CGPoint(x:0.5, y:1), to: CGPoint(x:0.5, y:-1))
return (from: CGPoint(x: 0.5, y: 1), to: CGPoint(x: 0.5, y: -1))
case .topLeftBottomRight:
return (from: CGPoint(x:-1, y:-1), to: CGPoint(x:1, y:1))
return (from: CGPoint(x: -1, y: -1), to: CGPoint(x: 1, y: 1))
case .bottomRightTopLeft:
return (from: CGPoint(x:1, y:1), to: CGPoint(x:-1, y:-1))
return (from: CGPoint(x: 1, y: 1), to: CGPoint(x: -1, y: -1))
}
}
var endPoint: GradientAnimationPoint {
switch self {
case .leftRight:
return (from: CGPoint(x:0, y:0.5), to: CGPoint(x:2, y:0.5))
return (from: CGPoint(x: 0, y: 0.5), to: CGPoint(x: 2, y: 0.5))
case .rightLeft:
return ( from: CGPoint(x:2, y:0.5), to: CGPoint(x:0, y:0.5))
return ( from: CGPoint(x: 2, y: 0.5), to: CGPoint(x: 0, y: 0.5))
case .topBottom:
return ( from: CGPoint(x:0.5, y:0), to: CGPoint(x:0.5, y:2))
return ( from: CGPoint(x: 0.5, y: 0), to: CGPoint(x: 0.5, y: 2))
case .bottomTop:
return ( from: CGPoint(x:0.5, y:2), to: CGPoint(x:0.5, y:0))
return ( from: CGPoint(x: 0.5, y: 2), to: CGPoint(x: 0.5, y: 0))
case .topLeftBottomRight:
return ( from: CGPoint(x:0, y:0), to: CGPoint(x:2, y:2))
return ( from: CGPoint(x: 0, y: 0), to: CGPoint(x: 2, y: 2))
case .bottomRightTopLeft:
return ( from: CGPoint(x:2, y:2), to: CGPoint(x:0, y:0))
return ( from: CGPoint(x: 2, y: 2), to: CGPoint(x: 0, y: 0))
}
}
// codebeat:enable[ABC]
......@@ -62,9 +62,8 @@ public enum GradientDirection {
public class SkeletonAnimationBuilder {
public init() { }
public func makeSlidingAnimation(withDirection direction: GradientDirection, duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation {
public func makeSlidingAnimation(withDirection direction: GradientDirection, duration: CFTimeInterval = 1.5, autoreverses: Bool = false) -> SkeletonLayerAnimation {
return { layer in
let startPointAnim = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.startPoint))
startPointAnim.fromValue = direction.startPoint.from
startPointAnim.toValue = direction.startPoint.to
......@@ -78,6 +77,7 @@ public class SkeletonAnimationBuilder {
animGroup.duration = duration
animGroup.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
animGroup.repeatCount = .infinity
animGroup.autoreverses = autoreverses
animGroup.isRemovedOnCompletion = false
return animGroup
......
......@@ -22,14 +22,12 @@ struct SkeletonConfig {
/// Transition style
var transition: SkeletonTransitionStyle
init(
type: SkeletonType,
colors: [UIColor],
gradientDirection: GradientDirection? = nil,
animated: Bool = false,
animation: SkeletonLayerAnimation? = nil,
transition: SkeletonTransitionStyle = .crossDissolve(0.25)
) {
init(type: SkeletonType,
colors: [UIColor],
gradientDirection: GradientDirection? = nil,
animated: Bool = false,
animation: SkeletonLayerAnimation? = nil,
transition: SkeletonTransitionStyle = .crossDissolve(0.25)) {
self.type = type
self.colors = colors
self.gradientDirection = gradientDirection
......
......@@ -2,7 +2,7 @@
import UIKit
protocol SkeletonFlowDelegate {
protocol SkeletonFlowDelegate: AnyObject {
func willBeginShowingSkeletons(rootView: UIView)
func didShowSkeletons(rootView: UIView)
func willBeginUpdatingSkeletons(rootView: UIView)
......
......@@ -23,12 +23,12 @@ public enum SkeletonType {
}
}
var layerAnimation: SkeletonLayerAnimation {
func defaultLayerAnimation(isRTL: Bool) -> SkeletonLayerAnimation {
switch self {
case .solid:
return { $0.pulse }
case .gradient:
return { $0.sliding }
return { SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: isRTL ? .rightLeft : .leftRight) }()
}
}
}
......@@ -49,7 +49,7 @@ struct SkeletonLayer {
self.holder = holder
self.maskLayer = type.layer
self.maskLayer.anchorPoint = .zero
self.maskLayer.bounds = holder.maxBoundsEstimated
self.maskLayer.bounds = holder.definedMaxBounds
self.maskLayer.cornerRadius = CGFloat(holder.skeletonCornerRadius)
addTextLinesIfNeeded()
self.maskLayer.tint(withColors: colors)
......@@ -61,7 +61,7 @@ struct SkeletonLayer {
}
func layoutIfNeeded() {
if let bounds = holder?.maxBoundsEstimated {
if let bounds = holder?.definedMaxBounds {
maskLayer.bounds = bounds
}
updateLinesIfNeeded()
......@@ -83,34 +83,37 @@ struct SkeletonLayer {
/// If there is more than one line, or custom preferences have been set for a single line, draw custom layers
func addTextLinesIfNeeded() {
guard let textView = holderAsTextView else { return }
let lineHeight = textView.multilineTextFont?.lineHeight ?? SkeletonAppearance.default.multilineHeight
let config = SkeletonMultilinesLayerConfig(lines: textView.numLines,
lineHeight: textView.multilineTextFont?.lineHeight,
lineHeight: lineHeight,
type: type,
lastLineFillPercent: textView.lastLineFillingPercent,
multilineCornerRadius: textView.multilineCornerRadius,
multilineSpacing: textView.multilineSpacing,
paddingInsets: textView.paddingInsets)
paddingInsets: textView.paddingInsets,
isRTL: holder?.isRTL ?? false)
maskLayer.addMultilinesLayers(for: config)
}
func updateLinesIfNeeded() {
guard let textView = holderAsTextView else { return }
let lineHeight = textView.multilineTextFont?.lineHeight ?? SkeletonAppearance.default.multilineHeight
let config = SkeletonMultilinesLayerConfig(lines: textView.numLines,
lineHeight: textView.multilineTextFont?.lineHeight,
lineHeight: lineHeight,
type: type,
lastLineFillPercent: textView.lastLineFillingPercent,
multilineCornerRadius: textView.multilineCornerRadius,
multilineSpacing: textView.multilineSpacing,
paddingInsets: textView.paddingInsets)
paddingInsets: textView.paddingInsets,
isRTL: holder?.isRTL ?? false)
maskLayer.updateMultilinesLayers(for: config)
}
var holderAsTextView: ContainsMultilineText? {
guard let textView = holder as? ContainsMultilineText,
(textView.numLines == 0 || textView.numLines > 1 || textView.numLines == 1 && !SkeletonAppearance.default.renderSingleLineAsView) else {
(textView.numLines == -1 || textView.numLines == 0 || textView.numLines > 1 || textView.numLines == 1 && !SkeletonAppearance.default.renderSingleLineAsView) else {
return nil
}
return textView
......@@ -119,7 +122,7 @@ struct SkeletonLayer {
extension SkeletonLayer {
func start(_ anim: SkeletonLayerAnimation? = nil, completion: (() -> Void)? = nil) {
let animation = anim ?? type.layerAnimation
let animation = anim ?? type.defaultLayerAnimation(isRTL: holder?.isRTL ?? false)
contentLayer.playAnimation(animation, key: "skeletonAnimation", completion: completion)
}
......
......@@ -94,6 +94,7 @@ public extension UIView {
extension UIView {
@objc func skeletonLayoutSubviews() {
guard Thread.isMainThread else { return }
skeletonLayoutSubviews()
guard isSkeletonActive else { return }
layoutSkeletonIfNeeded()
......@@ -113,6 +114,9 @@ extension UIView {
}
private func recursiveShowSkeleton(skeletonConfig config: SkeletonConfig, root: UIView? = nil) {
if isHiddenWhenSkeletonIsActive {
isHidden = true
}
guard isSkeletonable && !isSkeletonActive else { return }
currentSkeletonConfig = config
swizzleLayoutSubviews()
......@@ -120,7 +124,7 @@ extension UIView {
addDummyDataSourceIfNeeded()
subviewsSkeletonables.recursiveSearch(leafBlock: {
showSkeletonIfNotActive(skeletonConfig: config)
}){ subview in
}) { subview in
subview.recursiveShowSkeleton(skeletonConfig: config)
}
......@@ -148,7 +152,8 @@ extension UIView {
currentSkeletonConfig = config
updateDummyDataSourceIfNeeded()
subviewsSkeletonables.recursiveSearch(leafBlock: {
if skeletonLayer?.type != config.type {
if let skeletonLayer = skeletonLayer,
skeletonLayer.type != config.type {
removeSkeletonLayer()
addSkeletonLayer(skeletonConfig: config)
} else {
......@@ -181,6 +186,9 @@ extension UIView {
private func recursiveHideSkeleton(reloadDataAfter reload: Bool, transition: SkeletonTransitionStyle, root: UIView? = nil) {
guard isSkeletonActive else { return }
if isHiddenWhenSkeletonIsActive {
isHidden = false
}
currentSkeletonConfig?.transition = transition
isUserInteractionEnabled = true
removeDummyDataSourceIfNeeded(reloadAfter: reload)
......
......@@ -8,7 +8,6 @@ extension CALayer {
switch transition {
case .none:
completion?()
break
case .crossDissolve(let duration):
layer.contentLayer.setOpacity(from: 0, to: 1, duration: duration, completion: completion)
}
......
......@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.8.7</string>
<string>1.11.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment