diff --git a/iBox/Sources/Model/HomeTabType.swift b/iBox/Sources/Model/HomeTabType.swift new file mode 100644 index 0000000..2d7bb4c --- /dev/null +++ b/iBox/Sources/Model/HomeTabType.swift @@ -0,0 +1,20 @@ +// +// HomeTabType.swift +// iBox +// +// Created by jiyeon on 2/22/24. +// + +import Foundation + +enum HomeTabType: CaseIterable { + case boxList + case favorite + + func toString() -> String { + switch self { + case .boxList: "북마크 목록" + case .favorite: "즐겨찾기" + } + } +} diff --git a/iBox/Sources/Presenter/MainTabBarController.swift b/iBox/Sources/Presenter/MainTabBarController.swift index 7ffd765..57541e5 100644 --- a/iBox/Sources/Presenter/MainTabBarController.swift +++ b/iBox/Sources/Presenter/MainTabBarController.swift @@ -25,6 +25,7 @@ class MainTabBarController: UITabBarController { ] tabBar.tintColor = .box tabBar.backgroundColor = .systemBackground + selectedIndex = UserDefaultsManager.homeTabIndex.value } private func setupViewController(viewController: UIViewController, image: UIImage?) -> UIViewController { diff --git a/iBox/Sources/Presenter/MyPage/HomeTab/HomeTabSelectorCell.swift b/iBox/Sources/Presenter/MyPage/HomeTab/HomeTabSelectorCell.swift new file mode 100644 index 0000000..224e640 --- /dev/null +++ b/iBox/Sources/Presenter/MyPage/HomeTab/HomeTabSelectorCell.swift @@ -0,0 +1,69 @@ +// +// HomeTabSelectorCell.swift +// iBox +// +// Created by jiyeon on 2/22/24. +// + +import UIKit + +import SnapKit + +class HomeTabSelectorCell: UITableViewCell { + + // MARK: - Properties + + static let reuseIdentifier = "MainTabCell" + + // MARK: - UI Components + + let titleLabel = UILabel().then { + $0.font = .systemFont(ofSize: 16) + } + + let selectButton = UIButton().then { + $0.configuration = .plain() + } + + // MARK: - Initializer + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + setupLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setup Methods + + private func setupLayout() { + selectionStyle = .none + + addSubview(titleLabel) + addSubview(selectButton) + + titleLabel.snp.makeConstraints { + $0.left.equalToSuperview().inset(20) + $0.centerY.equalToSuperview() + } + + selectButton.snp.makeConstraints { + $0.right.equalToSuperview().inset(20) + $0.centerY.equalToSuperview() + $0.width.height.equalTo(20) + } + } + + func setupSelectButton(_ selected: Bool) { + if selected { + selectButton.configuration?.image = UIImage(systemName: "circle.inset.filled") + selectButton.tintColor = .box2 + } else { + selectButton.configuration?.image = UIImage(systemName: "circle") + selectButton.tintColor = .gray + } + } + +} diff --git a/iBox/Sources/Presenter/MyPage/HomeTab/HomeTabSelectorView.swift b/iBox/Sources/Presenter/MyPage/HomeTab/HomeTabSelectorView.swift new file mode 100644 index 0000000..454ae24 --- /dev/null +++ b/iBox/Sources/Presenter/MyPage/HomeTab/HomeTabSelectorView.swift @@ -0,0 +1,95 @@ +// +// HomeTabSelectorView.swift +// iBox +// +// Created by jiyeon on 2/22/24. +// + +import Combine +import UIKit + +class HomeTabSelectorView: BaseView { + + // MARK: - Properties + + private var viewModel: HomeTabSelectorViewModel? + private var cancellables = Set() + + // MARK: - UI Components + + let tableView = UITableView().then { + $0.separatorStyle = .none + $0.register(HomeTabSelectorCell.self, forCellReuseIdentifier: HomeTabSelectorCell.reuseIdentifier) + } + + // MARK: - Initializer + + override init(frame: CGRect) { + super.init(frame: frame) + setupProperty() + setupLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setup Methods + + private func setupProperty() { + tableView.delegate = self + tableView.dataSource = self + } + + private func setupLayout() { + addSubview(tableView) + + tableView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + } + + // MARK: - Bind ViewModel + + func bindViewModel(_ viewModel: HomeTabSelectorViewModel) { + self.viewModel = viewModel + viewModel.$selectedIndex + .receive(on: RunLoop.main) + .sink { [weak self] selectedIndex in + UserDefaultsManager.homeTabIndex.value = selectedIndex + self?.tableView.reloadData() + }.store(in: &cancellables) + } + +} + +extension HomeTabSelectorView: UITableViewDelegate { + + // 셀의 높이 설정 + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 55 + } + + // 테이블 뷰 셀이 선택되었을 때 실행되는 메서드 + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + guard let viewModel = viewModel else { return } + viewModel.selectedIndex = indexPath.row + } + +} + +extension HomeTabSelectorView: UITableViewDataSource { + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return HomeTabType.allCases.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let viewModel = viewModel, + let cell = tableView.dequeueReusableCell(withIdentifier: HomeTabSelectorCell.reuseIdentifier) as? HomeTabSelectorCell else { return UITableViewCell() } + cell.titleLabel.text = HomeTabType.allCases[indexPath.row].toString() + cell.setupSelectButton(viewModel.selectedIndex == indexPath.row) + return cell + } + +} diff --git a/iBox/Sources/Presenter/MyPage/HomeTab/HomeTabSelectorViewController.swift b/iBox/Sources/Presenter/MyPage/HomeTab/HomeTabSelectorViewController.swift new file mode 100644 index 0000000..d6c7bab --- /dev/null +++ b/iBox/Sources/Presenter/MyPage/HomeTab/HomeTabSelectorViewController.swift @@ -0,0 +1,34 @@ +// +// HomeTabSelectorViewwController.swift +// iBox +// +// Created by jiyeon on 2/22/24. +// + +import UIKit + +class HomeTabSelectorViewController: BaseNavigationBarViewController { + + // MARK: - Properties + + private let viewModel = HomeTabSelectorViewModel() + + // MARK: - life cycle + + override func viewDidLoad() { + super.viewDidLoad() + setupNavigationBar() // 얘는 왜 여기에 적어줘야 전부 다 적용이 될까 ..? 🧐 + + guard let contentView = contentView as? HomeTabSelectorView else { return } + contentView.bindViewModel(viewModel) + } + + // MARK: - BaseNavigationBarViewControllerProtocol + + override func setupNavigationBar() { + setNavigationBarTitleLabelText("홈화면 설정하기") + setNavigationBarTitleLabelFont(.systemFont(ofSize: 17, weight: .semibold)) + setNavigationBarBackButtonHidden(false) + } + +} diff --git a/iBox/Sources/Presenter/MyPage/MyPageView.swift b/iBox/Sources/Presenter/MyPage/MyPageView.swift index ee07b29..a066633 100644 --- a/iBox/Sources/Presenter/MyPage/MyPageView.swift +++ b/iBox/Sources/Presenter/MyPage/MyPageView.swift @@ -5,6 +5,7 @@ // Created by jiyeon on 1/3/24. // +import Combine import UIKit class MyPageView: BaseView { @@ -13,6 +14,7 @@ class MyPageView: BaseView { var delegate: MyPageViewDelegate? private var viewModel: MyPageViewModel? + private var cancellables = Set() // MARK: - UI @@ -103,6 +105,14 @@ class MyPageView: BaseView { func bindViewModel(_ viewModel: MyPageViewModel) { self.viewModel = viewModel + viewModel.transform(input: viewModel.input.eraseToAnyPublisher()) + .receive(on: RunLoop.main) + .sink { [weak self] event in + switch event { + case .updateMyPageSectionViewModels: + self?.tableView.reloadData() + } + }.store(in: &cancellables) } // MARK: - functions diff --git a/iBox/Sources/Presenter/MyPage/MyPageViewController.swift b/iBox/Sources/Presenter/MyPage/MyPageViewController.swift index 886a2f6..69349b1 100644 --- a/iBox/Sources/Presenter/MyPage/MyPageViewController.swift +++ b/iBox/Sources/Presenter/MyPage/MyPageViewController.swift @@ -25,6 +25,12 @@ class MyPageViewController: BaseNavigationBarViewController { guard let contentView = contentView as? MyPageView else { return } contentView.delegate = self contentView.bindViewModel(viewModel) + viewModel.input.send(.viewWillAppear) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + viewModel.input.send(.viewWillAppear) } // MARK: - BaseNavigationBarViewControllerProtocol @@ -39,13 +45,17 @@ extension MyPageViewController: MyPageViewDelegate { func pushViewController(_ indexPath: IndexPath) { if indexPath.section == 0 { - navigationController?.pushViewController(ThemeViewController(), animated: true) + switch indexPath.row { + case 0: navigationController?.pushViewController(ThemeViewController(), animated: true) + case 1: navigationController?.pushViewController(HomeTabSelectorViewController(), animated: true) + default: break + } } else { switch indexPath.row { case 0: print("이용 가이드 탭 !") case 1: print("앱 피드백 탭 !") case 2: print("개발자 정보 탭 !") - default: break; + default: break } } } diff --git a/iBox/Sources/Utils/UserDefaultsManager.swift b/iBox/Sources/Utils/UserDefaultsManager.swift index 479734c..8baef12 100644 --- a/iBox/Sources/Utils/UserDefaultsManager.swift +++ b/iBox/Sources/Utils/UserDefaultsManager.swift @@ -10,6 +10,7 @@ import Foundation enum UserDefaultsAccessKey: String { case theme // 다크 모드 case favorite // 즐겨찾기 + case homeTab // 첫 화면 } final class UserDefaultsManager { @@ -21,6 +22,10 @@ final class UserDefaultsManager { key: .favorite, defaultValue: Bookmark(name: "42 Intra", url: "https://profile.intra.42.fr/") ) + static let homeTabIndex = UserDefaultValue( + key: .homeTab, + defaultValue: 0 + ) } class UserDefaultValue { diff --git a/iBox/Sources/ViewModel/HomeTabSelectorViewModel.swift b/iBox/Sources/ViewModel/HomeTabSelectorViewModel.swift new file mode 100644 index 0000000..1e6b63f --- /dev/null +++ b/iBox/Sources/ViewModel/HomeTabSelectorViewModel.swift @@ -0,0 +1,15 @@ +// +// HomeTabSelectorViewModel.swift +// iBox +// +// Created by jiyeon on 2/22/24. +// + +import Combine +import Foundation + +class HomeTabSelectorViewModel { + + @Published var selectedIndex: Int = UserDefaultsManager.homeTabIndex.value + +} diff --git a/iBox/Sources/ViewModel/MyPageViewModel.swift b/iBox/Sources/ViewModel/MyPageViewModel.swift index 75ce095..cc6e8ac 100644 --- a/iBox/Sources/ViewModel/MyPageViewModel.swift +++ b/iBox/Sources/ViewModel/MyPageViewModel.swift @@ -5,23 +5,58 @@ // Created by jiyeon on 2/22/24. // +import Combine import Foundation class MyPageViewModel { - let myPageSectionViewModels: [MyPageSectionViewModel] = [ - MyPageSectionViewModel(MyPageSection( - title: "settings", - items: [MyPageItem(title: "테마")] - )), - MyPageSectionViewModel(MyPageSection( - title: "help", - items: [ - MyPageItem(title: "이용 가이드"), - MyPageItem(title: "앱 피드백"), - MyPageItem(title: "개발자 정보") - ] + enum Input { + case viewWillAppear + } + + enum Output { + case updateMyPageSectionViewModels + } + + // MARK: - Properties + + let input = PassthroughSubject() + private let output = PassthroughSubject() + private var cancellables = Set() + var myPageSectionViewModels = [MyPageSectionViewModel]() + + func transform(input: AnyPublisher) -> AnyPublisher { + input.sink { [weak self] event in + switch event { + case .viewWillAppear: + self?.myPageSectionViewModels.removeAll() + self?.updateMyPageSectionViewModels() + self?.output.send(.updateMyPageSectionViewModels) + } + }.store(in: &cancellables) + return output.eraseToAnyPublisher() + } + + private func updateMyPageSectionViewModels() { + myPageSectionViewModels.append(MyPageSectionViewModel( + MyPageSection( + title: "settings", + items: [ + MyPageItem(title: "테마", description: UserDefaultsManager.theme.value.toString()), + MyPageItem(title: "홈화면", description: HomeTabType.allCases[UserDefaultsManager.homeTabIndex.value].toString()) + ] + ) )) - ] + myPageSectionViewModels.append(MyPageSectionViewModel( + MyPageSection( + title: "help", + items: [ + MyPageItem(title: "이용 가이드"), + MyPageItem(title: "앱 피드백"), + MyPageItem(title: "개발자 정보") + ] + )) + ) + } }