SwiftUI swipeActions를 ScrollView와 LazyVStack에서 사용하는 방법 (iOS 20 / Xcode 27)
개발/SwiftUI 2026. 8. 3. 13:08반응형
SwiftUI swipeActions를 ScrollView와 LazyVStack에서 사용하는 방법 (iOS 20 / Xcode 27)
기존 SwiftUI에서는 swipeActions 및 드래그 앤 드롭 재정렬(onMove) 기능이 List 뷰 내부에서만 제한적으로 동작했습니다. iOS 20과 Xcode 27부터는 swipeActionsContainer 및 reorderable 모디파이어가 추가되어 ScrollView, LazyVStack, LazyHStack 등 일반 뷰 컨테이너에서도 동일한 상호작용을 구현할 수 있습니다.
기존 List 제한 사항과 신규 API 비교
List는 기본 UIKit의 UITableView 기반으로 작동하여 커스텀 레이아웃이나 스크롤 바인딩 처리에 제약이 있었습니다. 신규 API 도입으로 일반 스크롤 뷰에서도 레이아웃 자유도를 유지하면서 스와이프 및 정렬 기능을 적용할 수 있습니다.
| 구분 | iOS 19 이하 (List 전용 | iOS 20 이상 (swipeActionsContainer) |
| 적용 가능 뷰 | List | ScrollView, LazyVStack, LazyHStack, Custom Stack |
| 스와이프 선언 | .swipeActions(...) | .swipeActionsContainer(...) |
| 드래그 정렬 | .onMove(perform:) | .reorderable(by:) / .reorderContainer(...) |
| 스크롤 최적화 | 기본 테이블 셀 재사용 | lazy 계열 뷰의 개별 셀 상태 유지 |
1. ScrollView 및 LazyVStack 내 스와이프 액션 구현
swipeActionsContainer 모디파이어를 사용하면 ScrollView 내부의 각 아이템 뷰에 개별 스와이프 버튼을 할당할 수 있습니다.
import SwiftUI
struct CustomSwipeScrollView: View {
@State private var items = ["첫 번째 항목", "두 번째 항목", "세 번째 항목"]
var body: some View {
ScrollView {
LazyVStack(spacing: 12) {
ForEach(items, id: \.self) { item in
Text(item)
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
.swipeActionsContainer(edge: .trailing) {
Button(role: .destructive) {
deleteItem(item)
} label: {
Label("삭제", systemImage: "trash")
}
Button {
archiveItem(item)
} label: {
Label("보관", systemImage: "archivebox")
}
.tint(.blue)
}
}
}
.padding()
}
}
private func deleteItem(_ item: String) {
items.removeAll { $0 == item }
}
private func archiveItem(_ item: String) {
// 보관 처리 로직
}
}
- edge 파라미터를 통해 .leading 또는 .trailing 방향을 지정합니다.
- Button(role: .destructive) 사용 시 기본 시스템 삭제 스타일이 적용됩니다.
2. LazyVStack 커스텀 레이아웃 드래그 정렬 (reorderable)
reorderable 모디파이어를 활성화하면 List를 사용하지 않고도 요소의 순서를 드래그하여 변경할 수 있습니다.
import SwiftUI
struct ReorderableLazyVStack: View {
@State private var tasks = ["수업 듣기", "운동하기", "장보기", "코드 리뷰"]
var body: some View {
ScrollView {
LazyVStack(spacing: 10) {
ForEach($tasks, id: \.self) { $task in
HStack {
Image(systemName: "line.3.horizontal")
.foregroundColor(.gray)
Text(task)
Spacer()
}
.padding()
.background(Color.white)
.cornerRadius(10)
.shadow(radius: 1)
.reorderable(by: $tasks, item: task)
}
}
.reorderContainer()
.padding()
}
}
}
- 개별 아이템 뷰에 .reorderable(by:item:)을 선언하고, 바인딩된 데이터 배열을 전달합니다.
- 부모 컨테이너(LazyVStack 등)에 .reorderContainer()를 추가하여 드래그 상호작용 제스처 범위를 정의합니다.
3. 실무 적용 시 주의사항 및 버전 호환성
이전 iOS 버전을 지원해야 하는 프로젝트에서는 @available 속성을 활용해 조건부 처리를 작성해야 합니다.
if #available(iOS 20.0, *) {
contentView
.swipeActionsContainer(edge: .trailing) {
// iOS 20 이상 신규 API
}
} else {
// iOS 19 이하 대안 코드 또는 List 사용
}
- 식별자 고유성: ForEach 구문 사용 시 데이터 요소의 id가 명확히 구분되어야 드래그 정렬 시 애니메이션 튐 현상이 발생하지 않습니다.
- 제스처 충돌: ScrollView 자체의 horizontal 스크롤 제스처와 swipeActionsContainer가 충돌하지 않도록 vertical 방향 스크롤 뷰에서 주로 사용할 것을 권장합니다.
핵심 요약
- iOS 20 및 Xcode 27부터 List 없이 ScrollView 및 LazyVStack에서 swipeActionsContainer를 통해 스와이프 액션을 구현할 수 있습니다.
- reorderable 및 reorderContainer 모디파이어를 결합하여 커스텀 레이아웃 기반 드래그 앤 드롭 정렬이 가능합니다.
- 하위 버전 호환성을 위해 @available(iOS 20.0, *) 기법을 통한 조건부 분기 처리가 필요합니다.
반응형


