如今几乎所有的应用程序都有异步过程,如 API 请求、长时间运行的过程等。当这些过程在进行时,通常开发人员会放置一个加载视图来向用户显示有事情在进行。
SkeletonView 就是为了解决这个问题而诞生的,以一种优雅的方式向用户显示有事情在进行,并且为用户准备等待的内容。
享受它!
🌟 功能
- 易于使用
- 所有 UIView 都可 skeletonify
- 完全可定制
- 通用(iPhone & iPad)
- 与 Interface Builder 兼容
- 简单的 Swift 语法
- 轻量且易于阅读的代码库
🎬 指南
SkeletonView 指南 - 入门 | 如何使用 Swift 5.2 创建加载视图 by iKh4ever Studio | 在应用中创建 Skeleton 加载视图(Swift 5) - Xcode 11, 2020 by iOS Academy | 如何创建 iOS 中的数据加载动画 by MoureDev |
📲 安装
pod 'SkeletonView'
github "Juanpe/SkeletonView"
dependencies: [
.package(url: "https://github.com/Juanpe/SkeletonView.git", from: "1.7.0")
]
📣 重要!从版本 1.30.0 开始,
SkeletonView
支持了 XCFrameworks,如果您想将其作为 XCFramework 安装,请使用 这个仓库。
🐒 使用
使用 SkeletonView
需要 3 个步骤
import SkeletonView
skeletonables
。您可以通过以下两种方式实现
使用代码
avatarImageView.isSkeletonable = true
使用 IB/Storyboards
(1) view.showSkeleton() // Solid
(2) view.showGradientSkeleton() // Gradient
(3) view.showAnimatedSkeleton() // Solid animated
(4) view.showAnimatedGradientSkeleton() // Gradient animated
预览
实心 | 渐变 | 实心动画 | 渐变动画 |
![]() |
![]() |
![]() |
![]() |
📣 重要!
SkeletonView
是递归的,因此,如果您想在所有可骨架视图显示骨骼,您只需要在主容器视图中调用 show 方法。例如,使用UIViewControllers
。
🌿 集合
SkeletonView
与 UITableView
和 UICollectionView
兼容。
UITableView
如果您想在 UITableView
中显示骨骼,则需要遵守 SkeletonTableViewDataSource
协议。
public protocol SkeletonTableViewDataSource: UITableViewDataSource {
func numSections(in collectionSkeletonView: UITableView) -> Int // Default: 1
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
func collectionSkeletonView(_ skeletonView: UITableView, skeletonCellForRowAt indexPath: IndexPath) -> UITableViewCell? // Default: nil
func collectionSkeletonView(_ skeletonView: UITableView, prepareCellForSkeleton cell: UITableViewCell, at indexPath: IndexPath)
}
如您所见,此协议继承自 UITableViewDataSource
,因此您可以用骨架协议替换此协议。
此协议对某些方法提供了默认实现。例如,每个部分中的行数是在运行时计算的
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
// Default:
// It calculates how many cells need to populate whole tableview
📣 重要!如果您在上述方法中返回
UITableView.automaticNumberOfSkeletonRows
,则它将像默认行为一样工作(即它计算需要多少单元格来填充整个表视图)。
您需要实现一个方法来让 Skeleton 知晓单元格标识符。此方法没有默认实现
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "CellIdentifier"
}
默认情况下,库从每个 indexPath 中脱接单元格,但您也可以这样做,如果您想在骨骼出现之前进行一些更改
func collectionSkeletonView(_ skeletonView: UITableView, skeletonCellForRowAt indexPath: IndexPath) -> UITableViewCell? {
let cell = skeletonView.dequeueReusableCell(withIdentifier: "CellIdentifier", for: indexPath) as? Cell
cell?.textField.isHidden = indexPath.row == 0
return cell
}
如果您希望将脱接部分留给库,则可以使用此方法配置单元格
func collectionSkeletonView(_ skeletonView: UITableView, prepareCellForSkeleton cell: UITableViewCell, at indexPath: IndexPath) {
let cell = cell as? Cell
cell?.textField.isHidden = indexPath.row == 0
}
此外,您还可以对标题和页脚进行骨骼化。您需要遵守 SkeletonTableViewDelegate
协议。
public protocol SkeletonTableViewDelegate: UITableViewDelegate {
func collectionSkeletonView(_ skeletonView: UITableView, identifierForHeaderInSection section: Int) -> ReusableHeaderFooterIdentifier? // default: nil
func collectionSkeletonView(_ skeletonView: UITableView, identifierForFooterInSection section: Int) -> ReusableHeaderFooterIdentifier? // default: nil
}
📣 重要!
1️⃣ 如果您正在使用可调整大小的单元格(tableView.rowHeight = UITableViewAutomaticDimension
),则必须定义estimatedRowHeight
。
2️⃣ 当您在UITableViewCell
中添加元素时,应将其添加到contentView
,而不是直接添加到单元格。self.contentView.addSubview(titleLabel) ✅ self.addSubview(titleLabel) ❌
UICollectionView
对于 UICollectionView
,您需要遵守 SkeletonCollectionViewDataSource
协议。
public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
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
func collectionSkeletonView(_ skeletonView: UICollectionView, skeletonCellForItemAt indexPath: IndexPath) -> UICollectionViewCell? // default: nil
func collectionSkeletonView(_ skeletonView: UICollectionView, prepareCellForSkeleton cell: UICollectionViewCell, at indexPath: IndexPath)
func collectionSkeletonView(_ skeletonView: UICollectionView, prepareViewForSkeleton view: UICollectionReusableView, at indexPath: IndexPath)
}
其余的过程与 UITableView
相同
🔠 文本
当使用带文本的元素时,SkeletonView
会在线条上画线来模拟文本。
您可以为多行元素设置一些属性。
要使用代码修改百分比或半径,设置属性
descriptionTextView.lastLineFillPercent = 50
descriptionTextView.linesCornerRadius = 5
或者,如果您喜欢使用 IB/Storyboard
如何定义行数?
默认情况下,行数与 numberOfLines
属性的值相同。如果设置为 zero,则它将计算填充整个骨骼所需的行数,并绘制。
但是,如果您想设置特定的骨架行数,可以通过设置 skeletonTextNumberOfLines
属性来实现。此属性有两个可能的值,inherited
返回 numberOfLines
的值,以及 custom(Int)
返回指定的特定行数。
例如
label.skeletonTextNumberOfLines = 3 // .custom(3)
⚠️ 已弃用!useFontLineHeight 已弃用。您可以使用 skeletonTextLineHeight 代替
descriptionTextView.skeletonTextLineHeight = .relativeToFont
📣 重要!请注意,对于没有多行的视图,单行将被视为最后一行。
🦋 外观
骨架默认具有默认外观。因此,当您没有指定颜色、渐变或多行属性时,SkeletonView
将使用默认值。
默认值
- tint颜色 :
UIColor
- 默认值:
.skeletonDefault
(与.clouds
相同,但适配暗黑模式)
- 默认值:
- 渐变 : SkeletonGradient
- 默认值:
SkeletonGradient(baseColor: .skeletonDefault)
- 默认值:
- 多行高度 :
CGFloat
- 默认值: 15
- 多行间距 :
CGFloat
- 默认值: 10
- 多行最后一行填充百分比 :
Int
- 默认值: 70
- 多行边角半径 :
Int
- 默认值: 0
- 骨架边角半径 :
CGFloat
(IBInspectable)(为您的骨架视图添加边角)- 默认值: 0
要获取这些默认值,您可以使用 SkeletonAppearance.default
。使用此属性您也可以设置值
SkeletonAppearance.default.multilineHeight = 20
SkeletonAppearance.default.tintColor = .green
⚠️ 已弃用!useFontLineHeight 已过时。您可以使用 textLineHeight 代替
SkeletonAppearance.default.textLineHeight = .relativeToFont
🎨 自定义颜色
您可以选择骨架的着色颜色。您只需要传递您想要的颜色或渐变作为参数。
使用纯色
view.showSkeleton(usingColor: UIColor.gray) // Solid
// or
view.showSkeleton(usingColor: UIColor(red: 25.0, green: 30.0, blue: 255.0, alpha: 1.0))
使用渐变
let gradient = SkeletonGradient(baseColor: UIColor.midnightBlue)
view.showGradientSkeleton(usingGradient: gradient) // Gradient
此外,SkeletonView具有20种平面颜色 🤙🏼
UIColor.turquoise, UIColor.greenSea, UIColor.sunFlower, UIColor.flatOrange ...
https://flatuicolors.com
网站截图🏃♀️ 动画
SkeletonView有两个内置动画,对于实心骨架的 脉冲 以及对于渐变的 滑动。
此外,如果您想创建自己的骨架动画,这真的很简单。
Skeleton提供了一个showAnimatedSkeleton
函数,其中包含一个SkeletonLayerAnimation
闭包,在其中您可以定义自己的自定义动画。
public typealias SkeletonLayerAnimation = (CALayer) -> CAAnimation
您可以像这样调用该函数
view.showAnimatedSkeleton { (layer) -> CAAnimation in
let animation = CAAnimation()
// Customize here your animation
return animation
}
它还有 SkeletonAnimationBuilder
。它是一个构建器,用于创建SkeletonLayerAnimation
。
今天,您可以创建用于渐变的滑动动画,确定动画的 方向 和设置动画的 持续时间(默认为1.5秒)。
// func makeSlidingAnimation(withDirection direction: GradientDirection, duration: CFTimeInterval = 1.5) -> SkeletonLayerAnimation
let animation = SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: .leftToRight)
view.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation)
GradientDirection
是一个枚举,具有以下情况
方向 | 预览 |
---|---|
.leftRight | ![]() |
.rightLeft | ![]() |
.topBottom | ![]() |
.bottomTop | ![]() |
.topLeftBottomRight | ![]() |
.bottomRightTopLeft | ![]() |
😉 小贴士!还有一种方式可以创建滑动动画,只需使用此快捷方式
let animation = GradientDirection.leftToRight.slidingAnimation()
🏄 过渡
SkeletonView有两个内置过渡,以更平滑的方式显示或隐藏骨架
要使用过渡,只需将transition
参数添加到您的showSkeleton()
或hideSkeleton()
函数中,并带有过渡时间,如下所示
view.showSkeleton(transition: .crossDissolve(0.25)) //Show skeleton cross dissolve transition with 0.25 seconds fade time
view.hideSkeleton(transition: .crossDissolve(0.25)) //Hide skeleton cross dissolve transition with 0.25 seconds fade time
默认值是crossDissolve(0.25)
预览
无 | 交叉溶解 |
![]() |
![]() |
✨ 杂项
层次结构
由于SkeletonView
是递归的,我们希望骨架非常高效,因此我们希望尽快停止递归。因此,您必须将容器视图设置为Skeletonable
,因为骨架会停止在视图不是Skeletonable的情况下寻找skeletonable
子视图,从而打破递归。
因为图片胜于千言万语
在这个示例中,我们有一个UIViewController
和一个ContainerView
以及一个UITableView
。当视图准备好时,我们使用此方法显示骨架
view.showSkeleton()
isSkeletonable
=☠️
配置 | 结果 |
---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Skeleton视图布局
有时骨架布局可能无法适应您的布局,因为父视图的边界已更改。例如,旋转设备。
您可以按如下方式重新布局骨架视图:
override func viewDidLayoutSubviews() {
view.layoutSkeletonIfNeeded()
}
📣 重要!您不应该调用此方法。从 版本 1.8.1 开始,您不需要调用此方法,库会自动执行。因此,您只有在需要手动更新骨架布局的情况下才能使用此方法。
更新骨架
您可以通过以下方法随时更改骨架配置,例如颜色、动画等。
(1) view.updateSkeleton() // Solid
(2) view.updateGradientSkeleton() // Gradient
(3) view.updateAnimatedSkeleton() // Solid animated
(4) view.updateAnimatedGradientSkeleton() // Gradient animated
动画开始时隐藏视图
有时您想在动画开始时隐藏一些视图,因此有一个快捷属性可以帮助实现这一功能。
view.isHiddenWhenSkeletonIsActive = true // This works only when isSkeletonable = true
当骨架激活时不要修改用户交互
默认情况下,骨架化项的用户交互被禁用,但如果您不希望在骨架激活时修改用户交互指示器,可以使用 isUserInteractionDisabledWhenSkeletonIsActive
属性。
view.isUserInteractionDisabledWhenSkeletonIsActive = false // The view will be active when the skeleton will be active.
不要为标签中的骨架线条使用字体行高
使用 false
禁用骨架自动调整字体高度以针对 UILabel
或 UITextView
。默认情况下,骨架线条高度会自动调整到字体高度,以更准确地反映标签矩形内的文本,而不是使用边界框。
label.useFontLineHeight = false
延迟显示骨架
如果视图更新很快,您可以延迟显示骨架。
func showSkeleton(usingColor: UIColor,
animated: Bool,
delay: TimeInterval,
transition: SkeletonTransitionStyle)
func showGradientSkeleton(usingGradient: SkeletonGradient,
animated: Bool,
delay: TimeInterval,
transition: SkeletonTransitionStyle)
调试
为了帮助进行调试任务,当某些地方工作不正常时,SkeletonView
有一些新工具。
首先,UIView
有一个具有其骨架信息的属性。
var sk.skeletonTreeDescription: String
此外,您还可以激活新的 调试模式。您只需添加环境变量 SKELETON_DEBUG
并激活它。
然后,当骨架出现时,您可以在 Xcode 控制台中看到视图层次结构。
{
"type" : "UIView", // UITableView, UILabel...
"isSkeletonable" : true,
"reference" : "0x000000014751ce30",
"children" : [
{
"type" : "UIView",
"isSkeletonable" : true,
"children" : [ ... ],
"reference" : "0x000000014751cfa0"
}
]
}
支持的操作系统和 SDK 版本
- iOS 9.0+
- tvOS 9.0+
- Swift 5.3
❤️ 贡献
这是一个开源项目,所以请随时贡献。如何?
查看所有贡献者。
有关更多信息,请阅读贡献指南。
📢 提及
- iOS Dev Weekly #327
- Hacking with Swift 文章
- 11 月 Swift 顶级文章
- 30 个惊人的 iOS Swift 库(v2018)
- AppCoda Weekly #44
- iOS Cookies Newsletter #103
- Swift Developments Newsletter #113
- iOS Goodies #204
- Swift Weekly #96
- CocoaControls
- Awesome iOS Newsletter #74
- Swift News #36
- 最佳 iOS 文章、新工具等
🏆 赞助商
开源项目没有您的帮助无法长久生存。如果您发现 SkeletonView 有用,请考虑通过成为赞助人来支持此项目。
通过GitHub Sponsors 成为赞助人。
👨🏻💻 作者
👮🏻 许可证
MIT License
Copyright (c) 2017 Juanpe Catalán
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.