Skip to content

Commit

Permalink
Merge pull request #58 from THT-Team/Refactor/falling_compositional_l…
Browse files Browse the repository at this point in the history
…ayout_apply

DiffableDataSource + Compositional Layout으로 마이그레이션
  • Loading branch information
Minny27 authored Jan 31, 2024
2 parents 3ee6743 + 1d07f1d commit 029ccb8
Show file tree
Hide file tree
Showing 20 changed files with 340 additions and 207 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ extension FallingTarget {
],
"userProfilePhotos": [
{
"url": "test1",
"url": "https://cdn.pixabay.com/photo/2024/01/15/21/16/dog-8510901_640.jpg",
"priority": 1
},
{
"url": "https://firebasestorage.googleapis.com/v0/b/tht-falling.appspot.com/o/images%2Fprofile_example2.png?alt=media&token=e19493ed-10ba-4784-bf94-f4b99af84161",
"url": "https://firebasestorage.googleapis.com/v0/b/tht-falling.appspot.com/o/images%2Fprofile_example1.png?alt=media&token=dc28c0cd-98b2-4332-9660-35530283d77c",
"priority": 2
},
{
Expand Down Expand Up @@ -116,11 +116,11 @@ extension FallingTarget {
],
"userProfilePhotos": [
{
"url": "https://firebasestorage.googleapis.com/v0/b/tht-falling.appspot.com/o/images%2Fprofile_example1 .png?alt=media&token=dc28c0cd-98b2-4332-9660-35530283d77c",
"url": "https://cdn.pixabay.com/photo/2023/05/07/11/57/surfboard-7976219_960_720.jpg",
"priority": 1
},
{
"url": "https://firebasestorage.googleapis.com/v0/b/tht-falling.appspot.com/o/images%2Fprofile_example2.png?alt=media&token=e19493ed-10ba-4784-bf94-f4b99af84161",
"url": "https://cdn.pixabay.com/photo/2024/01/07/11/17/welsh-corgi-8492879_640.jpg",
"priority": 2
},
{
Expand Down Expand Up @@ -173,11 +173,11 @@ extension FallingTarget {
],
"userProfilePhotos": [
{
"url": "https://firebasestorage.googleapis.com/v0/b/tht-falling.appspot.com/o/images%2Fprofile_example1.png?alt=media&token=dc28c0cd-98b2-4332-9660-35530283d77c",
"url": "https://cdn.pixabay.com/photo/2023/03/18/05/26/ferris-wheel-7859855_640.jpg",
"priority": 1
},
{
"url": "https://firebasestorage.googleapis.com/v0/b/tht-falling.appspot.com/o/images%2Fprofile_example2.png?alt=media&token=e19493ed-10ba-4784-bf94-f4b99af84161",
"url": "https://cdn.pixabay.com/photo/2023/11/17/12/46/cat-8394224_640.jpg",
"priority": 2
},
{
Expand Down
71 changes: 67 additions & 4 deletions Projects/Features/Falling/Src/Home/FallingHomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ import DSKit

final class FallingHomeView: TFBaseView {
lazy var collectionView: UICollectionView = {
let flowLayout = UICollectionViewFlowLayout()
flowLayout.minimumLineSpacing = 14
flowLayout.scrollDirection = .vertical
let flowLayout = UICollectionViewCompositionalLayout.verticalListLayout(withEstimatedHeight: ((UIWindow.keyWindow?.frame.width ?? 0) - 32) * 1.64)
let collectionView = UICollectionView(frame: .zero,
collectionViewLayout: flowLayout)
collectionView.isScrollEnabled = false
Expand All @@ -23,11 +21,76 @@ final class FallingHomeView: TFBaseView {
}()

override func makeUI() {
self.backgroundColor = DSKitAsset.Color.neutral700.color

self.addSubview(collectionView)

self.collectionView.snp.makeConstraints {
$0.top.equalToSuperview().inset(8)
$0.top.equalTo(self.safeAreaLayoutGuide).inset(8)
$0.leading.bottom.trailing.equalToSuperview()
}
}
}

extension UICollectionViewCompositionalLayout {
static func verticalListLayout(withEstimatedHeight estimatedHeight: CGFloat = 110) -> UICollectionViewCompositionalLayout {
return UICollectionViewCompositionalLayout(section: .verticalListSection(withEstimatedHeight: estimatedHeight))
}

static func horizontalListLayout() -> UICollectionViewCompositionalLayout {
return UICollectionViewCompositionalLayout(section: .horizontalListSection())
}
}

extension NSCollectionLayoutSection {
static func verticalListSection(withEstimatedHeight estimatedHeight: CGFloat = 110) -> NSCollectionLayoutSection {
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)
)
let layoutItem = NSCollectionLayoutItem(layoutSize: itemSize)

let layoutGroupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(estimatedHeight)
)

let layoutGroup = NSCollectionLayoutGroup.vertical(
layoutSize: layoutGroupSize,
subitems: [layoutItem]
)

let section = NSCollectionLayoutSection(group: layoutGroup)
section.contentInsets = NSDirectionalEdgeInsets(
top: 0,
leading: 16,
bottom: 0,
trailing: 16
)
section.interGroupSpacing = 14
return section
}

static func horizontalListSection() -> NSCollectionLayoutSection {
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)
)
let layoutItem = NSCollectionLayoutItem(layoutSize: itemSize)

let layoutGroupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)
)

let layoutGroup = NSCollectionLayoutGroup.horizontal(
layoutSize: layoutGroupSize,
subitems: [layoutItem]
)

let section = NSCollectionLayoutSection(group: layoutGroup)
section.orthogonalScrollingBehavior = .paging

return section
}
}
101 changes: 46 additions & 55 deletions Projects/Features/Falling/Src/Home/FallingHomeViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@ import FallingInterface

final class FallingHomeViewController: TFBaseViewController {
private let viewModel: FallingHomeViewModel
private var dataSource: UICollectionViewDiffableDataSource<FallingProfileSection, FallingUser>!
private var dataSource: DataSource!
private lazy var homeView = FallingHomeView()

init(viewModel: FallingHomeViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
setupDelegate()
}

required init?(coder: NSCoder) {
Expand All @@ -37,15 +36,17 @@ final class FallingHomeViewController: TFBaseViewController {
let mindImageView = UIImageView(image: DSKitAsset.Image.Icons.mind.image)
let mindImageItem = UIBarButtonItem(customView: mindImageView)

let notificationButtonItem = UIBarButtonItem(image: DSKitAsset.Image.Icons.bell.image, style: .plain, target: nil, action: nil)
let notificationButtonItem = UIBarButtonItem.noti

navigationItem.leftBarButtonItem = mindImageItem
navigationItem.rightBarButtonItem = notificationButtonItem
}

override func bindViewModel() {
let timeOverSubject = PublishSubject<Void>()

let initialTrigger = Driver<Void>.just(())
let timerOverTrigger = self.rx.timeOverTrigger.asDriver()
let timerOverTrigger = timeOverSubject.asDriverOnErrorJustEmpty()

let viewWillAppearTrigger = self.rx.viewWillAppear.map { _ in true }.asDriverOnErrorJustEmpty()
let viewWillDisAppearTrigger = self.rx.viewWillDisAppear.map { _ in false }.asDriverOnErrorJustEmpty()
Expand All @@ -57,7 +58,7 @@ final class FallingHomeViewController: TFBaseViewController {
.when(.recognized)
.withLatestFrom(timerActiveRelay) { !$1 }
.asDriverOnErrorJustEmpty()

cardDoubleTapTrigger
.drive(timerActiveRelay)
.disposed(by: disposeBag)
Expand All @@ -66,71 +67,61 @@ final class FallingHomeViewController: TFBaseViewController {
.drive(timerActiveRelay)
.disposed(by: disposeBag)

let input = FallingHomeViewModel.Input(initialTrigger: initialTrigger,
timeOverTrigger: timerOverTrigger)
let input = FallingHomeViewModel.Input(
initialTrigger: initialTrigger,
timeOverTrigger: timerOverTrigger)

let output = viewModel.transform(input: input)

var usersCount = 0

let profileCellRegistration = UICollectionView.CellRegistration<FallingUserCollectionViewCell, FallingUser> { [weak self] cell, indexPath, item in

let observer = FallingUserCollectionViewCellObserver(userCardScrollIndex: output.userCardScrollIndex.asObservable(),
timerActiveTrigger: timerActiveRelay.asObservable())
let profileCellRegistration = UICollectionView.CellRegistration<CellType, ModelType> { cell, indexPath, item in
let timerActiveTrigger = Driver.combineLatest(
output.nextCardIndexPath,
timerActiveRelay.asDriver()
)
.filter { itemIndexPath, _ in indexPath == itemIndexPath }
.map { _, timerActiveFlag in timerActiveFlag }

cell.bind(model: item)
cell.bind(observer,
index: indexPath,
usersCount: usersCount)
cell.delegate = self
cell.bind(
FallinguserCollectionViewCellModel(userDomain: item),
timerActiveTrigger,
scrollToNextObserver: timeOverSubject
)
}

dataSource = UICollectionViewDiffableDataSource(collectionView: homeView.collectionView, cellProvider: { collectionView, indexPath, itemIdentifier in
dataSource = DataSource(collectionView: homeView.collectionView, cellProvider: { collectionView, indexPath, itemIdentifier in
return collectionView.dequeueConfiguredReusableCell(using: profileCellRegistration, for: indexPath, item: itemIdentifier)
})

var listCount = 0

output.userList
.drive(with: self, onNext: { this, list in
usersCount = list.count
var snapshot = NSDiffableDataSourceSnapshot<FallingProfileSection, FallingUser>()
.drive(with: self, onNext: { owner, list in
listCount = list.count
var snapshot = Snapshot()
snapshot.appendSections([.profile])
snapshot.appendItems(list)
this.dataSource.apply(snapshot)
owner.dataSource.apply(snapshot)
}).disposed(by: disposeBag)

output.userCardScrollIndex
.drive(with: self, onNext: { this, index in
if usersCount == 0 { return }
let index = index >= usersCount ? usersCount - 1 : index
let indexPath = IndexPath(row: index, section: 0)
this.homeView.collectionView.scrollToItem(at: indexPath,
at: .top,
animated: true)
})

output.nextCardIndexPath
.drive(with: self, onNext: { owner, indexPath in
guard indexPath.row < listCount else { return }
owner.homeView.collectionView.scrollToItem(
at: indexPath,
at: .top,
animated: true
)})
.disposed(by: self.disposeBag)
}

private func setupDelegate() {
homeView.collectionView.delegate = self
}
}

extension FallingHomeViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width - 32,
height: (view.frame.width - 32) * 1.64)
}
}
// MARK: DiffableDataSource

extension FallingHomeViewController: TimeOverDelegate {
@objc func scrollToNext() { }
}

extension Reactive where Base: FallingHomeViewController {
var timeOverTrigger: ControlEvent<Void> {
let source = methodInvoked(#selector(Base.scrollToNext)).map { _ in }
return ControlEvent(events: source)
}
extension FallingHomeViewController {
typealias CellType = FallingUserCollectionViewCell
typealias ModelType = FallingUser
typealias SectionType = FallingProfileSection
typealias DataSource = UICollectionViewDiffableDataSource<SectionType, ModelType>
typealias Snapshot = NSDiffableDataSourceSnapshot<SectionType, ModelType>
}

//#if DEBUG
Expand All @@ -141,9 +132,9 @@ extension Reactive where Base: FallingHomeViewController {
// static var previews: some View {
// let service = FallingAPI(isStub: true, sampleStatusCode: 200, customEndpointClosure: nil)
// let navigator = MainNavigator(controller: UINavigationController(), fallingService: service)
//
//
// let viewModel = MainViewModel(navigator: navigator, service: service)
//
//
// return FallingHomeViewController(viewModel: viewModel)
// .toPreView()
// }
Expand Down
42 changes: 24 additions & 18 deletions Projects/Features/Falling/Src/Home/FallingHomeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,53 +10,59 @@ import FallingInterface

import RxSwift
import RxCocoa
import Foundation

final class FallingHomeViewModel: ViewModelType {

private let fallingUseCase: FallingUseCaseInterface
// weak var delegate: FallingHomeDelegate?

// weak var delegate: FallingHomeDelegate?

var disposeBag: DisposeBag = DisposeBag()

struct Input {
let initialTrigger: Driver<Void>
let timeOverTrigger: Driver<Void>
}

struct Output {
let userList: Driver<[FallingUser]>
let userCardScrollIndex: Driver<Int>
let nextCardIndexPath: Driver<IndexPath>
}

init(fallingUseCase: FallingUseCaseInterface) {
self.fallingUseCase = fallingUseCase
}

func transform(input: Input) -> Output {
let currentIndexRelay = BehaviorRelay<Int>(value: 0)
let timeOverTrigger = input.timeOverTrigger

let usersResponse = input.initialTrigger
.flatMapLatest { [unowned self] _ in
self.fallingUseCase.user(alreadySeenUserUUIDList: [], userDailyFallingCourserIdx: 1, size: 100)
.asDriver(onErrorJustReturn: .init(selectDailyFallingIdx: 0, topicExpirationUnixTime: 0, userInfos: []))
}

let userList = usersResponse.map { $0.userInfos }.asDriver()
let userListObservable = userList.map { _ in

let updateUserListTrigger = userList.map { _ in
currentIndexRelay.accept(currentIndexRelay.value)
}
let nextScrollIndex = timeOverTrigger.withLatestFrom(currentIndexRelay.asDriver(onErrorJustReturn: 0)) { _, index in

let updateScrollIndexTrigger = timeOverTrigger.withLatestFrom(currentIndexRelay.asDriver(onErrorJustReturn: 0)) { _, index in
currentIndexRelay.accept(index + 1)
}

let userCardScrollIndex = Driver.merge(userListObservable, nextScrollIndex).withLatestFrom(currentIndexRelay.asDriver(onErrorJustReturn: 0))

let nextCardIndexPath = Driver.merge(
updateUserListTrigger,
updateScrollIndexTrigger
).withLatestFrom(currentIndexRelay.asDriver(onErrorJustReturn: 0)
.map { IndexPath(row: $0, section: 0) })

return Output(
userList: userList,
userCardScrollIndex: userCardScrollIndex)
nextCardIndexPath: nextCardIndexPath
)
}
}
2 changes: 1 addition & 1 deletion Projects/Features/Falling/Src/Subviews/CardTimeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ final class CardTimeView: TFBaseView {
lazy var containerView: UIView = {
let view = UIView()
view.layer.cornerRadius = 15
view.backgroundColor = DSKitAsset.Color.dimColor.color.withAlphaComponent(0.5)
view.backgroundColor = DSKitAsset.Color.DimColor.timerDim.color.withAlphaComponent(0.5)
return view
}()

Expand Down
Loading

0 comments on commit 029ccb8

Please sign in to comment.