Published on

๐Ÿ“ฑ iOS - CollectionView์˜ Cell/View์—์„œ์˜ subscribe ๋ฌธ์ œ

Authors
  • avatar
    Name
    ์ด์ฐฝ์ค€
    Twitter

โ“ CollectionView์˜ HeaderView๋ฅผ ๊ตฌ๋…ํ•˜๋Š” ๊ณผ์ •์—์„œ ๋ฌธ์ œ ๋ฐœ์ƒ

HeaderView์˜ ๋ฒ„ํŠผ์„ tapํ•˜๋ฉด ๋ฐ”์ธ๋”ฉ๋œ Action์ด ์—ฌ๋Ÿฌ๋ฒˆ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ ๋ฐœ์ƒ

configureSupplementaryView: { dataSource, collectionView, kind, indexPath in
  let sectionItem = dataSource[indexPath.section]
  let header = collectionView.dequeueReusableSupplementaryView(
    ofKind: kind,
    for: indexPath) as HeaderView
  header.reactor = HeaderViewReactor(section: sectionItem.model)
  header.rx.rightButtonDidTap
    .map { Reactor.Action.rightButtonDidTap(sectionItem.model) }
    .bind(to: self.reactor!.action)
    .disposed(by: self.disposeBag)
  return header
}

ํ•ด๋‹น HeaderView๋Š” ์œ„์™€ ๊ฐ™์ด ํ• ๋‹น๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์˜์‹ฌ 1.DashboardFlow์˜ ๋ณต์ œ?

ํ•ด๋‹น ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ์‹œ์ ์ด DashboardFlow๋ฅผ ๊ฑด๋“œ๋ฆด ๋•Œ์˜€๊ณ , ํ™•์‹ ์„ ๊ฐ–์ง€ ๋ชปํ•œ ์ƒํƒœ์—์„œ ์ž‘์—…์„ ํ•˜๊ณ  ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์šฐ์„ ์ ์œผ๋กœ ์˜์‹ฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ ๋‹ค๋ฅธ Flow๋กœ ์ด๋™ ํ›„์— ๋‹ค์‹œ ๋Œ์•„์™€ ๊ฐ™์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ํšŸ์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ•˜๋Š” ํ˜„์ƒ์„ ๋ฐœ๊ฒฌํ•˜์˜€๊ธฐ์— ์˜์‹ฌ์„ ํ‚ค์›Œ๊ฐ€๊ณ  ์žˆ์—ˆ์ง€๋งŒโ€ฆ

๋ฉ”๋ชจ๋ฆฌ ์Šค๋ƒ…์ƒท์„ ์‚ดํŽด๋ณธ ๊ฒฐ๊ณผ DashboardFlow๋Š” ๋‹จ ํ•œ ๊ฐœ๋งŒ ์ƒ์„ฑ๋˜์–ด์žˆ๋‹ค..

์ถ”๊ฐ€์ ์œผ๋กœ ์˜์‹ฌ๋˜๋˜ ๋ถ€๋ถ„์ธ .contribute(withNext: self.rootViewController) ๋ถ€๋ถ„์„ ์ฃผ์„์ฒ˜๋ฆฌํ•˜๊ณ  ์•ฑ์„ ์‹คํ–‰ํ•ด๋„ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๋ณด์•„ ์ด ๋ถ€๋ถ„์€ ๋ฌธ์ œ๊ฐ€ ์•„๋‹Œ ๊ฒƒ์œผ๋กœ ๋ณด์ž„..!

์˜์‹ฌ 2. ๋ชจ๋“  HeaderView์˜ Action์ด ํŠธ๋ฆฌ๊ฑฐ๋˜์—ˆ๋‹ค..?

HomeViewReactor์˜ ํ•ด๋‹น Action์„ ๋ฐ›๋Š” ๋ถ€๋ถ„์—์„œ ๋กœ๊น…์„ ํ•ด๋ณด์•˜๋‹ค.

case .rightButtonDidTap(let sectionType):
  let type = "\(sectionType)"
  os_log(.debug, "\(type)")
  switch sectionType {
  case .upcoming:
    self.steps.accept(AppStep.reminderIsRequired)
  case .timeline:
    self.steps.accept(AppStep.filterIsRequired(self.currentSortType.value))
  }
  return .empty()
}

๊ทธ๋žฌ๋”๋‹ˆ ๋‘ ๊ฐœ์˜ ์„น์…˜(timeline, upcoming)์—์„œ ๊ฐ ๋‘ ๋ฒˆ์”ฉ ๋กœ๊น…์ด ๋˜๊ณ  ์žˆ๋Š” ๊ฒƒ์„ ๋ฐœ๊ฒฌํ•˜์˜€๋‹ค.

์•„๋‹ˆ ๊ทธ๋Ÿฐ๋ฐ..

configureSupplementaryView: { dataSource, collectionView, kind, indexPath in
  let sectionItem = dataSource[indexPath.section]
  let header = collectionView.dequeueReusableSupplementaryView(
    ofKind: kind,
    for: indexPath) as HeaderView
  header.reactor = HeaderViewReactor(section: sectionItem.model)
  header.rx.rightButtonDidTap
    .map { Reactor.Action.rightButtonDidTap(sectionItem.model) }
    .bind(to: self.reactor!.action)
    .disposed(by: self.disposeBag)
  return header
}

drive๋กœ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ๋„ ์•„๋‹ˆ๊ณ  bind๋กœ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ์–ด์„œ ์ŠคํŠธ๋ฆผ์ด ๊ณต์œ ๋˜๋Š” ๊ฒƒ๋„ ์•„๋‹Œ๋ฐ ์™œ ๋ชจ๋“  ํ—ค๋”์—์„œ Action์ด ํŠธ๋ฆฌ๊ฑฐ ๋˜๋Š”๊ฑฐ์ง€..?

โ— ์‹ค๋งˆ๋ฆฌ ๋ฐœ๊ฒฌ

๊ทธ๋Ÿฌ๋˜ ์ค‘ ํ—ฌํ”„๋ฅผ ์š”์ฒญํ•œ ํŒ€์›์—๊ฒŒ ๋ฐ›์€ ๋Œ“๊ธ€โ€ฆ!

์• ์ดˆ์— ๊ตฌ๋…์ด ์—ฌ๋Ÿฌ๋ฒˆ ๋˜๊ณ  ์žˆ๋‹ค๋Š” ์ œ๋ณดโ€ฆ!

๋ฐ”๋กœ ํ…Œ์ŠคํŠธ ๋Œ์ž…..

self.collectionView.rx.didEndDisplayingSupplementaryView
  .asDriver(onErrorRecover: { _ in return .empty()})
  .drive(with: self, onNext: { _, endDisplayingView in
    print(endDisplayingView)
.disposed(by: self.disposeBag)

์œ„ ์ฒ˜๋Ÿผ HeaderView๊ฐ€ ํ™”๋ฉด์—์„œ ์‚ฌ๋ผ์งˆ ๋•Œ๋งˆ๋‹ค ๋กœ๊น…์„ ํ•˜๊ณ  ์‹คํ–‰์„ ํ•ด๋ณด์•˜๋‹ค.

์šฐ์„  HomeVC๊ฐ€ ๋กœ๋“œ ๋˜๋ฉฐ ๋‘ ๋ฒˆ์”ฉ ์ถœ๋ ฅ์ด ๋˜๊ณ ..

๊ธฐ๋ณธ 2ํšŒ + endDisplaying 1ํšŒ โ†’ 3ํšŒ

๊ธฐ๋ณธ 2ํšŒ + endDisplaying 3ํšŒ โ†’ 5ํšŒ

HeaderView๋ฅผ didEndDisplaying ์‹œํ‚ฌ ๋•Œ๋งˆ๋‹ค ํ•ด๋‹น ํ˜„์ƒ์˜ ํšŸ์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ๋‹ค..

์–ด๋–ป๊ฒŒ ์ฐพ์•˜๋Œ€โ€ฆ

๊ทธ๋ž˜์„œ ์›์ธ์€ ์•Œ์•˜๋Š”๋ฐ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•˜์ง€..?

ํ•ด๊ฒฐ

ํ˜น์‹œ๋‚˜ ์‹ถ์–ด์„œ HeaderView ์ž์ฒด๋Š” ๋ช‡๊ฐœ์ธ์ง€ ํ™•์ธํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ๋ฒˆ endDisplaying ์‹œ์ผœ์ค€ ๋’ค ํ™•์ธํ•ด๋ณด์•˜์ง€๋งŒ ์ •์ƒ์ ์œผ๋กœ ๋‘ ๊ฐœ๋งŒ ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ผ๊ฐ€์žˆ๋Š” ์ƒํ™ฉ์ด๋„ค์š”โ€ฆ

View ์ž์ฒด๋Š” ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ subscribe๋งŒ ์—ฌ๋Ÿฌ๋ฒˆ ๋˜๊ณ  ์žˆ๊ณ , ๊ทธ๊ฒƒ๋“ค์ด ์œ ์ง€๋˜๊ณ  ์žˆ๋‹ค๋Š” ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค..

ํ•ด๊ฒฐ ์‹œ๋„ #1

๊ฒฐ๊ตญ ๋ฌธ์ œ๋Š” HeaderView๋Š” ์žฌ์‚ฌ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์— didEndDisplaying ๋œ ํ›„ ๋‹ค์‹œ willDisplay ๋  ๋•Œ๋งˆ๋‹ค configureSupplementaryView๊ฐ€ ์‹คํ–‰๋˜์–ด์„œ ์ƒˆ๋กœ์šด ๊ตฌ๋…์ด ๋Š˜์–ด๋‚˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด์˜€์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด didEndDisplaying์ด๋‚˜ willDisplay๊ฐ€ ๋  ๋•Œ๋งˆ๋‹ค ๊ตฌ๋…์„ ํ•ด์ œํ•ด์ฃผ๋ฉด ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ๊ฐ€..?

self.collectionView.rx.didEndDisplayingSupplementaryView
  .asDriver(onErrorRecover: { _ in return .empty()})
  .drive(with: self, onNext: { _, endDisplayingView in
    let (view, _, _) = endDisplayingView
    guard let view = view as? HeaderView else { return }
    print(view)
    view.disposeBag = DisposeBag()
  })
  .disposed(by: self.disposeBag)

๊ทธ๋ž˜์„œ ์œ„์™€ ๊ฐ™์ด ๊ณ ์ณ๋ณด์•˜๋‹ค.

ํ•˜์ง€๋งŒ ์–ด๋ฆผ๋„ ์—†์ง€..

์ƒˆ๋กœ์šด DisposeBag์œผ๋กœ ๊ฐˆ์•„ ๋ผ์›Œ์ฃผ๋Š” ๊ฒƒ ๋งŒ์œผ๋กœ๋Š” ๊ตฌ๋…์ด ํ•ด์ œ๋˜์ง€ ์•Š๊ณ  ์žˆ๊ตฐ์š”..

ํ•ด๊ฒฐ ์‹œ๋„ #2

ํ•ด๊ฒฐ ์‹œ๋„ #2 ์ด๊ธด ํ•˜์ง€๋งŒ ๋„์ค‘์— ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์ฐพ์•„๋ฒ„๋ ธ์Šต๋‹ˆ๋‹ค.

header.rx.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didEndDisplayingSupplementaryView:forElementOfKind:at:)))
  .subscribe(onNext: {

  })
  .disposed(by: <#T##DisposeBag#>)

์—ฌ๊ธฐ๊นŒ์ง€ ์ž‘์„ฑ์„ ํ–ˆ๋Š”๋ฐ์š”.. diseposed(by:)๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋„์ค‘์— ๊ฐ‘์ž๊ธฐ ์˜๋ฌธ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

์†์ด ๊ฐ€๋Š”๋Œ€๋กœ disposed(by: self.disposeBag)๋ฅผ ์น˜๋˜ ์ฐฐ๋‚˜โ€ฆ

๋จธ๋ฆฟ์† ์ž ์žฌ์˜์‹์ด ์ €๋ฅผ ํ•œ ๋Œ€ ์ณค์Šต๋‹ˆ๋‹ค..

header.rx.rightButtonDidTap
  .map { Reactor.Action.rightButtonDidTap(sectionItem.model) }
  .subscribe(onNext: {
    self.reactor!.action.onNext($0)
  })
  .disposed(by: self.disposeBag)

๋‹ค์‹œ subscribe๋ฅผ ํ•˜๋˜ ์ฝ”๋“œ๋กœ ๋Œ์•„๊ฐ€๋ด…์‹œ๋‹ค.

์—ฌ๊ธฐ์„œ self๋Š” HeaderView๊ฐ€ ์•„๋‹ˆ๋ผ HomeViewController์ž…๋‹ˆ๋‹ค.

HeaderView์˜ ๊ตฌ๋…์€ HeaderView์˜ dequeueReusable์— ๋”ฐ๋ผ ๋™์ž‘ํ•ด์•ผํ•˜๋Š”๋ฐ ๋ง์ด์ฃ โ€ฆ

๊ทธ๋ž˜์„œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ์•„์ฃผ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

header.rx.rightButtonDidTap
  .map { Reactor.Action.rightButtonDidTap(sectionItem.model) }
  .subscribe(onNext: {
    self.reactor!.action.onNext($0)
  })
  .disposed(by: header.disposeBag) // ๐Ÿ‘Š ์ด ๋ถ€๋ถ„

self.dispseBag์„ header.disposeBag์œผ๋กœ ๋ฐ”๊ฟ”์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค..

์˜ค๋Š˜๋„ ๋˜ ํ•œ ๋ฒˆ ์ƒ๊ฐ์—†์ด ํ•˜๋Š” ์ฝ”๋”ฉ์€ ์œ„ํ—˜ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๋Š๋ผ๊ณ  ์ง€๋‚˜๊ฐ‘๋‹ˆ๋‹ค..

๐ŸŽ‰ ํ•ด๊ฒฐ!

์–ํ˜ธ..!

ํœด.. ๋ฐ˜์„ฑํ•ด๋ผ ๋‚˜ ์ž์‹ ..