Skip to content

Commit

Permalink
[#74]Feat: 좋아요 버튼 액션 구현
Browse files Browse the repository at this point in the history
- TimeState에 none 케이스 생성
- 좋아요, 싫어요, 신고, 차단 시, 타이머, 프로그래스를 초기화하기 위함
- 13초에서 15초로 디자인 반영
- vc에서는 scroll만 처리하고 나머지는 cell에서 처리할 수 있도록 구현
  • Loading branch information
Minny27 authored and ibcylon committed May 27, 2024
1 parent 23b33c4 commit e48ed0c
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 69 deletions.
20 changes: 7 additions & 13 deletions Projects/Features/Falling/Src/Home/FallingHomeViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import DSKit
import FallingInterface

enum FallingCellButtonAction {
case info(IndexPath)
case reject(IndexPath)
case like(IndexPath)
}
Expand Down Expand Up @@ -121,7 +120,7 @@ final class FallingHomeViewController: TFBaseViewController {
}

let footerRegistration = UICollectionView.SupplementaryRegistration
<UICollectionReusableView>(elementKind: UICollectionView.elementKindSectionFooter) { _,_,_ in }
<DummyFooterView>(elementKind: UICollectionView.elementKindSectionFooter) { _,_,_ in }

dataSource = DataSource(collectionView: homeView.collectionView, cellProvider: { collectionView, indexPath, itemIdentifier in
return collectionView.dequeueConfiguredReusableCell(using: profileCellRegistration, for: indexPath, item: itemIdentifier)
Expand Down Expand Up @@ -156,21 +155,16 @@ final class FallingHomeViewController: TFBaseViewController {
})
.disposed(by: self.disposeBag)

output.infoButtonAction
.drive(with: self) { owner, indexPath in
guard let cell = owner.homeView.collectionView.cellForItem(at: indexPath) as? FallingUserCollectionViewCell
else { return }
cell.userInfoView.isHidden.toggle()
output.likeButtonAction
.drive(with: self) { owner, _ in
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
timeOverSubject.onNext(.scroll)
}
}
.disposed(by: disposeBag)

output.rejectButtonAction
.drive(with: self) { owner, indexPath in
guard let cell = owner.homeView.collectionView.cellForItem(at: indexPath) as? FallingUserCollectionViewCell else { return }

cell.rejectLottieView.isHidden = false
cell.rejectLottieView.play()

.drive(with: self) { owner, _ in
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
timeOverSubject.onNext(.scroll)
}
Expand Down
11 changes: 6 additions & 5 deletions Projects/Features/Falling/Src/Home/FallingHomeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ final class FallingHomeViewModel: ViewModelType {
struct Output {
let userList: Driver<[FallingUser]>
let nextCardIndexPath: Driver<IndexPath>
let infoButtonAction: Driver<IndexPath>
let likeButtonAction: Driver<IndexPath>
let rejectButtonAction: Driver<IndexPath>
let complaintsAction: Driver<IndexPath>
let blockAction: Driver<IndexPath>
Expand Down Expand Up @@ -74,11 +74,12 @@ final class FallingHomeViewModel: ViewModelType {
).withLatestFrom(currentIndexRelay.asDriver(onErrorJustReturn: 0)
.map { IndexPath(row: $0, section: 0) })

let infoButtonAction = input.cellButtonAction
let likeButtonAction = input.cellButtonAction
.compactMap { action -> IndexPath? in
if case let .info(indexPath) = action {
if case let .like(indexPath) = action {
return indexPath
} else { return nil }
}
return nil
}

let rejectButtonAction = input.cellButtonAction
Expand Down Expand Up @@ -107,7 +108,7 @@ final class FallingHomeViewModel: ViewModelType {
return Output(
userList: userList,
nextCardIndexPath: nextCardIndexPath,
infoButtonAction: infoButtonAction,
likeButtonAction: likeButtonAction,
rejectButtonAction: rejectButtonAction,
complaintsAction: complaintsAction,
blockAction: blockAction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,8 @@ final class FallingUserCollectionViewCell: TFBaseCollectionViewCell {
return pauseView
}()

lazy var rejectLottieView: LottieAnimationView = {
lazy var lottieView: LottieAnimationView = {
let lottieAnimationView = LottieAnimationView()
lottieAnimationView.animation = AnimationAsset.unlike.animation
lottieAnimationView.isHidden = true
lottieAnimationView.contentMode = .scaleAspectFit
return lottieAnimationView
Expand All @@ -77,7 +76,7 @@ final class FallingUserCollectionViewCell: TFBaseCollectionViewCell {
override func makeUI() {
self.layer.cornerRadius = 20

self.contentView.addSubviews([profileCollectionView, cardTimeView, userInfoBoxView, userInfoBoxView, userInfoView, rejectLottieView, pauseView])
self.contentView.addSubviews([profileCollectionView, cardTimeView, userInfoBoxView, userInfoBoxView, userInfoView, lottieView, pauseView])

profileCollectionView.snp.makeConstraints {
$0.edges.equalToSuperview()
Expand All @@ -104,7 +103,7 @@ final class FallingUserCollectionViewCell: TFBaseCollectionViewCell {
$0.edges.equalToSuperview()
}

self.rejectLottieView.snp.makeConstraints {
self.lottieView.snp.makeConstraints {
$0.center.equalToSuperview()
$0.width.height.equalTo(188) // TODO: 사이즈 수정 예정
}
Expand All @@ -130,7 +129,7 @@ final class FallingUserCollectionViewCell: TFBaseCollectionViewCell {
) where O: ObserverType, O.Element == FallingCellButtonAction {
let infoButtonTapTrigger = userInfoBoxView.infoButton.rx.tap.asDriver()

let rejectButtonTapTrigger = userInfoBoxView.rejectButton.rx.tap.mapToVoid().asDriverOnErrorJustEmpty()
let rejectButtonTapTrigger = userInfoBoxView.rejectButton.rx.tap.asDriver()

let likeButtonTapTrigger = userInfoBoxView.likeButton.rx.tap.asDriver()

Expand Down Expand Up @@ -196,13 +195,28 @@ final class FallingUserCollectionViewCell: TFBaseCollectionViewCell {
output.rejectButtonAction
.compactMap { [weak self] _ in self?.indexPath }
.map { FallingCellButtonAction.reject($0) }
.drive(fallingCellButtonAction)
.drive(with: self) { owner, action in
fallingCellButtonAction.onNext(action)
owner.lottieView.snp.updateConstraints { $0.width.height.equalTo(188) }
owner.lottieView.animation = AnimationAsset.unlike.animation
owner.lottieView.isHidden = false
owner.lottieView.play()
}
.disposed(by: disposeBag)

output.likeButtonAction
.compactMap { [weak self] _ in self?.indexPath }
.map { FallingCellButtonAction.like($0) }
.drive(fallingCellButtonAction)
.drive(with: self) { owner, action in
fallingCellButtonAction.onNext(action)
owner.lottieView.snp.updateConstraints { $0.width.height.equalTo(200) }
owner.lottieView.animation = AnimationAsset.likeHeart.animation
owner.lottieView.isHidden = false
owner.lottieView.play()
owner.cardTimeView.progressView.addGradientLayer()
owner.cardTimeView.timerView.timerLabel.isHidden = true
owner.cardTimeView.timerView.addGradientLayer()
}
.disposed(by: disposeBag)

output.reportButtonAction
Expand Down Expand Up @@ -270,7 +284,7 @@ extension FallingUserCollectionViewCell {
extension Reactive where Base: FallingUserCollectionViewCell {
var timeState: Binder<TimeState> {
return Binder(self.base) { (base, timeState) in
base.cardTimeView.timerView.trackLayer.strokeColor = timeState.fillColor.color.cgColor
base.cardTimeView.timerView.trackLayer.strokeColor = timeState.trackLayerStrokeColor.color.cgColor
base.cardTimeView.timerView.strokeLayer.strokeColor = timeState.timerTintColor.color.cgColor
base.cardTimeView.timerView.dotLayer.strokeColor = timeState.timerTintColor.color.cgColor
base.cardTimeView.timerView.dotLayer.fillColor = timeState.timerTintColor.color.cgColor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,27 @@ import FallingInterface
import DSKit

enum TimeState {
case initial(value: Double) // 12~13
case five(value: Double) // 10~12
case four(value: Double) // 8~10
case three(value: Double) // 6~8
case two(value: Double) // 4~6
case one(value: Double) // 2~4
case zero(value: Double) // 1~2
case over(value: Double) // 0~1
case initial(value: Double) // 14~15
case five(value: Double) // 12~14
case four(value: Double) // 10~12
case three(value: Double) // 8~10
case two(value: Double) // 6~8
case one(value: Double) // 4~6
case zero(value: Double) // 2~4
case over(value: Double) // 0~2
case none // Reject(싫어요) or Like(좋아요) or Delete(신고, 차단) user

init(rawValue: Double) {
switch rawValue {
case 12.0..<13.0: self = .initial(value: rawValue)
case 10.0..<12.0: self = .five(value: rawValue)
case 8.0..<10.0: self = .four(value: rawValue)
case 6.0..<8.0: self = .three(value: rawValue)
case 4.0..<6.0: self = .two(value: rawValue)
case 2.0..<4.0: self = .one(value: rawValue)
case 1.0..<2.0: self = .zero(value: rawValue)
default: self = .over(value: rawValue)
case 14.0..<15.0: self = .initial(value: rawValue)
case 12.0..<14.0: self = .five(value: rawValue)
case 10.0..<12.0: self = .four(value: rawValue)
case 8.0..<10.0: self = .three(value: rawValue)
case 6.0..<8.0: self = .two(value: rawValue)
case 4.0..<6.0: self = .one(value: rawValue)
case 2.0..<4.0: self = .zero(value: rawValue)
case 0.0..<2.0: self = .over(value: rawValue)
default: self = .none
}
}

Expand Down Expand Up @@ -58,43 +60,44 @@ enum TimeState {

var isDotHidden: Bool {
switch self {
case .initial, .over: return true
case .initial, .over, .none: return true
default: return false
}
}

var fillColor: DSKitColors {
var trackLayerStrokeColor: DSKitColors {
switch self {
case .over: return DSKitAsset.Color.neutral300
case .initial, .over, .none: return DSKitAsset.Color.neutral300
default: return DSKitAsset.Color.clear
}
}

var getText: String {
switch self {
case .initial, .over: return "-"
case .five: return "5"
case .four: return "4"
case .three: return "3"
case .two: return "2"
case .one: return "1"
case .zero: return "0"
default: return "-"
}
}

var getProgress: Double {
switch self {
case .initial: return 1
case .five(let value), .four(let value), .three(let value), .two(let value), .one(let value), .zero(let value), .over(let value):
return round((value / 2 - 1) / 5 * 1000) / 1000
return round((value / 2 - 2) / 5 * 1000) / 1000
default: return 0
}
}
}

final private class Timer {
private var disposable: Disposable? = nil

let currentTime = BehaviorRelay<Double>(value: 13.0)
let currentTime = BehaviorRelay<Double>(value: 15.0)
private var startTime: Double

init(startTime: Double) {
Expand All @@ -103,7 +106,7 @@ final private class Timer {

func start() {
guard disposable == nil else { return }
if startTime < 0 { startTime = 13.0 }
if startTime < 0 { startTime = 15.0 }

disposable = Observable<Int>.interval(.milliseconds(10),
scheduler: MainScheduler.instance)
Expand Down Expand Up @@ -144,8 +147,8 @@ final class FallingUserCollectionViewCellModel: ViewModelType {
struct Output {
let user: Driver<FallingUser>
let timeState: Driver<TimeState>
let timeZero: Driver<AnimationAction>
let timerActiveAction: Driver<Bool>
let timeZero: Driver<AnimationAction>
let rejectButtonAction: Driver<Void>
let likeButtonAction: Driver<Void>
let showUserInfoAction: Driver<Bool>
Expand All @@ -154,20 +157,18 @@ final class FallingUserCollectionViewCellModel: ViewModelType {
}

func transform(input: Input) -> Output {
let timer = Timer(startTime: 13.0)
let user = Driver.just(self.userDomain)

let timerActiveTrigger = input.timerActiveTrigger

let rejectButtonAction = input.rejectButtonTapTrigger
.do(onNext: { _ in
timer.pause()
timer.currentTime.accept(-1.0) // reject 시에는 0.5초 후에 넘어 가야하는 제약이 있어서, 0초로 설정하지 않았고, reject 버튼 이벤트에 대한 처리는 상위 뷰에서 따로 처리하고 있음.
})
let timer = Timer(startTime: 15.0)
let time = timer.currentTime.asDriver(onErrorJustReturn: 0.0)
let user = Driver.just(userDomain)

let likeButtonAction = input.likeButtonTapTrigger
let timeState = Driver.merge(
input.rejectButtonTapTrigger.map { TimeState.none },
input.likeButtonTapTrigger.map { TimeState.none },
input.deleteCellTrigger.map { TimeState.none },
time.map { TimeState(rawValue: $0) }
)

let timeActiveAction = timerActiveTrigger
let timerActiveAction = input.timerActiveTrigger
.do { value in
if !value {
timer.pause()
Expand All @@ -176,12 +177,17 @@ final class FallingUserCollectionViewCellModel: ViewModelType {
}
}

let time = timer.currentTime.asDriver(onErrorJustReturn: 0.0)

let timeState = time.map { TimeState(rawValue: $0) }
let timeZero = time.filter { $0 == 0.0 }.flatMapLatest { _ in Driver.just(AnimationAction.scroll) }
let timerActiveAction = timeActiveAction.asDriver(onErrorJustReturn:
true)

let rejectButtonAction = input.rejectButtonTapTrigger
.do { _ in
timer.pause()
}

let likeButtonAction = input.likeButtonTapTrigger
.do { _ in
timer.pause()
}

let showUserInfoAction = input.showUserInfoTrigger

Expand All @@ -190,14 +196,13 @@ final class FallingUserCollectionViewCellModel: ViewModelType {
let deleteCellAction = input.deleteCellTrigger
.do(onNext: {
timer.pause()
timer.currentTime.accept(-1.0)
})

return Output(
user: user,
timeState: timeState,
timeZero: timeZero,
timerActiveAction: timerActiveAction,
timeZero: timeZero,
rejectButtonAction: rejectButtonAction,
likeButtonAction: likeButtonAction,
showUserInfoAction: showUserInfoAction,
Expand Down

0 comments on commit e48ed0c

Please sign in to comment.