Compare commits

...

28 Commits

Author SHA1 Message Date
Juanpe Catalán 46e2180c4b docs: bump version 1.11.0 and update CHANGELOG file 2020-10-29 14:20:20 +01:00
Juanpe Catalán f32c420165 Merge pull request #344 from Juanpe/feature/resize_labels_based_on_number_of_lines
Feature/resize labels based on number of lines
2020-10-29 14:17:57 +01:00
Juanpe Catalán 0ca38d8ea1 doc: update CHANGELOG file 2020-10-29 14:13:16 +01:00
Juanpe Catalán 9ed337ba53 Merge branch 'develop' of https://github.com/Juanpe/SkeletonView into feature/resize_labels_based_on_number_of_lines 2020-10-29 14:12:09 +01:00
Juanpe Catalán 932a1f6e6e Merge pull request #340 from yzhao198/fixMultiLineAndPadding
Fix incorrect padding and multiline layer frame calculation
2020-10-26 08:11:37 +01:00
Yang Zhao 5cd08fb77c Updated CHANGELOG.md. 2020-10-25 18:40:02 -06:00
Yang Zhao f7cb021a93 Fixed incorrect padding, and incorrect multiline layer frame calculation. 2020-10-25 18:32:31 -06:00
Juanpe Catalán 4fbfbeca55 feat: update layout to desired height 2020-10-22 00:48:23 +02:00
Juanpe Catalán c92461ae5c fix: shows at least one line 2020-10-21 14:35:48 +02:00
Juanpe Catalán efed2e7f32 Merge pull request #341 from Juanpe/feature/animation_autoreverses
Support autoreverses in gradient animations
2020-10-19 11:52:16 +02:00
Juanpe Catalán 52cbd3ae2c doc: Include PR #341 in the CHANGELOG file 2020-10-19 11:49:08 +02:00
Juanpe Catalán a349f7174c feat: support autoreverses animations for gradients 2020-10-19 11:41:57 +02:00
Juanpe Catalán a58c97eb1a Update CHANGELOG.md 2020-10-19 10:36:48 +02:00
Juanpe Catalán 8e6fea2de3 Update CHANGELOG.md 2020-10-19 10:36:33 +02:00
Juanpe Catalán bf417d6bf8 Update CHANGELOG.md 2020-10-19 10:36:16 +02:00
Juanpe Catalán c3295531be Merge pull request #339 from mohn93/develop
Add  hiddenWhenSkeletonIsActive property
2020-10-19 10:34:43 +02:00
Mohanned Binmiskeen a87f7302c0 Documentation for isHiddenWhenSkeletonIsActive 2020-10-16 22:46:20 +02:00
Juanpe Catalán 5e22f38454 Upgrade Danger version 3.5.0 2020-10-07 13:07:29 +02:00
Mohanned Binmiskeen 0dbdb9064d Add hiddenWhenSkeletonIsActive property which will hide the view when the skeleton animation starts 2020-10-06 16:03:55 +02:00
Juanpe Catalán c8efc4ccb1 doc: Update CHANGELOG 2020-10-02 17:33:48 +02:00
Juanpe Catalán 01733b358c bump version 1.10.0 2020-10-02 17:15:47 +02:00
Juanpe Catalán 027143499c Merge pull request #337 from Juanpe/feature/support_RTL
RTL support
2020-10-02 17:14:24 +02:00
Juanpe Catalán ae1b5ee790 fix: add condition for tvOS 10.0 2020-10-02 17:08:25 +02:00
Juanpe Catalán 0c0a0ea451 doc: Update CHANGELOG file 2020-10-02 17:01:40 +02:00
Juanpe Catalán 8d090904b4 feat: support RTL 2020-10-02 16:47:00 +02:00
Juanpe Catalán 8d00d5fa16 Merge pull request #336 from Juanpe/fix/replacing_text_wrongly_after_hide
Not replace text when the skeleton disappears
2020-10-02 14:00:28 +02:00
Juanpe Catalán f2bc1cab27 doc: update CHANGELOG file 2020-10-02 13:39:20 +02:00
Juanpe Catalán 83b99f16b1 fix: modified how the skeleton save and restore the original view state of the elements 2020-10-02 13:23:26 +02:00
26 changed files with 324 additions and 143 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: Execute danger
uses: danger/swift@3.3.0
uses: danger/swift@3.5.0
with:
args: --failOnErrors --no-publish-check
env:
+14 -7
View File
@@ -7,11 +7,23 @@ disabled_rules:
- identifier_name
- multiple_closures_with_trailing_closure
- class_delegate_protocol
- force_unwrapping
- force_try
- force_cast
- function_parameter_count
- discouraged_optional_collection
- shorthand_operator
- reduce_boolean
- weak_delegate
- nesting
- closure_end_indentation
- function_default_parameter_at_end
- unowned_variable_capture
- legacy_constructor
opt_in_rules:
- multiline_arguments
- multiline_parameters
- closure_spacing
- closure_end_indentation
- closure_body_length
- collection_alignment
- contains_over_filter_is_empty
@@ -27,8 +39,6 @@ opt_in_rules:
- file_name_no_space
- first_where
- flatmap_over_map_reduce
- force_unwrapping
- function_default_parameter_at_end
- implicitly_unwrapped_optional
- joined_default_parameter
- last_where
@@ -43,7 +53,6 @@ opt_in_rules:
- sorted_first_last
- switch_case_on_newline
- unneeded_parentheses_in_closure_argument
- unowned_variable_capture
- unused_declaration
- unused_import
- vertical_whitespace_opening_braces
@@ -51,12 +60,10 @@ opt_in_rules:
- enum_case_associated_values_counts
- legacy_multiple
- legacy_random
force_cast: error
force_unwrapping: error
indentation: 2
file_length:
- 2500
- 3000
large_tuple:
- 5
- 6
- 6
+23
View File
@@ -3,10 +3,33 @@ All notable changes to this project will be documented in this file
### Next version
#### 🙌 New
#### 🔬Improvements
#### 🩹 Bug fixes
## 📦 [1.11.0](https://github.com/Juanpe/SkeletonView/releases/tag/1.11.0)
#### 🙌 New
* [**339**](https://github.com/Juanpe/SkeletonView/pull/339): Add `hiddenWhenSkeletonIsActive` property - [@mohn93](https://github.com/mohn93)
* [**341**](https://github.com/Juanpe/SkeletonView/pull/341): Support autoreverses in gradient animations - [@Juanpe](https://github.com/Juanpe)
* [**344**](https://github.com/Juanpe/SkeletonView/pull/344): Resize labels based on number of lines - [@Juanpe](https://github.com/Juanpe)
#### 🩹 Bug fixes
* [**340**](https://github.com/Juanpe/SkeletonView/pull/340): Fixed incorrect padding, and incorrect multiline layer frame calculation - [@yzhao198](https://github.com/yzhao198)
## 📦 [1.10.0](https://github.com/Juanpe/SkeletonView/releases/tag/1.10.0)
#### 🙌 New
* [**327**](https://github.com/Juanpe/SkeletonView/pull/327): Add SwiftLint - [@Juanpe](https://github.com/Juanpe)
* [**329**](https://github.com/Juanpe/SkeletonView/pull/329): Spanish README 🇪🇸 - [@Juanpe](https://github.com/Juanpe)
#### 🩹 Bug fixes
* [**336**](https://github.com/Juanpe/SkeletonView/pull/336): Not replace text when the skeleton disappears. Solved issues: [#296](https://github.com/Juanpe/SkeletonView/issues/296), [#330](https://github.com/Juanpe/SkeletonView/issues/330) - [@Juanpe](https://github.com/Juanpe)
* [**337**](https://github.com/Juanpe/SkeletonView/pull/337): RTL support. Solved issues: [#143](https://github.com/Juanpe/SkeletonView/issues/143) - [@Juanpe](https://github.com/Juanpe)
## 📦 [1.9](https://github.com/Juanpe/SkeletonView/releases/tag/1.9)
#### 🩹 Bug fixes
+38 -23
View File
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Va7-1y-Tel">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Va7-1y-Tel">
<device id="retina5_9" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@@ -25,7 +26,7 @@
<constraint firstAttribute="height" constant="78" id="gF5-G1-lKI"/>
</constraints>
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </string>
<color key="textColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="textColor" systemColor="labelColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<userDefinedRuntimeAttributes>
@@ -49,31 +50,38 @@
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ifX-3x-oCb">
<rect key="frame" x="165" y="44.666666666666671" width="125" height="44"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CJW-A4-Fb8">
<rect key="frame" x="166" y="27" width="166" height="12"/>
<color key="backgroundColor" red="0.92156862750000001" green="0.16862745100000001" blue="0.54901960780000003" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="width" constant="125" id="MuV-52-GiO"/>
<constraint firstAttribute="height" constant="44" id="vIU-os-12z"/>
<constraint firstAttribute="height" relation="lessThanOrEqual" constant="80" id="XAA-g8-NVH"/>
</constraints>
<state key="normal" title="Button">
<color key="titleColor" systemColor="systemBlueColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<fontDescription key="fontDescription" type="system" pointSize="10"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="linesCornerRadius">
<integer key="value" value="5"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="skeletonLineSpacing">
<real key="value" value="8"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</button>
</label>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="ifX-3x-oCb" firstAttribute="centerY" secondItem="nMj-pU-5wJ" secondAttribute="centerY" id="8sl-4R-4in"/>
<constraint firstItem="CJW-A4-Fb8" firstAttribute="leading" secondItem="nMj-pU-5wJ" secondAttribute="trailing" constant="28" id="Drg-cD-6E8"/>
<constraint firstItem="e9V-mk-xH0" firstAttribute="leading" secondItem="F9K-jU-100" secondAttribute="leading" constant="45" id="HvQ-HY-zYU"/>
<constraint firstItem="e9V-mk-xH0" firstAttribute="centerX" secondItem="F9K-jU-100" secondAttribute="centerX" constant="1" id="KcB-tG-NXa"/>
<constraint firstItem="ifX-3x-oCb" firstAttribute="leading" secondItem="nMj-pU-5wJ" secondAttribute="trailing" constant="27" id="LQC-s6-w43"/>
<constraint firstAttribute="height" constant="243" id="MIj-xq-gr1"/>
<constraint firstItem="nMj-pU-5wJ" firstAttribute="leading" secondItem="F9K-jU-100" secondAttribute="leading" constant="45" id="TK6-Ws-2xY"/>
<constraint firstItem="e9V-mk-xH0" firstAttribute="top" secondItem="F9K-jU-100" secondAttribute="top" constant="142" id="Wcx-nZ-1lR"/>
<constraint firstAttribute="trailing" secondItem="e9V-mk-xH0" secondAttribute="trailing" constant="43" id="XbU-Og-rht"/>
<constraint firstItem="CJW-A4-Fb8" firstAttribute="top" secondItem="F9K-jU-100" secondAttribute="top" constant="27" id="ceh-gB-7Et"/>
<constraint firstItem="nMj-pU-5wJ" firstAttribute="top" secondItem="F9K-jU-100" secondAttribute="top" constant="20" id="hQL-cr-MaN"/>
<constraint firstAttribute="trailing" secondItem="CJW-A4-Fb8" secondAttribute="trailing" constant="43" id="nfT-a5-z36"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
@@ -91,7 +99,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="avatar" translatesAutoresizingMaskIntoConstraints="NO" id="oiE-tt-nc2">
<rect key="frame" x="15" y="18" width="82" height="82"/>
<rect key="frame" x="16" y="18" width="82" height="82"/>
<color key="backgroundColor" red="0.56078431370000004" green="0.59607843140000005" blue="0.7843137255" alpha="0.90709546230000004" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="width" constant="82" id="4j0-PU-CmN"/>
@@ -101,10 +109,11 @@
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VhU-1t-AaI" userLabel="Label">
<rect key="frame" x="118" y="29" width="237" height="18"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VhU-1t-AaI" userLabel="Label">
<rect key="frame" x="119" y="29" width="235" height="20"/>
<color key="backgroundColor" red="0.92156862750000001" green="0.16862745100000001" blue="0.54901960780000003" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="height" relation="lessThanOrEqual" constant="71" id="HRL-cI-ieC"/>
<constraint firstAttribute="height" constant="20" id="HRL-cI-ieC"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
@@ -198,7 +207,7 @@
<action selector="btnChangeColorTouchUpInside:" destination="BYZ-38-t0r" eventType="touchUpInside" id="cB8-Ik-LIJ"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Tdu-YQ-saq">
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Tdu-YQ-saq">
<rect key="frame" x="263" y="69" width="94" height="30"/>
<state key="normal" title="Hide skeleton"/>
<connections>
@@ -218,7 +227,7 @@
</connections>
</stepper>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="l4N-LL-ZrJ" firstAttribute="leading" secondItem="mrw-PM-jJJ" secondAttribute="trailing" constant="8" id="5iU-dO-qVc"/>
<constraint firstItem="mrw-PM-jJJ" firstAttribute="centerY" secondItem="l4N-LL-ZrJ" secondAttribute="centerY" id="9OM-mx-4Jo"/>
@@ -243,7 +252,8 @@
</constraints>
</view>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="F9K-jU-100" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="1es-nY-bd3"/>
<constraint firstItem="F9K-jU-100" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="A3E-iv-1qp"/>
@@ -256,7 +266,6 @@
<constraint firstItem="UCB-SP-lQk" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="o6z-Dj-ppC"/>
<constraint firstItem="XgY-1a-UGc" firstAttribute="bottom" secondItem="6Tk-OE-BBY" secondAttribute="bottom" id="vnZ-9k-MfI"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSkeletonable" value="YES"/>
</userDefinedRuntimeAttributes>
@@ -285,8 +294,8 @@
<view key="view" contentMode="scaleToFill" id="Jwx-gI-Qod">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="Ao1-hk-zrH"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
<tabBarItem key="tabBarItem" title="Item" id="iKp-9S-aib"/>
</viewController>
@@ -316,5 +325,11 @@
</scenes>
<resources>
<image name="avatar" width="215" height="211"/>
<systemColor name="labelColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
+4 -4
View File
@@ -52,11 +52,11 @@ class ViewController: UIViewController {
super.viewDidLoad()
tableview.isSkeletonable = true
transitionDurationStepper.value = 0.25
view.showAnimatedSkeleton()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
view.showAnimatedSkeleton()
}
@IBAction func changeAnimated(_ sender: Any) {
@@ -180,7 +180,7 @@ extension ViewController: SkeletonTableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellIdentifier", for: indexPath) as! Cell
cell.label1.text = "cell => \(indexPath.row)"
cell.label1.text = "cell -> \(indexPath.row)"
return cell
}
}
@@ -193,7 +193,7 @@ extension ViewController: SkeletonTableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView
.dequeueReusableHeaderFooterView(withIdentifier: "HeaderIdentifier") as! HeaderFooterSection
header.titleLabel.text = "header => \(section)"
header.titleLabel.text = "header -> \(section)"
return header
}
@@ -204,7 +204,7 @@ extension ViewController: SkeletonTableViewDelegate {
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footer = tableView
.dequeueReusableHeaderFooterView(withIdentifier: "FooterIdentifier") as! HeaderFooterSection
footer.titleLabel.text = "footer => \(section)"
footer.titleLabel.text = "footer -> \(section)"
return footer
}
}
+7
View File
@@ -479,6 +479,13 @@ You can change the skeleton configuration at any time like its colour, animation
(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:
```swift
view.isHiddenWhenSkeletonIsActive = true // This works only when isSkeletonable = true
```
**Debug**
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SkeletonView"
s.version = "1.9"
s.version = "1.11.0"
s.summary = "An elegant way to show users that something is happening and also prepare them to which contents he is waiting"
s.description = <<-DESC
Today almost all apps have async processes, as API requests, long runing processes, etc. And while the processes are working, usually developers place a loading view to show users that something is going on.
+6
View File
@@ -28,6 +28,8 @@
17DD0E1D207FB32100C56334 /* ContainsMultilineText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E3A1FB123C100EE67C5 /* ContainsMultilineText.swift */; };
17DD0E1E207FB32100C56334 /* PrepareForSkeletonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F56B94451FAE20AF0095662F /* PrepareForSkeletonProtocol.swift */; };
17DD0E1F207FB32100C56334 /* RecursiveProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5307E311FB0F42F00EE67C5 /* RecursiveProtocol.swift */; };
1E291F3E2540655D0018D602 /* UIView+Autolayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E291F3D2540655D0018D602 /* UIView+Autolayout.swift */; };
1E291F3F2540655D0018D602 /* UIView+Autolayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E291F3D2540655D0018D602 /* UIView+Autolayout.swift */; };
1E6C67A2230E76CC0019D87B /* SkeletonTransitionStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4CE587C22EEE65200333067 /* SkeletonTransitionStyle.swift */; };
1E6C67A3230E76CE0019D87B /* UIView+Transitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4CE587B22EEE63100333067 /* UIView+Transitions.swift */; };
1EE42E1F23FF25CC00BF665A /* ProcessInfo+XCTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE42E1E23FF25CC00BF665A /* ProcessInfo+XCTest.swift */; };
@@ -161,6 +163,7 @@
17DD0E00207FB27400C56334 /* SkeletonView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SkeletonView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
17DD0E1A207FB2C200C56334 /* SkeletonView-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SkeletonView-tvOS.plist"; sourceTree = "<group>"; };
17DD0E1B207FB2C200C56334 /* SkeletonView-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SkeletonView-iOS.plist"; sourceTree = "<group>"; };
1E291F3D2540655D0018D602 /* UIView+Autolayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Autolayout.swift"; sourceTree = "<group>"; };
1EE42E1E23FF25CC00BF665A /* ProcessInfo+XCTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+XCTest.swift"; sourceTree = "<group>"; };
42ABD06D210B548200BEEFF4 /* SkeletonViewExampleUICollectionView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SkeletonViewExampleUICollectionView.app; sourceTree = BUILT_PRODUCTS_DIR; };
42ABD070210B54E100BEEFF4 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -443,6 +446,7 @@
F5307E2D1FB0E5E400EE67C5 /* UIView+Frame.swift */,
F5804771230ECD0000066D02 /* UIView+IBInspectable.swift */,
F587FB85202CEC95002DB5FE /* UIView+UIApplicationDelegate.swift */,
1E291F3D2540655D0018D602 /* UIView+Autolayout.swift */,
5600784323FD293D00669AD6 /* UITableView+VisibleSections.swift */,
1EE42E1E23FF25CC00BF665A /* ProcessInfo+XCTest.swift */,
);
@@ -726,6 +730,7 @@
buildActionMask = 2147483647;
files = (
872D5A5721C177E20037D763 /* UIView+Extension.swift in Sources */,
1E291F3F2540655D0018D602 /* UIView+Autolayout.swift in Sources */,
17DD0E1F207FB32100C56334 /* RecursiveProtocol.swift in Sources */,
872D5A5D21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */,
17DD0E1E207FB32100C56334 /* PrepareForSkeletonProtocol.swift in Sources */,
@@ -786,6 +791,7 @@
buildActionMask = 2147483647;
files = (
E4CE588B22EEF70D00333067 /* UIView+Transitions.swift in Sources */,
1E291F3E2540655D0018D602 /* UIView+Autolayout.swift in Sources */,
872D5A5621C177E20037D763 /* UIView+Extension.swift in Sources */,
872D5A5C21C24EDD0037D763 /* UILabel+Multiline.swift in Sources */,
F54CF5E42024CEB000330B0D /* UIView+CollectionSkeleton.swift in Sources */,
@@ -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
}
func setHeight(_ height: CGFloat) -> SkeletonMultilineLayerBuilder {
self.height = height
return self
}
@discardableResult
func setHeight(_ height: CGFloat) -> SkeletonMultilineLayerBuilder {
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
+51 -34
View File
@@ -30,12 +30,21 @@ extension CAGradientLayer {
struct SkeletonMultilinesLayerConfig {
var lines: Int
var lineHeight: CGFloat?
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
@@ -47,8 +56,8 @@ extension CALayer {
}
func addMultilinesLayers(for config: SkeletonMultilinesLayerConfig) {
let numberOfSublayers = config.lines == 1 ? 1 : calculateNumLines(for: config)
var height = config.lineHeight ?? SkeletonAppearance.default.multilineHeight
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,9 +86,9 @@ 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
var height = config.lineHeight
if numberOfSublayers == 1 && SkeletonAppearance.default.renderSingleLineAsView {
height = bounds.height
@@ -86,7 +96,11 @@ extension CALayer {
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)
}
}
@@ -98,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 - paddingInsets.bottom - paddingInsets.top)
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
}
}
@@ -126,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() }
@@ -0,0 +1,25 @@
// 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"
+18 -16
View File
@@ -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 {
@@ -23,6 +29,11 @@ public extension UIView {
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonable) as? Bool ?? false }
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 {
get { return ao_get(pkey: &ViewAssociatedKeys.skeletonCornerRadius) as? Float ?? 0.0 }
@@ -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
}
}
@@ -50,7 +79,6 @@ extension UIButton {
backgroundColor = .clear
startTransition { [weak self] in
self?.setTitle(nil, for: .normal)
self?.isUserInteractionEnabled = false
}
}
}
@@ -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 }
}
@@ -55,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) }
}
}
@@ -29,8 +29,12 @@ public extension UITextView {
extension UITextView: ContainsMultilineText {
var multilineTextFont: UIFont? {
return font
}
font
}
var numLines: Int {
-1
}
var lastLineFillingPercent: Int {
get {
+21 -13
View File
@@ -24,14 +24,15 @@ 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
}
}
}
@@ -51,9 +52,13 @@ 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
}
}
}
}
@@ -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
}
}
}
}
@@ -112,8 +119,9 @@ extension UIButton {
override func recoverViewState(forced: Bool) {
super.recoverViewState(forced: forced)
startTransition { [weak self] in
self?.setTitle(self?.buttonState?.title, for: .normal)
self?.isUserInteractionEnabled = self?.buttonState?.isUserInteractionsEnabled ?? false
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
}
}
@@ -48,10 +44,8 @@ struct RecoverableImageViewState {
struct RecoverableButtonViewState {
var title: String?
var isUserInteractionsEnabled: Bool
init(view: UIButton) {
self.title = view.titleLabel?.text
self.isUserInteractionsEnabled = view.isUserInteractionEnabled
}
}
+4 -3
View File
@@ -18,8 +18,8 @@ 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]
@@ -62,7 +62,7 @@ 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
@@ -77,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
+6 -8
View File
@@ -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
+14 -11
View File
@@ -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)
}
+6
View File
@@ -114,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()
@@ -183,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)
+1 -1
View File
@@ -12,7 +12,7 @@ Install _fastlane_ using
```
[sudo] gem install fastlane -NV
```
or alternatively using `brew cask install fastlane`
or alternatively using `brew install fastlane`
# Available Actions
### release_current