diff --git a/iBox/Sources/Base/BaseViewController.swift b/iBox/Sources/Base/BaseViewController.swift index fd38cb5..c4be0a0 100644 --- a/iBox/Sources/Base/BaseViewController.swift +++ b/iBox/Sources/Base/BaseViewController.swift @@ -15,4 +15,5 @@ class BaseViewController: UIViewController { super.viewDidLoad() view.addSubview(baseView) } + } diff --git a/iBox/Sources/BoxList/BoxListView.swift b/iBox/Sources/BoxList/BoxListView.swift new file mode 100644 index 0000000..93d1e45 --- /dev/null +++ b/iBox/Sources/BoxList/BoxListView.swift @@ -0,0 +1,163 @@ +// +// BoxListView.swift +// iBox +// +// Created by 이지현 on 1/3/24. +// + +import UIKit + +import SnapKit + +protocol BoxListViewDelegate: AnyObject { + func didSelectWeb(at url: String, withName name: String) +} + +class BoxListView: UIView { + weak var delegate: BoxListViewDelegate? + + var folderArr = [ + Folder(name: "기본 폴더", color: .gray, webs: [ + Web(name: "42 Intra", url: "https://profile.intra.42.fr/"), + Web(name: "42Where", url: "https://where42.kr/"), + Web(name: "42Stat", url: "https://stat.42seoul.kr/"), + Web(name: "집현전", url: "https://42library.kr/") + ]), + Folder(name: "새 폴더", color: .green, webs: [Web(name: "Cabi", url: "https://cabi.42seoul.io/")], isOpened: false), + Folder(name: "새 폴더(2)", color: .yellow, webs: [Web(name: "24HANE", url: "https://24hoursarenotenough.42seoul.kr/")], isOpened: false) + ] + + private lazy var backgroundView = { + let view = UIView() + view.clipsToBounds = true + view.layer.cornerRadius = 20 + view.backgroundColor = ColorPalette.tableViewBackgroundColor + + view.addSubview(tableView) + tableView.snp.makeConstraints { make in + make.top.bottom.equalToSuperview().offset(10) + make.leading.trailing.equalToSuperview() + } + return view + }() + + private lazy var tableView = { + let tableView = UITableView() + tableView.dataSource = self + tableView.delegate = self + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + + tableView.sectionHeaderTopPadding = 0 +// tableView.separatorInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15) + tableView.clipsToBounds = true + tableView.layer.cornerRadius = 20 + tableView.backgroundColor = .clear + tableView.separatorColor = .clear + return tableView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = ColorPalette.backgroundColor + + setupLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupLayout() { + addSubview(backgroundView) + backgroundView.snp.makeConstraints { make in + make.top.equalTo(safeAreaLayoutGuide).inset(10) + make.leading.trailing.bottom.equalTo(safeAreaLayoutGuide).inset(20) + } + } +} + +extension BoxListView: UITableViewDataSource { + + func numberOfSections(in tableView: UITableView) -> Int { + return folderArr.count + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + if !folderArr[section].isOpened { + return 0 + } + return folderArr[section].webs.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + cell.selectionStyle = .none + cell.backgroundColor = .clear + cell.textLabel?.text = folderArr[indexPath.section].webs[indexPath.row].name + cell.imageView?.image = UIImage(systemName: "ellipsis.rectangle.fill") + cell.imageView?.tintColor = ColorPalette.webIconColor + return cell + } + +} + +extension BoxListView: UITableViewDelegate { + + public func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + let view = UIView() + let line = UIView() + view.addSubview(line) + line.snp.makeConstraints { make in + make.top.bottom.equalToSuperview() + make.leading.trailing.equalToSuperview().inset(15) + } + view.backgroundColor = ColorPalette.tableViewBackgroundColor + line.backgroundColor = .tertiaryLabel + return view + } + + public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + return 0.3 + } + + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return 50 + } + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + let button = FolderButton(isOpen: folderArr[section].isOpened) + button.setFolderName(folderArr[section].name) + button.setFolderColor(folderArr[section].color.toUIColor()) + button.tag = section + + button.addTarget(self, action: #selector(handleOpenClose), for: .touchUpInside) + + return button + } + + @objc private func handleOpenClose(button: FolderButton) { + let section = button.tag + + var indexPaths = [IndexPath]() + for row in folderArr[section].webs.indices { + let indexPath = IndexPath(row: row, section: section) + indexPaths.append(indexPath) + } + + folderArr[section].isOpened.toggle() + button.toggleStatus() + + if folderArr[section].isOpened { + tableView.insertRows(at: indexPaths, with: .fade) + } else { + tableView.deleteRows(at: indexPaths, with: .fade) + } + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let webUrl = folderArr[indexPath.section].webs[indexPath.row].url + let webName = folderArr[indexPath.section].webs[indexPath.row].name + delegate?.didSelectWeb(at: webUrl, withName: webName) + } +} + diff --git a/iBox/Sources/BoxList/BoxListViewController.swift b/iBox/Sources/BoxList/BoxListViewController.swift new file mode 100644 index 0000000..35806f8 --- /dev/null +++ b/iBox/Sources/BoxList/BoxListViewController.swift @@ -0,0 +1,29 @@ +// +// BoxListViewController.swift +// iBox +// +// Created by 이지현 on 12/27/23. +// + +import UIKit + +class BoxListViewController: BaseViewController { + + override func viewDidLoad() { + super.viewDidLoad() + baseView.delegate = self + + title = "iBox" + navigationController?.navigationBar.prefersLargeTitles = true + } + +} + +extension BoxListViewController: BoxListViewDelegate { + func didSelectWeb(at url: String, withName name: String) { + let viewController = WebViewController() + viewController.title = name + viewController.selectedWebsite = url + navigationController?.pushViewController(viewController, animated: true) + } +} diff --git a/iBox/Sources/BoxList/FolderButton.swift b/iBox/Sources/BoxList/FolderButton.swift new file mode 100644 index 0000000..a2587f9 --- /dev/null +++ b/iBox/Sources/BoxList/FolderButton.swift @@ -0,0 +1,81 @@ +// +// FolderButton.swift +// iBox +// +// Created by 이지현 on 1/4/24. +// + +import UIKit + +import SnapKit + +class FolderButton: UIButton { + private var isOpen: Bool = true + + private lazy var folderImageView = { + let imageView = UIImageView() + imageView.image = UIImage(systemName: "folder.fill") + imageView.contentMode = .scaleAspectFit + return imageView + }() + + private lazy var folderNameLabel = { + let label = UILabel() + label.textColor = .label + label.font = .systemFont(ofSize: 18, weight: .semibold) + return label + }() + + private lazy var openCloseImageView = { + let imageView = UIImageView() + imageView.tintColor = .tertiaryLabel + imageView.image = isOpen ? UIImage(systemName: "chevron.up") : UIImage(systemName: "chevron.down") + return imageView + }() + + init(isOpen: Bool) { + self.isOpen = isOpen + super.init(frame: .zero) + backgroundColor = .clear + + setupLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupLayout() { + addSubview(folderImageView) + folderImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.width.height.equalTo(30) + make.leading.equalToSuperview().offset(20) + } + + addSubview(folderNameLabel) + folderNameLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.leading.equalTo(folderImageView.snp.trailing).offset(10) + } + + addSubview(openCloseImageView) + openCloseImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.trailing.equalToSuperview().offset(-20) + } + } + + func setFolderName(_ name: String) { + folderNameLabel.text = name + } + + func setFolderColor(_ color: UIColor) { + folderImageView.tintColor = color + } + + func toggleStatus() { + isOpen = !isOpen + openCloseImageView.image = isOpen ? UIImage(systemName: "chevron.up") : UIImage(systemName: "chevron.down") + } +} diff --git a/iBox/Sources/BoxList/Model/Folder.swift b/iBox/Sources/BoxList/Model/Folder.swift new file mode 100644 index 0000000..9ea6fc4 --- /dev/null +++ b/iBox/Sources/BoxList/Model/Folder.swift @@ -0,0 +1,20 @@ +// +// Folder.swift +// iBox +// +// Created by 이지현 on 1/4/24. +// + +import Foundation + +struct Folder { + let name: String + let color: ColorName + let webs: [Web] + var isOpened: Bool = true +} + +struct Web { + let name: String + let url: String +} diff --git a/iBox/Sources/ColorPalette.swift b/iBox/Sources/ColorPalette.swift new file mode 100644 index 0000000..da9113b --- /dev/null +++ b/iBox/Sources/ColorPalette.swift @@ -0,0 +1,76 @@ +// +// ColorPalette.swift +// iBox +// +// Created by 이지현 on 1/3/24. +// + +import UIKit + +enum ColorName: String { + case gray + case green + case red + case blue + case yellow + + func toUIColor() -> UIColor { + switch self { + case .gray: + return UIColor.systemGray2 + case .green: + return UIColor.systemGreen + case .red: + return UIColor.systemRed + case .blue: + return UIColor.systemBlue + case .yellow: + return UIColor.systemYellow + } + } +} + +struct ColorPalette { + + public static var backgroundColor = { + return UIColor { (UITraitCollection: UITraitCollection) -> UIColor in + if UITraitCollection.userInterfaceStyle == .dark { + return .black + } else { + return .systemGray5 + } + } + }() + + public static var tableViewBackgroundColor = { + return UIColor { (UITraitCollection: UITraitCollection) -> UIColor in + if UITraitCollection.userInterfaceStyle == .dark { + return .systemGray5 + } else { + return .white + } + } + }() + + public static var folderGray = { + return UIColor { (UITraitCollection: UITraitCollection) -> UIColor in + if UITraitCollection.userInterfaceStyle == .dark { + return .systemGray2 + } else { + return .systemGray3 + } + } + }() + + public static var webIconColor = { + return UIColor { (UITraitCollection: UITraitCollection) -> UIColor in + if UITraitCollection.userInterfaceStyle == .dark { + return .systemGray + } else { + return .black + } + } + }() + +} + diff --git a/iBox/Sources/Presenter/BoxListViewController.swift b/iBox/Sources/Presenter/BoxListViewController.swift deleted file mode 100644 index 3ea9aa1..0000000 --- a/iBox/Sources/Presenter/BoxListViewController.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// BoxListViewController.swift -// iBox -// -// Created by 이지현 on 12/27/23. -// - -import UIKit - -class BoxListViewController: BaseViewController { - - override func viewDidLoad() { - super.viewDidLoad() - - // Do any additional setup after loading the view. - } - - - /* - // MARK: - Navigation - - // In a storyboard-based application, you will often want to do a little preparation before navigation - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - // Get the new view controller using segue.destination. - // Pass the selected object to the new view controller. - } - */ - -} diff --git a/iBox/Sources/Presenter/MainTabBarController.swift b/iBox/Sources/Presenter/MainTabBarController.swift index b7e0318..571cd94 100644 --- a/iBox/Sources/Presenter/MainTabBarController.swift +++ b/iBox/Sources/Presenter/MainTabBarController.swift @@ -14,6 +14,7 @@ class MainTabBarController: UITabBarController { view.backgroundColor = .systemBackground setupTabBar() + setupTabBarAppearance() } private func setupTabBar() { @@ -26,8 +27,15 @@ class MainTabBarController: UITabBarController { } private func setupViewController(viewController: UIViewController, image: UIImage?) -> UIViewController { + viewController.tabBarItem.title = "" viewController.tabBarItem.image = image return UINavigationController(rootViewController: viewController) } + + private func setupTabBarAppearance() { + let appearance = UITabBarItem.appearance() + appearance.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.clear], for: .normal) + appearance.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.clear], for: .selected) + } } diff --git a/iBox/Sources/Web/WebView.swift b/iBox/Sources/Web/WebView.swift new file mode 100644 index 0000000..bd72e75 --- /dev/null +++ b/iBox/Sources/Web/WebView.swift @@ -0,0 +1,56 @@ +// +// WebView.swift +// iBox +// +// Created by 이지현 on 1/4/24. +// + +import UIKit +import WebKit + +import SnapKit + +class WebView: UIView { + var selectedWebsite: String? { + didSet { + loadWebsite() + } + } + + private lazy var webView = { + let webView = WKWebView() + webView.navigationDelegate = self +// webView.scrollView.contentInsetAdjustmentBehavior = .always + + return webView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = .systemBackground + + setupLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupLayout() { + addSubview(webView) + webView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + + private func loadWebsite() { + guard let website = selectedWebsite, let url = URL(string: website) else { return } + webView.load(URLRequest(url: url)) + webView.allowsBackForwardNavigationGestures = true + } + +} + +extension WebView: WKNavigationDelegate { + +} diff --git a/iBox/Sources/Web/WebViewController.swift b/iBox/Sources/Web/WebViewController.swift new file mode 100644 index 0000000..5b47a29 --- /dev/null +++ b/iBox/Sources/Web/WebViewController.swift @@ -0,0 +1,21 @@ +// +// WebViewController.swift +// iBox +// +// Created by 이지현 on 1/4/24. +// + +import UIKit + +class WebViewController: BaseViewController { + var selectedWebsite: String? + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .systemBackground + navigationItem.largeTitleDisplayMode = .never + + baseView.selectedWebsite = selectedWebsite + } + +}