Skip to content

Commit 52b18e3

Browse files
committed
Add list footer
1 parent d00eed3 commit 52b18e3

File tree

5 files changed

+187
-115
lines changed

5 files changed

+187
-115
lines changed

RELEASE_NOTES.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ SwiftUIKit makes its best effort to honor semver, but breaking changes can occur
44

55

66

7+
## 5.9.1
8+
9+
This version improves the plain list content capabilities of the SDK.
10+
11+
### ✨ Features
12+
13+
* `PlainListContent` is a wrapper that removes any list decorations from a view.
14+
* `ListHeader` and `ListFooter` are `PlainListContent` wrappers that perform header/footer specific adjustments.
15+
16+
17+
718
## 5.9
819

920
This version deprecates progress types, since a native `Gauge` can be used instead.

Sources/SwiftUIKit/Lists/ListHeader.swift

Lines changed: 0 additions & 115 deletions
This file was deleted.
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
//
2+
// PlainListContent.swift
3+
// SwiftUIKit
4+
//
5+
// Created by Daniel Saidi on 2023-06-01.
6+
// Copyright © 2023-2025 Daniel Saidi. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
11+
/// This view can be used to add a plain content view to any
12+
/// part of a `List`.
13+
private struct PlainListContent<Content: View>: View {
14+
15+
public init(
16+
@ViewBuilder content: @escaping () -> Content
17+
) {
18+
self.content = content
19+
}
20+
21+
private let content: (() -> Content)
22+
23+
public var body: some View {
24+
content()
25+
.frame(maxWidth: .infinity)
26+
.listRowBackground(Color.clear)
27+
.prefersListRowSeparatorHidden()
28+
}
29+
}
30+
31+
/// This view can be used to add a plain footer to a `List`.
32+
///
33+
/// The view adds negative padding to adjust for the spacing
34+
/// to the aligning content on certain platforms.
35+
public struct ListFooter<Content: View>: View {
36+
37+
public init(
38+
@ViewBuilder content: @escaping () -> Content
39+
) {
40+
#if os(iOS)
41+
self.padding = -10
42+
#else
43+
self.padding = 0
44+
#endif
45+
self.content = content
46+
}
47+
48+
private let padding: Double
49+
private let content: (() -> Content)
50+
51+
public var body: some View {
52+
PlainListContent(content: content)
53+
.padding(.top, padding)
54+
}
55+
}
56+
57+
/// This view can be used to add a plain header to a `List`.
58+
///
59+
/// The view adds negative padding to adjust for the spacing
60+
/// to the aligning content on certain platforms.
61+
public struct ListHeader<Content: View>: View {
62+
63+
/// Create a list header.
64+
///
65+
/// - Parameters:
66+
/// - content: The header view.
67+
public init(
68+
@ViewBuilder content: @escaping () -> Content
69+
) {
70+
#if os(iOS)
71+
self.padding = -10
72+
#else
73+
self.padding = 0
74+
#endif
75+
self.content = content
76+
}
77+
78+
private let padding: Double
79+
private let content: (() -> Content)
80+
81+
public var body: some View {
82+
PlainListContent(content: content)
83+
.padding(.bottom, padding)
84+
}
85+
}
86+
87+
private extension View {
88+
89+
@ViewBuilder
90+
func prefersListRowSeparatorHidden() -> some View {
91+
#if os(tvOS) || os(watchOS)
92+
self
93+
#else
94+
if #available(iOS 15.0, macOS 13.0, watchOS 9.0, *) {
95+
self.listRowSeparator(.hidden)
96+
} else {
97+
self
98+
}
99+
#endif
100+
}
101+
}
102+
103+
#Preview {
104+
105+
func item() -> some View {
106+
Text("Preview.Item", bundle: .module)
107+
}
108+
109+
return VStack {
110+
List {
111+
ListHeader {
112+
Color.red.frame(square: 150)
113+
}
114+
Section {
115+
item()
116+
item()
117+
item()
118+
item()
119+
}
120+
ListFooter {
121+
Color.red.frame(square: 150)
122+
}
123+
}
124+
}
125+
}

Sources/SwiftUIKit/SwiftUIKit.docc/SwiftUIKit.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,15 @@ SwiftUIKit is available under the MIT license.
104104
- ``ListCardButtonStyle``
105105
- ``ListCardStyle``
106106
- ``ListDragHandle``
107+
- ``ListFooter``
107108
- ``ListHeader``
108109
- ``ListPadding``
109110
- ``ListSectionTitle``
110111
- ``ListSelectItem``
111112
- ``ListShelfSection``
112113
- ``ListShelfSectionStyle``
113114
- ``ListSubtitle``
115+
- ``PlainListContent``
114116
- ``Reorderable``
115117
- ``ReorderableForEach``
116118
- ``SidebarListRowBackgroundModifier``
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import SwiftUI
2+
3+
public extension ListHeader {
4+
5+
@available(*, deprecated, message: "Apply a custom bottom padding directly to the view instead.")
6+
@_disfavoredOverload
7+
init(
8+
bottomPadding: Double? = nil,
9+
@ViewBuilder view: @escaping () -> Content
10+
) {
11+
self.init(content: view)
12+
}
13+
}
14+
15+
@available(*, deprecated, message: "Use ListHeader directly instead")
16+
public extension View {
17+
18+
/// Convert the view to a list header.
19+
///
20+
/// - Parameters:
21+
/// - bottomPadding: A custom bottom padding, if any.
22+
func listHeader(
23+
bottomPadding: Double? = nil
24+
) -> some View {
25+
ListHeader(bottomPadding: bottomPadding) {
26+
self
27+
}
28+
}
29+
}
30+
31+
@available(*, deprecated, message: "Use ListHeader directly instead")
32+
public extension Image {
33+
34+
/// Convert the image to a list header.
35+
///
36+
/// - Parameters:
37+
/// - height: The image height, if any.
38+
/// - bottomPadding: A custom bottom padding, if any.
39+
@MainActor func listHeader(
40+
height: CGFloat?,
41+
bottomPadding: Double? = nil
42+
) -> some View {
43+
ListHeader {
44+
self.resizable()
45+
.aspectRatio(contentMode: .fit)
46+
.frame(height: height)
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)