-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
💄 :: [#812] Credit Song List Page UI
- Loading branch information
Showing
29 changed files
with
1,008 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
Projects/Features/CreditSongListFeature/Interface/CreditSongListFactory.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import UIKit | ||
|
||
public protocol CreditSongListFactory { | ||
func makeViewController(workerName: String) -> UIViewController | ||
} |
5 changes: 5 additions & 0 deletions
5
Projects/Features/CreditSongListFeature/Interface/CreditSongListTabFactory.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import UIKit | ||
|
||
public protocol CreditSongListTabFactory { | ||
func makeViewController(workerName: String) -> UIViewController | ||
} |
5 changes: 5 additions & 0 deletions
5
Projects/Features/CreditSongListFeature/Interface/CreditSongListTabItemFactory.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import UIKit | ||
|
||
public protocol CreditSongListTabItemFactory { | ||
func makeViewController(workerName: String, sortType: CreditSongSortType) -> UIViewController | ||
} |
16 changes: 16 additions & 0 deletions
16
Projects/Features/CreditSongListFeature/Interface/CreditSortType.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
public enum CreditSongSortType: CaseIterable { | ||
case latest | ||
case popular | ||
case oldest | ||
|
||
public var display: String { | ||
switch self { | ||
case .latest: | ||
return "최신순" | ||
case .popular: | ||
return "인기순" | ||
case .oldest: | ||
return "과거순" | ||
} | ||
} | ||
} |
1 change: 0 additions & 1 deletion
1
Projects/Features/CreditSongListFeature/Interface/Interface.swift
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
...ures/CreditSongListFeature/Sources/CreditSongList/Component/CreditSongListComponent.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import CreditSongListFeatureInterface | ||
import NeedleFoundation | ||
import UIKit | ||
|
||
public protocol CreditSongListDependency: Dependency { | ||
var creditSongListTabFactory: any CreditSongListTabFactory { get } | ||
} | ||
|
||
public final class CreditSongListComponent: Component<CreditSongListDependency>, CreditSongListFactory { | ||
public func makeViewController(workerName: String) -> UIViewController { | ||
let reactor = CreditSongListReactor(workerName: workerName) | ||
let viewController = CreditSongListViewController( | ||
reactor: reactor, | ||
creditSongListTabFactory: dependency.creditSongListTabFactory | ||
) | ||
return viewController | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
Projects/Features/CreditSongListFeature/Sources/CreditSongList/CreditSongListReactor.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import ReactorKit | ||
|
||
final class CreditSongListReactor: Reactor { | ||
enum Action {} | ||
enum Mutation {} | ||
struct State { | ||
var workerName: String | ||
} | ||
|
||
let initialState: State | ||
internal let workerName: String | ||
|
||
init(workerName: String) { | ||
self.initialState = .init( | ||
workerName: workerName | ||
) | ||
self.workerName = workerName | ||
} | ||
|
||
func mutate(action: Action) -> Observable<Mutation> { | ||
.empty() | ||
} | ||
|
||
func reduce(state: State, mutation: Mutation) -> State { | ||
state | ||
} | ||
} |
170 changes: 170 additions & 0 deletions
170
.../Features/CreditSongListFeature/Sources/CreditSongList/CreditSongListViewController.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
import BaseFeature | ||
import CreditSongListFeatureInterface | ||
import DesignSystem | ||
import RxSwift | ||
import SnapKit | ||
import UIKit | ||
import Utility | ||
|
||
final class CreditSongListViewController: BaseReactorViewController<CreditSongListReactor> { | ||
private let creditProfileGradientContainerView = UIView().then { | ||
$0.alpha = 0.6 | ||
} | ||
|
||
private let wmNavigationBar = WMNavigationBarView() | ||
private let dismissButton = UIButton(type: .system).then { | ||
$0.setImage(DesignSystemAsset.Navigation.back.image, for: .normal) | ||
$0.tintColor = .black | ||
} | ||
|
||
private let creditProfileView = CreditProfileView() | ||
private let creditSongListTabViewController: UIViewController | ||
private var creditSongListTabView: UIView { creditSongListTabViewController.view } | ||
|
||
private var creditProfileViewTopConstraint: NSLayoutConstraint? | ||
private var minusCreditProfileViewMaxHeight: CGFloat = 0 | ||
private var scrollHistory: [Int: CGFloat] = [:] | ||
|
||
private var profileGradientLayer: CAGradientLayer? | ||
|
||
init( | ||
reactor: Reactor, | ||
creditSongListTabFactory: any CreditSongListTabFactory | ||
) { | ||
self.creditSongListTabViewController = creditSongListTabFactory.makeViewController( | ||
workerName: reactor.workerName | ||
) | ||
super.init(reactor: reactor) | ||
} | ||
|
||
override func viewDidLayoutSubviews() { | ||
super.viewDidLayoutSubviews() | ||
if profileGradientLayer == nil { | ||
let gradientLayer = CAGradientLayer() | ||
gradientLayer.colors = [ | ||
DesignSystemAsset.BlueGrayColor.blueGray900.color.cgColor, | ||
DesignSystemAsset.BlueGrayColor.blueGray900.color.withAlphaComponent(0.0).cgColor | ||
] | ||
profileGradientLayer?.frame = creditProfileGradientContainerView.bounds | ||
creditProfileGradientContainerView.layer.addSublayer(gradientLayer) | ||
profileGradientLayer = gradientLayer | ||
} | ||
|
||
if minusCreditProfileViewMaxHeight == 0, creditProfileView.frame.height != 0 { | ||
minusCreditProfileViewMaxHeight = -creditProfileView.frame.height | ||
} | ||
} | ||
|
||
override func addView() { | ||
view.addSubviews(creditProfileGradientContainerView, wmNavigationBar, creditProfileView) | ||
wmNavigationBar.setLeftViews([dismissButton]) | ||
|
||
addChild(creditSongListTabViewController) | ||
view.addSubviews(creditSongListTabView) | ||
creditSongListTabViewController.didMove(toParent: self) | ||
} | ||
|
||
override func setLayout() { | ||
creditProfileGradientContainerView.snp.makeConstraints { | ||
$0.top.horizontalEdges.equalToSuperview() | ||
$0.height.equalTo(200) | ||
} | ||
|
||
wmNavigationBar.snp.makeConstraints { | ||
$0.top.equalToSuperview().offset(STATUS_BAR_HEGHIT()) | ||
$0.horizontalEdges.equalToSuperview() | ||
$0.height.equalTo(48) | ||
} | ||
|
||
creditProfileViewTopConstraint = creditProfileView.topAnchor.constraint( | ||
equalTo: wmNavigationBar.bottomAnchor, | ||
constant: 8 | ||
) | ||
creditProfileViewTopConstraint?.isActive = true | ||
|
||
creditProfileView.snp.makeConstraints { | ||
$0.horizontalEdges.equalToSuperview().inset(20) | ||
} | ||
|
||
creditSongListTabView.snp.makeConstraints { | ||
$0.top.equalTo(creditProfileView.snp.bottom).offset(24) | ||
$0.horizontalEdges.bottom.equalToSuperview() | ||
} | ||
} | ||
|
||
override func configureUI() { | ||
view.backgroundColor = DesignSystemAsset.BlueGrayColor.blueGray100.color | ||
} | ||
|
||
override func configureNavigation() {} | ||
|
||
override func bindAction(reactor: CreditSongListReactor) { | ||
dismissButton.rx.tap | ||
.bind(with: self) { owner, _ in | ||
owner.navigationController?.popViewController(animated: true) | ||
} | ||
.disposed(by: disposeBag) | ||
} | ||
|
||
override func bindState(reactor: CreditSongListReactor) { | ||
let sharedState = reactor.state.share() | ||
|
||
sharedState.map(\.workerName) | ||
.bind(with: self) { owner, name in | ||
owner.creditProfileView.updateProfile(name: name) | ||
} | ||
.disposed(by: disposeBag) | ||
|
||
CreditSongListScopedState.shared.creditSongTabItemScrolledObservable | ||
.observe(on: MainScheduler.asyncInstance) | ||
.bind(with: self) { owner, scrollView in | ||
owner.songListDidScroll(scrollView: scrollView) | ||
} | ||
.disposed(by: disposeBag) | ||
} | ||
} | ||
|
||
private extension CreditSongListViewController { | ||
func songListDidScroll(scrollView: UIScrollView) { | ||
guard let creditProfileViewTopConstraint else { return } | ||
|
||
let scrollDiff = scrollView.contentOffset.y - scrollHistory[ | ||
scrollView.hash, | ||
default: scrollView.contentOffset.y | ||
] | ||
let absoluteTop: CGFloat = 0 | ||
let absoluteBottom: CGFloat = scrollView.contentSize.height - scrollView.frame.size.height | ||
let isScrollingDown = scrollDiff > 0 && scrollView.contentOffset.y > absoluteTop | ||
let isScrollingUp = scrollDiff < 0 && scrollView.contentOffset.y < absoluteBottom | ||
|
||
if scrollView.contentOffset.y < absoluteBottom { | ||
var newHeight = creditProfileViewTopConstraint.constant | ||
|
||
if isScrollingDown { | ||
newHeight = max( | ||
minusCreditProfileViewMaxHeight, | ||
creditProfileViewTopConstraint.constant - abs(scrollDiff) | ||
) | ||
} else if isScrollingUp { | ||
if scrollView.contentOffset.y <= abs(minusCreditProfileViewMaxHeight) { | ||
newHeight = min( | ||
8, | ||
creditProfileViewTopConstraint.constant + abs(scrollDiff) | ||
) | ||
} | ||
} | ||
|
||
if newHeight != creditProfileViewTopConstraint.constant { | ||
creditProfileViewTopConstraint.constant = newHeight | ||
|
||
let openAmount = creditProfileViewTopConstraint.constant + abs(minusCreditProfileViewMaxHeight) | ||
let percentage = openAmount / abs(minusCreditProfileViewMaxHeight) | ||
creditProfileView.alpha = percentage | ||
|
||
self.view.layoutIfNeeded() | ||
} | ||
|
||
scrollHistory[scrollView.hash] = scrollView.contentOffset.y | ||
} | ||
} | ||
} |
Oops, something went wrong.