ScrollViewSectionKit
一个 SwiftUI 库,允许您在 ScrollView
SwiftUI 组件中添加原生(plain
、grouped
、insetGrouped
)或自定义的分节样式。
概览
- 💎 100% 使用 SwiftUI 制作。
- 🚀 轻量级,无外部依赖。
- ✅ API 力求模仿 Apple 的
List
组件,同时您也可以在其中使用ScrollView
组件。 - 🎨 预定义的
.plain
、.grouped
和.insetGrouped
分节样式,但您也可以完全自定义分节样式。 - 🧪 在多个生产应用程序中进行测试。
- 📑 详细的 API 文档。
要求
- iOS 14.0+
- watchOS 7.0+
- tvOS 14.0+
- macOS 11.0+
安装
Swift Package Manager
ScrollViewSectionKit
通过 Swift Package Manager 可用。
https://github.com/pavolkmet/ScrollViewSectionKit
CocoaPods
ScrollViewSectionKit
通过 CocoaPods 可用。要安装它,只需将以下行添加到 Podfile 中
pod 'ScrollViewSectionKit'
然后只需运行 pod install
手动安装
将 ScrollViewSectionKit
中的 Source
文件夹中的所有文件拖放到您的项目中,并选择 如有需要则复制项目
使用
import ScrollViewSectionKit
struct ContentView: View {
var body: some View {
ScrollView {
VStack(spacing: 0.0) {
ScrollViewSection {
Text("This is a 1st row.")
Text("This is a 2nd row.")
} header: {
Text("Section header".uppercased())
} footer: {
Text("Section footer")
}
}
}
.scrollViewSectionStyle(.insetGrouped)
.scrollViewSectionBackgroundColor(.clear)
.background {
Color(uiColor: UIColor.systemGroupedBackground)
.ignoresSafeArea()
}
}
}
查看 示例应用程序 以获取所有示例和高级用法。
配置
分节 - 样式
您可以通过使用 scrollViewSectionStyle(_ style: any ScrollViewSectionStyle)
修改器来自定义分节的显示。此修改器可用于全局或针对每个分节。库已经包含 .plain
、.grouped
和 .insetGrouped
样式。
在以下示例中,第一个 ScrollViewSection
使用了 .grouped
样式,因为它被设置为 ScrollView
组件的一部分,但第二个 ScrollViewSection
使用了 .insetGrouped
样式。这是因为内部使用了 scrollViewSectionStyle(_ style: any ScrollViewSectionStyle)
修改器,它使用了 EnvironmentKey
。这意味着您可以使用此修改器为整个应用程序设置全局样式。
import ScrollViewSectionKit
struct ContentView: View {
var body: some View {
ScrollView {
VStack(spacing: 0.0) {
ScrollViewSection {
Text("This is a 1st row.")
Text("This is a 2nd row.")
} header: {
Text("Section header".uppercased())
} footer: {
Text("Section footer")
}
ScrollViewSection {
Text("This is a 1st row.")
Text("This is a 2nd row.")
} header: {
Text("Section header".uppercased())
} footer: {
Text("Section footer")
}
.scrollViewSectionStyle(.insetGrouped)
}
}
.scrollViewSectionStyle(.grouped)
.scrollViewSectionBackgroundColor(.clear)
.background {
Color(uiColor: UIColor.systemGroupedBackground)
.ignoresSafeArea()
}
}
}
它应该看起来像这样 ⬇️
分节 - 背景颜色
您可以通过使用 scrollViewSectionBackgroundColor(_ color: Color)
修改器来使用不同的背景颜色自定义分节的表现。此修改器可用于全局或针对每个分节。
以下示例中,第一个ScrollViewSection
使用.blue.opacity(0.28)
背景色,因为这个颜色被设置为ScrollView
组件的一部分。然而,第二个ScrollViewSection
使用.orange.opacity(0.28)
背景色。这是因为底层中,scrollViewSectionBackgroundColor(_ color: Color)
修改器正在使用EnvironmentKey
。这意味着你可以使用这个修改器为整个应用程序设置全局样式。
import ScrollViewSectionKit
struct ContentView: View {
var body: some View {
ScrollView {
VStack(spacing: 0.0) {
ScrollViewSection {
Text("This is a 1st row.")
Text("This is a 2nd row.")
} header: {
Text("Section header".uppercased())
} footer: {
Text("Section footer")
}
ScrollViewSection {
Text("This is a 1st row.")
Text("This is a 2nd row.")
} header: {
Text("Section header".uppercased())
} footer: {
Text("Section footer")
}
.scrollViewSectionStyle(.insetGrouped)
.scrollViewSectionBackgroundColor(.orange.opacity(0.28))
}
}
.scrollViewSectionStyle(.grouped)
.scrollViewSectionBackgroundColor(.blue.opacity(0.28))
.background {
Color(uiColor: UIColor.systemGroupedBackground)
.ignoresSafeArea()
}
}
}
它应该看起来像这样 ⬇️
行 - 背景颜色
你可以通过应用不同的背景色来使用scrollViewRowBackgroundColor(_ color: Color?)
修改器来自定义行的外观。这个修改器可以在每个部分或每行使用。
在以下示例中,第一个部分的第二行使用.blue.opacity(0.28)
背景色,因为这已经是行的一部分设置的。另一方面,第二个部分的第一个行使用.green.opacity(0.28)
背景色,因为整个部分使用了此修改器,第二个部分的最后一行则使用.orange.opacity(0.28)
背景色。
import ScrollViewSectionKit
struct ContentView: View {
var body: some View {
ScrollView {
VStack(spacing: 0.0) {
ScrollViewSection {
Text("This is a 1st row.")
Text("This is a 2nd row.")
.scrollViewRowBackgroundColor(.blue.opacity(0.28))
} header: {
Text("Section header".uppercased())
} footer: {
Text("Section footer")
}
ScrollViewSection {
Text("This is a 1st row.")
Text("This is a 2nd row.")
.scrollViewRowBackgroundColor(.orange.opacity(0.28))
} header: {
Text("Section header".uppercased())
} footer: {
Text("Section footer")
}
.scrollViewSectionStyle(.insetGrouped)
.scrollViewRowBackgroundColor(.green.opacity(0.28))
}
}
.scrollViewSectionStyle(.grouped)
.scrollViewSectionBackgroundColor(.clear)
.background {
Color(uiColor: UIColor.systemGroupedBackground)
.ignoresSafeArea()
}
}
}
它应该看起来像这样 ⬇️
行 - 边距
你还可以通过应用不同的行内边距来使用scrollViewRowInsets(_ insets: EdgeInsets?)
修改器来自定义行的外观。这个修改器可以在每个部分或每行使用。
在以下示例中,整个第一个部分使用EdgeInsets(top: 0.0, leading: 40.0, bottom: 0.0, trailing: 0.0)
行内边距,因为整个部分使用了此修改器。另一方面,第二个部分的第一个行使用EdgeInsets(top: 0.0, leading: 40.0, bottom: 0.0, trailing: 0.0)
行内边距,因为这只有一个行使用了此修改器,最后一行则使用默认的行内边距。
import ScrollViewSectionKit
struct ContentView: View {
var body: some View {
ScrollView {
VStack(spacing: 0.0) {
ScrollViewSection {
Text("This is a 1st row.")
Text("This is a 2nd row.")
} header: {
Text("Section header".uppercased())
} footer: {
Text("Section footer")
}
.scrollViewRowInsets(EdgeInsets(top: 0.0, leading: 40.0, bottom: 0.0, trailing: 0.0))
ScrollViewSection {
HStack(spacing: 10.0) {
Image(systemName: "person.fill")
.resizable()
.scaledToFit()
.frame(width: 20.0, height: 20.0)
Text("This is a 1st row.")
}
.scrollViewRowInsets(EdgeInsets(top: 0.0, leading: 40.0, bottom: 0.0, trailing: 0.0))
Text("This is a 2nd row.")
} header: {
Text("Section header".uppercased())
} footer: {
Text("Section footer")
}
.scrollViewSectionStyle(.insetGrouped)
}
}
.scrollViewSectionStyle(.grouped)
.scrollViewSectionBackgroundColor(.clear)
.background {
Color(uiColor: UIColor.systemGroupedBackground)
.ignoresSafeArea()
}
}
}
它应该看起来像这样 ⬇️
行 - 分隔线边距
在之前的示例中,我们调整了行内边距,但分隔符仍然使用默认的内边距。我们可以通过使用scrollViewRowSeparatorInsets(_ insets: EdgeInsets?)
修改器来改变这一点。此修改器可以在每个部分或每行使用。
在以下示例中,整个第一个部分使用EdgeInsets(top: 0.0, leading: 40.0, bottom: 0.0, trailing: 0.0)
行分隔内边距,因为整个部分使用了此修改器。另一方面,第二个部分的第一个行使用EdgeInsets(top: 0.0, leading: 70.0, bottom: 0.0, trailing: 0.0)
行分隔内边距,因为这只有一个行使用了此修改器,最后一行使用默认的行分隔内边距。
import ScrollViewSectionKit
struct ContentView: View {
var body: some View {
ScrollView {
VStack(spacing: 0.0) {
ScrollViewSection {
Text("This is a 1st row.")
Text("This is a 2nd row.")
} header: {
Text("Section header".uppercased())
} footer: {
Text("Section footer")
}
.scrollViewRowInsets(EdgeInsets(top: 0.0, leading: 40.0, bottom: 0.0, trailing: 0.0))
.scrollViewRowSeparatorInsets(EdgeInsets(top: 0.0, leading: 40.0, bottom: 0.0, trailing: 0.0))
ScrollViewSection {
HStack(spacing: 10.0) {
Image(systemName: "person.fill")
.resizable()
.scaledToFit()
.frame(width: 20.0, height: 20.0)
Text("This is a 1st row.")
}
.scrollViewRowInsets(EdgeInsets(top: 0.0, leading: 40.0, bottom: 0.0, trailing: 0.0))
.scrollViewRowSeparatorInsets(EdgeInsets(top: 0.0, leading: 70.0, bottom: 0.0, trailing: 0.0))
Text("This is a 2nd row.")
} header: {
Text("Section header".uppercased())
} footer: {
Text("Section footer")
}
.scrollViewSectionStyle(.insetGrouped)
}
}
.scrollViewSectionStyle(.grouped)
.scrollViewSectionBackgroundColor(.clear)
.background {
Color(uiColor: UIColor.systemGroupedBackground)
.ignoresSafeArea()
}
}
}
它应该看起来像这样 ⬇️
行 - 分隔线色调
你可以通过应用不同的调色板颜色来使用scrollViewRowSeparatorTint(_ color: Color?)
修改器来自定义行的分隔外观。此修改器可以在每个部分或每行使用。
在以下示例中,第一个部分的第二行的分隔使用.blue
调色板颜色,因为这已经是行的一部分设置的。另一方面,第二个部分的第一个和第三行的分隔使用.green
调色板颜色,因为整个部分使用了此修改器,同一部分的第二行分隔则使用.orange
调色板颜色。
import ScrollViewSectionKit
struct ContentView: View {
var body: some View {
ScrollView {
VStack(spacing: 0.0) {
ScrollViewSection {
Text("This is a 1st row.")
Text("This is a 2nd row.")
.scrollViewRowSeparatorTint(.blue)
Text("This is a 3nd row.")
} header: {
Text("Section header".uppercased())
} footer: {
Text("Section footer")
}
ScrollViewSection {
Text("This is a 1st row.")
Text("This is a 2nd row.")
.scrollViewRowSeparatorTint(.orange)
Text("This is a 3nd row.")
} header: {
Text("Section header".uppercased())
} footer: {
Text("Section footer")
}
.scrollViewSectionStyle(.insetGrouped)
.scrollViewRowSeparatorTint(.green)
}
}
.scrollViewSectionStyle(.grouped)
.scrollViewSectionBackgroundColor(.clear)
.background {
Color(uiColor: UIColor.systemGroupedBackground)
.ignoresSafeArea()
}
}
}
它应该看起来像这样 ⬇️
行 - 右键菜单
你还可以通过使用scrollViewRowContextMenu(@ViewBuilder _ menuItems: @escaping () -> some View)
修改器为行添加上下文菜单。此修改器可以在每个部分或每行使用。
在以下示例中,第一个部分的第二行使用多个按钮,它们之间由Divider
组件分隔。
import ScrollViewSectionKit
struct ContentView: View {
var body: some View {
ScrollView {
VStack(spacing: 0.0) {
ScrollViewSection {
Text("This is a 1st row.")
Text("This is a 2nd row.")
.scrollViewRowContextMenu {
Button {
} label: {
Label("Star", systemImage: "star")
}
Button {
} label: {
Label("Profile", systemImage: "person")
}
Divider()
Button(role: .destructive) {
} label: {
Label("Block", systemImage: "hand.raised")
}
}
} header: {
Text("Section header".uppercased())
} footer: {
Text("Section footer")
}
ScrollViewSection {
Text("This is a 1st row.")
Text("This is a 2nd row.")
} header: {
Text("Section header".uppercased())
} footer: {
Text("Section footer")
}
.scrollViewSectionStyle(.insetGrouped)
}
}
.scrollViewSectionStyle(.grouped)
.scrollViewSectionBackgroundColor(.clear)
.background {
Color(uiColor: UIColor.systemGroupedBackground)
.ignoresSafeArea()
}
}
}
它应该看起来像这样 ⬇️
鸣谢
- Moving Parts关于
SwiftUI under the Hood: Variadic Views
的精彩文章,这篇文章使得所有这一切都成为可能。 - Federico Zanetello再次提供了一个关于如何在SwiftUI中创建自定义视图样式的精彩文章,这在我开发这个库的过程中帮了我很多。
作者
- Twitter: @PavolKmet
许可证
MIT License
Copyright (c) 2023 Pavol Kmet
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.