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 |
Tags
- MVVM
- tableView
- uiscrollview
- Protocol
- UITextView
- 애니메이션
- Observable
- rxswift
- Human interface guide
- 리팩토링
- uitableview
- Clean Code
- 리펙토링
- UICollectionView
- 스위프트
- HIG
- swift documentation
- 리펙터링
- SWIFT
- collectionview
- ribs
- RxCocoa
- 클린 코드
- ios
- Refactoring
- combine
- swiftUI
- Xcode
- map
- clean architecture
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] HorizontalScroll 수평 스크롤 (자동 스크롤 구현, UICollectionViewCompositionalLayout 사용) 본문
iOS 응용 (swift)
[iOS - swift] HorizontalScroll 수평 스크롤 (자동 스크롤 구현, UICollectionViewCompositionalLayout 사용)
jake-kim 2022. 5. 2. 22:48
cf) UICollectionViewFlowLayout를 이용한 방법은 이 포스팅 글 참고
수평 스크롤 구현 방향
- UICollectionView를 사용하여 스크롤을 구현
- UIScrollView를 사용하면 터치 이벤트 시, 어떤 아이템을 터치했는지 체크하기가 까다롭기 때문에 Cell을 사용할 수 있는 UICollectionView 사용
- IUCollectionView의 UICollectionViewFlowLayout를 사용해도 되지만, 더욱 복잡한 레이아웃을 쉽게 추가하기 쉬운 확장성 있는 CompositionlalLayout 사용
- UICollectionViewCompositionalLayout 개념은 이전 포스팅 글 참고
- 스크롤 구현은 Timer를 두고 매초마다 collectionView.scrollToItem(at:at:animated:) 메소드를 사용하여 이동되게끔 구현
구현
- 예제로 사용할 클래스 준비
import UIKit
class ViewController: UIViewController {
}
- 필요한 프로퍼티 선언
- currentItem은 현재 몇번째 item인지 상태를 저장해놓고, 매초마다 +1 시키기 위해 존재
- currentIndexPath는 computedProperty이고 단순히 IndexPath값을 얻어오기 편하게 하기위해서 사용
private var collectionView: UICollectionView!
private var dataSource = (0...30).map(Int.init(_:))
private var scrollTimer: Timer?
private var currentItem = -1
private var currentIndexPath: IndexPath {
IndexPath(item: self.currentItem, section: 0)
}
deinit {
self.scrollTimer?.invalidate()
self.scrollTimer = nil
}
- NSCollectionLayoutSection을 가져오는 getLayoutSection() 메소드 정의
- CompositionalLayout은 layout만 따로 구현해놓고 생성자의 collectionViewLayout 파라미터에 넘겨주면 되므로 사용
// 커스텀 layout들을 섹션마다 다르게 손쉽게 사용 방법
self.collectionView = UICollectionView(
frame: .zero,
collectionViewLayout: UICollectionViewCompositionalLayout { [weak self] section, env -> NSCollectionLayoutSection? in
guard let ss = self else { return nil }
switch self.dataSource[section] {
...
}
}
)
- getLayoutSection() 메소드 정의
- page처럼 하나씩 스크롤되어야 하므로, sectino의 orthogonalScrollingBehavior를 .paging으로 할당
// 레이아웃 정의
private func getLayoutSection() -> NSCollectionLayoutSection {
// item
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 5)
// group
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)
)
let group = NSCollectionLayoutGroup.horizontal(
layoutSize: groupSize,
subitems: [item]
)
// section
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .paging
return section
}
- 자동이 아닌 손으로 스크롤하는 경우, 자동 스크롤 될때도 지금 스크롤된 IndexPath를 기준으로 스크롤되어야 하므로, section의 visibleItemsInvalidationHandler에서 현재 보여지는 IndexPath를 가져와서 반영
// section
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .paging
section.visibleItemsInvalidationHandler = { [weak self] (visibleItems, offset, env) in
guard
let ss = self,
let visibleIndexPath = visibleItems.last?.indexPath,
ss.currentIndexPath != visibleIndexPath
else { return }
ss.currentItem = visibleIndexPath.item
}
return section
- Timer에서 자동 스크롤 구현
// in viewDidLoad
self.scrollTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true, block: { [weak self] _ in
guard let ss = self else { return }
let countOfIndexPath = ss.collectionView.numberOfItems(inSection: 0)
ss.currentItem = (ss.currentItem + 1) % countOfIndexPath
UIView.animate(
withDuration: 2.0,
animations: {
ss.collectionView.scrollToItem(
at: ss.currentIndexPath,
at: .left,
animated: true
)
},
completion: nil
)
})
'iOS 응용 (swift)' 카테고리의 다른 글
Comments