mirror of
https://github.com/Dimillian/Skills.git
synced 2026-03-25 08:55:54 +00:00
Introduces the 'swiftui-ui-patterns' skill to docs/skills.json, providing best practices and example-driven guidance for building SwiftUI views and components. Adds SKILL.md and a comprehensive set of reference files covering TabView, NavigationStack, sheets, forms, controls, grids, overlays, haptics, focus handling, media, matched transitions, split views, and more.
87 lines
2.3 KiB
Markdown
87 lines
2.3 KiB
Markdown
# ScrollView and Lazy stacks
|
||
|
||
## Intent
|
||
|
||
Use `ScrollView` with `LazyVStack`, `LazyHStack`, or `LazyVGrid` when you need custom layout, mixed content, or horizontal/ grid-based scrolling.
|
||
|
||
## Core patterns
|
||
|
||
- Prefer `ScrollView` + `LazyVStack` for chat-like or custom feed layouts.
|
||
- Use `ScrollView(.horizontal)` + `LazyHStack` for chips, tags, avatars, and media strips.
|
||
- Use `LazyVGrid` for icon/media grids; prefer adaptive columns when possible.
|
||
- Use `ScrollViewReader` for scroll-to-top/bottom and anchor-based jumps.
|
||
- Use `safeAreaInset(edge:)` for input bars that should stick above the keyboard.
|
||
|
||
## Example: vertical custom feed
|
||
|
||
```swift
|
||
@MainActor
|
||
struct ConversationView: View {
|
||
private enum Constants { static let bottomAnchor = "bottom" }
|
||
@State private var scrollProxy: ScrollViewProxy?
|
||
|
||
var body: some View {
|
||
ScrollViewReader { proxy in
|
||
ScrollView {
|
||
LazyVStack {
|
||
ForEach(messages) { message in
|
||
MessageRow(message: message)
|
||
.id(message.id)
|
||
}
|
||
Color.clear.frame(height: 1).id(Constants.bottomAnchor)
|
||
}
|
||
.padding(.horizontal, .layoutPadding)
|
||
}
|
||
.safeAreaInset(edge: .bottom) {
|
||
MessageInputBar()
|
||
}
|
||
.onAppear {
|
||
scrollProxy = proxy
|
||
withAnimation {
|
||
proxy.scrollTo(Constants.bottomAnchor, anchor: .bottom)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## Example: horizontal chips
|
||
|
||
```swift
|
||
ScrollView(.horizontal, showsIndicators: false) {
|
||
LazyHStack(spacing: 8) {
|
||
ForEach(chips) { chip in
|
||
ChipView(chip: chip)
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## Example: adaptive grid
|
||
|
||
```swift
|
||
let columns = [GridItem(.adaptive(minimum: 120))]
|
||
|
||
ScrollView {
|
||
LazyVGrid(columns: columns, spacing: 8) {
|
||
ForEach(items) { item in
|
||
GridItemView(item: item)
|
||
}
|
||
}
|
||
.padding(8)
|
||
}
|
||
```
|
||
|
||
## Design choices to keep
|
||
|
||
- Use `Lazy*` stacks when item counts are large or unknown.
|
||
- Use non-lazy stacks for small, fixed-size content to avoid lazy overhead.
|
||
- Keep IDs stable when using `ScrollViewReader`.
|
||
- Prefer explicit animations (`withAnimation`) when scrolling to an ID.
|
||
|
||
## Pitfalls
|
||
|
||
- Avoid nesting scroll views of the same axis; it causes gesture conflicts.
|
||
- Don’t combine `List` and `ScrollView` in the same hierarchy without a clear reason.
|
||
- Overuse of `LazyVStack` for tiny content can add unnecessary complexity.
|