Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- Protocol
- 리팩토링
- collectionview
- ios
- uiscrollview
- 클린 코드
- Clean Code
- 리펙토링
- SWIFT
- 애니메이션
- RxCocoa
- Human interface guide
- Xcode
- uitableview
- UICollectionView
- HIG
- clean architecture
- UITextView
- 스위프트
- MVVM
- map
- swift documentation
- 리펙터링
- rxswift
- Observable
- ribs
- swiftUI
- combine
- tableView
- Refactoring
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - SwiftUI] 2. List 형태 UI - pull to refresh, 페이지네이션 구현 방법 (List, refreshable, @Sendable) 본문
iOS 응용 (SwiftUI)
[iOS - SwiftUI] 2. List 형태 UI - pull to refresh, 페이지네이션 구현 방법 (List, refreshable, @Sendable)
jake-kim 2024. 4. 24. 01:331. List 형태 UI - @State를 활용하여 로딩 상태, 로드 상태, 실패 상태 띄우기
2. List 형태 UI - pull to refresh, 페이지네이션 구현 방법 (List, refreshable, pagination, @Sendable)
3. List 형태 UI - 검색된 결과 UI에 보여지게 하는 방법 (searchable)
복습) 1번 글까지 한 것
- @State로 선언된 프로퍼티를 활용하면 매우 쉽게 상태 표현이 가능
- 뷰 코드에서 이 프로퍼티에 관한 switch문을 활용하여 상태에 따라 뷰를 다르게 보여주는 코드를 준비해 놓고, 프로퍼티만 변경되면 자동으로 바인딩되어 뷰가 변경됨
- 코드: https://github.com/JK0369/ExListSwiftUI.git
struct ContentView: View {
enum ViewState {
case loading
case success
case failed
}
@State private var state = ViewState.loading
var body: some View {
Group {
switch state {
case .loading:
...
case .success:
...
case .failed:
...
}
}
...
}
}
List와 pull to refresh (refreshable)
- SwiftUI에서는 refreshable를 사용하면 간편하게 리프레시 구현이 가능
- 위 코드의 success부분에 List를 띄우기 위해서 필요한 데이터 Person 정의
struct Person: Identifiable {
let id: String
let age: Int
let name: String
}
- 이 데이터를 List에 뿌려주기
- List에는 rowContent 인자에 별도의 뷰를 주입해주어야 하므로 이 값을 정의 (closure형태로 구현해도 되지만 뷰를 나누기 위해서 PersonRowView로 따로 만들기)
struct ContentView: View {
...
@State private var items = [Person]()
var body: some View {
Group {
switch state {
case .loading:
...
case .success:
List(items, rowContent: <#T##(Person) -> RowContent#>)
...
}
}
...
}
}
- PersonRowView라는 별도의 뷰로 만들어놓으면 아래처럼 심플하게 표현이 가능
List(items, rowContent: PersonRowView.init)
- PersonRowView 정의
import SwiftUI
struct PersonRowView: View {
let item: Person
var body: some View {
NavigationLink {
Text("detail")
} label: {
HStack {
Text("age: \(item.age), name: \(item.name)")
.font(.caption.weight(.heavy))
}
}
}
}
List형태의 뷰 완성)
struct ContentView: View {
...
@State private var items = [Person(id: "1", age: 10, name: "jake")]
@State private var state = ViewState.loading
var body: some View {
Group {
switch state {
...
case .success:
List(items, rowContent: PersonRowView.init)
...
}
}
}
- refershable을 추가하여, 여기에 값을 더하는 코드를 추가
- loadMoreItems는 @Sendable타입으로 정의 (@Sendable 타입은 아래에서 설명)
List(items, rowContent: PersonRowView.init)
.refreshable(action: loadMoreItems)
@Sendable func loadMoreItems() async {
try? await Task.sleep(nanoseconds: 500_000_000)
let mocks = [
Person(id: Date.now.description, age: 10, name: "jake1"),
Person(id: Date.now.description + "1", age: 20, name: "jake2"),
Person(id: Date.now.description + "2", age: 30, name: "jake3"),
Person(id: Date.now.description + "3", age: 40, name: "jake4"),
Person(id: Date.now.description + "4", age: 50, name: "jake5"),
Person(id: Date.now.description + "5", age: 60, name: "jake6"),
Person(id: Date.now.description + "6", age: 20, name: "jake7"),
Person(id: Date.now.description + "7", age: 30, name: "jake8"),
Person(id: Date.now.description + "8", age: 40, name: "jake9"),
Person(id: Date.now.description + "9", age: 50, name: "jake10"),
Person(id: Date.now.description + "10", age: 60, name: "jake11"),
Person(id: Date.now.description + "11", age: 20, name: "jake12"),
Person(id: Date.now.description + "12", age: 30, name: "jake13"),
Person(id: Date.now.description + "13", age: 40, name: "jake14"),
Person(id: Date.now.description + "14", age: 50, name: "jake15"),
Person(id: Date.now.description + "15", age: 60, name: "jake16"),
]
items.append(contentsOf: mocks)
}
- @Sendable타입이란?
- concurrency 환경에서 value type과 같은 곳에 동시에 수정과 읽기가 일어나면 크래시가 발생하는데, 이를 안전하게 처리해주는 것
- 개발자 입장에서 concurrency 상황에서의 문제들에 관해 고민할 필요 없도록 @Sendable을 선언하여 데이터 업데이트를 편하게 사용
- pull to refresh 완성
페이지네이션 구현 ()
- Swift에서 UITableView를 사용할 때 페이지네이션 구현 방법
- 내장된 델리게이트 함수 prefetchRows 사용
- contentSize.height와 contentOffset.y을 계산하여, contentSize.height값이 얼마 남지 않았을때 loadMoreData 호출
- UITableView에서 빈 footerView를 놓고 이 footerView가 보일 때 loadMoreData와 같은 함수 호출
- SwiftUI에서도 위 방법들 모두 다 가능하지만 가장 간편한 방법은 UITableView의 footerView를 놓는 것과 유사하게 마지막 Row가 보일때 loadMoreData를 호출하는 것
- SwiftUI의 onAppear를 사용하면 매우 쉽게 구현이 가능
- List 코드에서 클로저를 열고, 위에서 구현했던 PersonRowView 삽입
List(items) { person in
PersonRowView(item: person)
}
- onAppear를 활용하여 마지막 id의 아이템이 보일 경우 loadMoreData를 호출
List(items) { person in
PersonRowView(item: person)
.onAppear {
if person.id == items.last?.id {
Task {
await loadMoreItems()
}
}
}
}
페이지네이션 완성)
* 전체 코드: https://github.com/JK0369/ExListSwiftUI
* 참고
'iOS 응용 (SwiftUI)' 카테고리의 다른 글
Comments