Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b0bbddb
fix: #346 실제로 시스템 설정에 다녀온 경우에만 checkNotificationAuthorizationWhenBack 호출
dev-domo Dec 8, 2025
089f3a6
refactor: #346 최신 토글 상태 변수를 실제 토글 버튼의 isOn 속성으로 초기화
dev-domo Dec 8, 2025
4d6be1e
setting: 프로비저닝 파일 업데이트
dev-domo Feb 12, 2026
430cc36
style: #346 탭바 구현에 필요한 이미지 세팅
dev-domo Feb 15, 2026
2f0cf17
feat: #346 상단 탭바 설계
dev-domo Feb 15, 2026
c165f21
feat: #346 퀘스트 전체 조회 뷰에 상단 탭바 적용
dev-domo Feb 15, 2026
c11f62b
refactor: #346 퀘스트 뷰로 이동하는 메서드 추가
dev-domo Feb 15, 2026
3b6eea6
setting: #346 entitlements 수정
dev-domo Feb 16, 2026
fe06d5f
setting: #354 프로피버징 파일 설정 변경
dev-domo Feb 16, 2026
be574f9
Merge branch 'fix/#346-questToggle' into feat/#354-questTopTabBar
dev-domo Feb 16, 2026
74cd809
feat: #354 상단 탭바 구현
dev-domo Feb 16, 2026
4e1e3b2
feat: #354 퀘스트 조회 화면용 탭 아이템 구현
dev-domo Feb 16, 2026
d6d1d62
feat: #354 공통 퀘스트 뷰컨트롤러 생성
dev-domo Feb 16, 2026
df0f855
refactor: #354 새로운 퀘스트 조회 뷰컨트롤러로 이동하는 로직 추가
dev-domo Feb 16, 2026
af5cbce
style: #354 퀘스트 상단 탭바에 필요한 이미지 세팅
dev-domo Feb 16, 2026
8276d47
fix: #354 퀘스트 토글 오류 수정
dev-domo Feb 16, 2026
16738a5
Merge remote-tracking branch 'origin/feat/#354-questTopTabBar' into f…
dev-domo Feb 16, 2026
c37cee6
setting: #354 entitlements 설정 복구
dev-domo Feb 16, 2026
1af4c50
refactor: #354 변수명 수정
dev-domo Feb 16, 2026
8aab424
refactor: #354 메서드명 수정
dev-domo Feb 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ByeBoo-iOS/ByeBoo-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "com.heartz.ByeBoo-iOS";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.heartz.ByeBoo-iOS";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.heartz.ByeBoo-iOS";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ final class ByeBooTabBar: UITabBarController {
private func setViewController() {

let homeViewController = ViewControllerFactory.shared.makeHomeViewController()
let questViewController = ViewControllerFactory.shared.makeQuestViewController()
let questViewController = ViewControllerFactory.shared.makeParentQuestViewController()
let myPageViewController = ViewControllerFactory.shared.makeMyPageViewController()

self.viewControllers = [
Expand Down
14 changes: 14 additions & 0 deletions ByeBoo-iOS/ByeBoo-iOS/Presentation/Common/TopTabBar/TabItem.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// TabItem.swift
// ByeBoo-iOS
//
// Created by APPLE on 2/12/26.
//

import UIKit

protocol TabItem: CaseIterable {
var title: String { get }
var image: UIImage { get }
var viewController: UIViewController { get }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// TopTabBar.swift
// ByeBoo-iOS
//
// Created by APPLE on 2/12/26.
//

import UIKit

final class TopTabBar: UIStackView {

private let itemViews: [TopTabBarItemView]
var didTap: ((Int) -> Void)?

init(items: [any TabItem]) {
self.itemViews = items.map { TopTabBarItemView(item: $0) }
super.init(frame: .zero)

setStyle()
setUI()
setAction()
}

required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

private func setStyle() {
self.do {
$0.backgroundColor = .grayscale900
$0.axis = .horizontal
$0.spacing = 4
$0.distribution = .fillEqually
}
}

private func setUI() {
itemViews.forEach { self.addArrangedSubview($0) }
}

private func setAction() {
itemViews.enumerated().forEach { index, itemView in
let tapGesture = UITapGestureRecognizer(
target: self, action: #selector(barDidTap(_:))
)
itemView.addGestureRecognizer(tapGesture)
itemView.tag = index
}

if let firstIndex = itemViews.indices.first {
updateTabBar(tag: firstIndex)
}
}
}

extension TopTabBar {

@objc
private func barDidTap(_ sender: UITapGestureRecognizer) {
guard let tag = sender.view?.tag else {
return
}

didTap?(tag)
updateTabBar(tag: tag)
}

private func updateTabBar(tag: Int) {
itemViews.forEach { $0.updateBarItem(for: tag) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// TopTabBarItemView.swift
// ByeBoo-iOS
//
// Created by APPLE on 2/12/26.
//

import UIKit

final class TopTabBarItemView: BaseView {

private let tabStackView = UIStackView()
private let tabImageView = UIImageView()
private let tabNameLabel = UILabel()
private let underlineLabel = UILabel()

init(item: any TabItem) {
tabImageView.image = item.image
tabNameLabel.text = item.title

super.init(frame: .zero)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func setStyle() {
tabStackView.do {
$0.axis = .horizontal
$0.spacing = 2
$0.alignment = .center
}
tabNameLabel.do {
$0.textColor = .grayscale100
$0.font = FontManager.body2M16.font
$0.textAlignment = .center
}
underlineLabel.do {
$0.layer.borderColor = UIColor.grayscale300.cgColor
$0.layer.borderWidth = 1
}
}

override func setUI() {
addSubviews(
tabStackView,
underlineLabel
)
tabStackView.addArrangedSubviews(
tabImageView,
tabNameLabel
)
}

override func setLayout() {
tabStackView.snp.makeConstraints {
$0.top.equalToSuperview()
$0.centerX.equalToSuperview()
$0.width.equalTo(108.adjustedW)
$0.height.equalTo(24.adjustedH)
}
tabImageView.snp.makeConstraints {
$0.size.equalTo(24.adjustedW)
$0.verticalEdges.equalToSuperview()
$0.leading.equalToSuperview().inset(9.5.adjustedW)
}
tabNameLabel.snp.makeConstraints {
$0.leading.equalTo(tabImageView.snp.trailing).offset(2.adjustedW)
$0.centerY.equalToSuperview()
$0.trailing.equalToSuperview().inset(11.5.adjustedW)
}
underlineLabel.snp.makeConstraints {
$0.top.equalTo(tabStackView.snp.bottom).offset(4.adjustedH)
$0.horizontalEdges.equalToSuperview()
$0.height.equalTo(1.adjustedH)
$0.bottom.equalToSuperview()
}
}
}

extension TopTabBarItemView {

func updateBarItem(for tag: Int) {
let condition = (self.tag == tag)

tabImageView.layer.opacity = condition ? 1 : 0.44
tabNameLabel.textColor = condition ? .grayscale100 : .grayscale600
underlineLabel.layer.borderColor = condition ? UIColor.grayscale300.cgColor : UIColor.clear.cgColor
underlineLabel.layer.borderWidth = condition ? 1 : 0
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기 변경사항은 as-is랑 to-be가 어떻게 바뀐건가요?

Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ final class MyPageViewController: BaseViewController {
private let viewModel: MyPageViewModel
private var cancellables = Set<AnyCancellable>()
private var name: String?
private var beforeNotificationStatus = false

private let rootView = MyPageView()
private lazy var beforeNotificationStatus = rootView.noticeView.noticeSwitch.isOn
private var didOpenSetting = false

init(viewModel: MyPageViewModel) {
self.viewModel = viewModel
Expand Down Expand Up @@ -204,25 +205,29 @@ extension MyPageViewController {

@objc
private func checkNoticeAuthorizationWhenBack() {
guard didOpenSetting else { return }

UNUserNotificationCenter.current().getNotificationSettings { [weak self] settings in
guard let self else { return }

let isAuthorized: Bool
switch settings.authorizationStatus {
case .authorized, .provisional, .ephemeral:
if !beforeNotificationStatus {
self.viewModel.action(.notificationSwitchDidTap)
}
beforeNotificationStatus = true
isAuthorized = true
default:
if beforeNotificationStatus {
self.viewModel.action(.notificationSwitchDidTap)
} else {
DispatchQueue.main.async {
self.rootView.noticeView.noticeSwitch.setOn(false, animated: false)
}
isAuthorized = false
}

if beforeNotificationStatus != isAuthorized {
viewModel.action(.notificationSwitchDidTap)
} else if !isAuthorized {
DispatchQueue.main.async {
self.rootView.noticeView.noticeSwitch.setOn(false, animated: false)
}
beforeNotificationStatus = false
}

beforeNotificationStatus = isAuthorized
didOpenSetting = false
}
}

Expand Down Expand Up @@ -425,6 +430,7 @@ extension MyPageViewController {
private func moveSetting() {
if let url = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(url)
didOpenSetting = true
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// QuestTabItem.swift
// ByeBoo-iOS
//
// Created by APPLE on 2/12/26.
//

import UIKit

enum QuestTabItem: TabItem {

case myJourney
case commonJourney

var title: String {
switch self {
case .myJourney:
return "나의 여정"
case .commonJourney:
return "공통 여정"
}
}

var image: UIImage {
switch self {
case .myJourney:
return .myJourney
case .commonJourney:
return .commonJourney
}
}

var viewController: UIViewController {
switch self {
case .myJourney:
return ViewControllerFactory.shared.makeQuestViewController()
case .commonJourney:
return ViewControllerFactory.shared.makeCommonQuestViewController()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// CommonQuestViewController.swift
// ByeBoo-iOS
//
// Created by APPLE on 2/12/26.
//

final class CommonQuestViewController: BaseViewController {

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: false)
}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: false)
}
}
Loading
Loading