Skip to content

Commit babf690

Browse files
committed
Refactor: 온보딩 UI 수정된 디자인 반영 (#T3-154)
- OnboardingType 워딩 수정 - OnboardingChoiceButton, OnboardingViewController, 등 수정
1 parent fb6ceb3 commit babf690

10 files changed

Lines changed: 500 additions & 344 deletions

File tree

Projects/Presentation/Sources/Common/Extension/NSAttributedString+.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,16 @@ extension NSAttributedString {
1313

1414
attributedString.addAttribute(
1515
.font,
16-
value: BitnagilFont(style: .body2, weight: .regular).font,
16+
value: BitnagilFont(style: .subtitle1, weight: .medium).font,
1717
range: NSRange(location: 0, length: text.count)
1818
)
1919

2020
if let range = text.range(of: highlightText) {
2121
let nsRange = NSRange(range, in: text)
22-
attributedString.addAttribute(
23-
.font,
24-
value: BitnagilFont(style: .body2, weight: .semiBold).font,
25-
range: nsRange
26-
)
22+
attributedString.addAttributes([
23+
.font: BitnagilFont(style: .subtitle1, weight: .semiBold).font,
24+
.foregroundColor: BitnagilColor.orange500 ?? .orange
25+
], range: nsRange)
2726
}
2827
return attributedString
2928
}

Projects/Presentation/Sources/Onboarding/Model/OnboardingChoiceType.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,20 @@ extension OnboardingChoiceType: OnboardingChoiceProtocol {
1515
case .eveningTime: "저녁을 편안하게 마무리하고 싶어요."
1616
case .allTime: "언제든 상관 없어요."
1717

18-
case .never: "밖에 나가지 않고 집에서만 지냈어요."
19-
case .rarely: "잠깐 외출했어요."
20-
case .sometimes: "가끔 나가요."
21-
case .often: "자주 외출해요."
22-
2318
case .stability: "안정감"
2419
case .connection: "연결감"
2520
case .growth: "성장감"
2621
case .vitality: "생동감"
2722

23+
case .never: "나가지 않고 집에서만 지냈어요."
24+
case .rarely: "잠깐 외출했어요."
25+
case .sometimes: "가끔 나가요."
26+
case .often: "자주 외출해요."
27+
2828
case .once: "시작이 더 중요해요."
2929
case .twoToThree: "너무 무리하지 않아도 괜찮아요."
30-
case .fourOrMore: "이 정도면 충분히 활력 있는 한 주가 될거에요."
31-
case .notSure: "목표 선택을 도와드릴게요!"
30+
case .fourOrMore: "충분히 활력 있는 한 주가 될거에요."
31+
case .notSure: "아직 잘 모르겠어요."
3232
}
3333
}
3434

@@ -50,7 +50,7 @@ extension OnboardingChoiceType: OnboardingChoiceProtocol {
5050
case .fourOrMore:
5151
return "일주일에 4회 이상"
5252
case .notSure:
53-
return "아직 잘 모르겠어요"
53+
return "목표 선택을 도와드릴게요!"
5454

5555
default:
5656
return nil

Projects/Presentation/Sources/Onboarding/Model/OnboardingType.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,27 @@ extension OnboardingType {
1212
var step: Int {
1313
switch self {
1414
case .time: 1
15-
case .frequency: 2
16-
case .feeling: 3
15+
case .feeling: 2
16+
case .frequency: 3
1717
case .outdoor: 4
1818
}
1919
}
2020

2121
var mainTitle: String {
2222
switch self {
2323
case .time: "어떤 시간대를\n더 잘 보내고 싶나요?"
24+
case .feeling: "요즘 어떤 회복이 필요하신가요?"
2425
case .frequency: "최근 얼마나 자주\n바깥 바람을 쐬시나요?"
25-
case .feeling: "요즘 어떤 회복이\n필요하신가요?"
2626
case .outdoor: "작지만 의미 있는 변화를 위해,\n일주일에 몇 번 외출하고 싶으신가요?"
2727
}
2828
}
2929

3030
var subTitle: String? {
3131
switch self {
3232
case .time: nil
33-
case .frequency: nil
3433
case .feeling: "여러 개 선택할 수 있어요!"
35-
case .outdoor: "무리하지 않는 선에서, 나만의 외출 목표를 정해보세요."
34+
case .frequency: nil
35+
case .outdoor: "부담 없이 나만의 외출 목표를 정해보세요."
3636
}
3737
}
3838

@@ -43,17 +43,17 @@ extension OnboardingType {
4343
OnboardingChoiceType.eveningTime,
4444
OnboardingChoiceType.allTime]
4545

46-
case .frequency:
47-
return [OnboardingChoiceType.never,
48-
OnboardingChoiceType.rarely,
49-
OnboardingChoiceType.sometimes,
50-
OnboardingChoiceType.often]
51-
5246
case .feeling:
5347
return [OnboardingChoiceType.stability,
5448
OnboardingChoiceType.connection,
5549
OnboardingChoiceType.growth,
5650
OnboardingChoiceType.vitality]
51+
52+
case .frequency:
53+
return [OnboardingChoiceType.never,
54+
OnboardingChoiceType.rarely,
55+
OnboardingChoiceType.sometimes,
56+
OnboardingChoiceType.often]
5757

5858
case .outdoor:
5959
return [OnboardingChoiceType.once,

Projects/Presentation/Sources/Onboarding/View/Component/OnboardingChoiceButton.swift

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,24 @@
55
// Created by 최정인 on 7/9/25.
66
//
77

8+
import SnapKit
89
import UIKit
910

1011
final class OnboardingChoiceButton: UIButton {
11-
1212
private enum Layout {
1313
static let cornerRadius: CGFloat = 12
1414
static let horizontalMargin: CGFloat = 20
1515
static let stackViewSpacing: CGFloat = 2
16-
static let mainLabelHeight: CGFloat = 28
16+
static let mainLabelHeight: CGFloat = 24
1717
static let subLabelHeight: CGFloat = 20
18+
static let checkedIconTrailingSpacing: CGFloat = 20
19+
static let checkedIconSize: CGFloat = 28
1820
}
1921

2022
private let stackView = UIStackView()
2123
private let mainLabel = UILabel()
2224
private var subLabel: UILabel? = nil
25+
private let checkedIcon = UIImageView()
2326

2427
private var isChecked: Bool = false {
2528
didSet {
@@ -41,37 +44,35 @@ final class OnboardingChoiceButton: UIButton {
4144
}
4245

4346
private func configureAttribute() {
44-
backgroundColor = .white
45-
layer.borderWidth = 1
47+
backgroundColor = BitnagilColor.gray99
48+
layer.masksToBounds = true
4649
layer.cornerRadius = Layout.cornerRadius
4750

4851
stackView.axis = .vertical
4952
stackView.alignment = .leading
5053
stackView.spacing = Layout.stackViewSpacing
5154
stackView.isUserInteractionEnabled = false
5255

53-
guard let subTitle = onboardingChoice.subTitle else {
54-
mainLabel.text = onboardingChoice.mainTitle
55-
mainLabel.font = BitnagilFont(style: .body1, weight: .regular).font
56-
mainLabel.textColor = BitnagilColor.gray50
57-
return
58-
}
59-
6056
mainLabel.text = onboardingChoice.mainTitle
61-
mainLabel.font = BitnagilFont(style: .subtitle1, weight: .semiBold).font
57+
mainLabel.font = BitnagilFont(style: .body1, weight: .semiBold).font
6258
mainLabel.textColor = BitnagilColor.gray50
6359

64-
subLabel = UILabel()
65-
if let subLabel {
66-
subLabel.text = subTitle
67-
subLabel.font = BitnagilFont(style: .body2, weight: .regular).font
68-
subLabel.textColor = BitnagilColor.gray50
60+
if let subTitle = onboardingChoice.subTitle {
61+
subLabel = UILabel()
62+
subLabel?.text = subTitle
63+
subLabel?.font = BitnagilFont(style: .body2, weight: .medium).font
64+
subLabel?.textColor = BitnagilColor.gray50
6965
}
66+
67+
checkedIcon.image = BitnagilIcon.orangeCheckedCircleIcon
68+
checkedIcon.isHidden = true
7069
}
7170

7271
private func configureLayout() {
7372
addSubview(stackView)
73+
addSubview(checkedIcon)
7474
stackView.addArrangedSubview(mainLabel)
75+
7576
if let subLabel {
7677
stackView.addArrangedSubview(subLabel)
7778
mainLabel.snp.makeConstraints { make in
@@ -86,13 +87,19 @@ final class OnboardingChoiceButton: UIButton {
8687
make.leading.equalToSuperview().inset(Layout.horizontalMargin)
8788
make.centerY.equalToSuperview()
8889
}
90+
91+
checkedIcon.snp.makeConstraints { make in
92+
make.trailing.equalToSuperview().inset(Layout.checkedIconTrailingSpacing)
93+
make.size.equalTo(Layout.checkedIconSize)
94+
make.centerY.equalToSuperview()
95+
}
8996
}
9097

9198
private func updateButtonAttribute() {
92-
backgroundColor = isChecked ? BitnagilColor.lightBlue75 : .white
93-
layer.borderColor = (isChecked ? BitnagilColor.lightBlue200 : .white)?.cgColor
94-
mainLabel.textColor = isChecked ? BitnagilColor.navy500 : BitnagilColor.gray50
95-
subLabel?.textColor = isChecked ? BitnagilColor.navy500 : BitnagilColor.gray50
99+
backgroundColor = isChecked ? BitnagilColor.orange50 : BitnagilColor.gray99
100+
mainLabel.textColor = isChecked ? BitnagilColor.orange500 : BitnagilColor.gray50
101+
subLabel?.textColor = isChecked ? BitnagilColor.orange500 : BitnagilColor.gray50
102+
checkedIcon.isHidden = !isChecked
96103
}
97104

98105
func updateButtonState(isChecked: Bool) {
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//
2+
// OnboardingResultSummaryView.swift
3+
// Presentation
4+
//
5+
// Created by 최정인 on 8/11/25.
6+
//
7+
8+
import Domain
9+
import SnapKit
10+
import UIKit
11+
12+
final class OnboardingResultSummaryView: UIView {
13+
private enum Layout {
14+
static let orderImageViewSize: CGFloat = 24
15+
static let resultSummaryLabelLeadingSpacing: CGFloat = 14
16+
static let resultSummaryLabelHeight: CGFloat = 28
17+
}
18+
19+
private let orderImageView = UIImageView()
20+
private let resultSummaryLabel = UILabel()
21+
private let grayLine = UIImageView()
22+
23+
init(onboardingType: OnboardingType?, highlightText: String) {
24+
super.init(frame: .zero)
25+
configureAttribute(onboardingType: onboardingType, highlightText: highlightText)
26+
configureLayout()
27+
}
28+
29+
required init?(coder: NSCoder) {
30+
fatalError("init(coder:) has not been implemented")
31+
}
32+
33+
private func configureAttribute(onboardingType: OnboardingType?, highlightText: String) {
34+
switch onboardingType {
35+
case .time:
36+
orderImageView.image = BitnagilIcon.circleOneIcon
37+
case .feeling:
38+
orderImageView.image = BitnagilIcon.circleTwoIcon
39+
case .outdoor:
40+
orderImageView.image = BitnagilIcon.circleThreeIcon
41+
default:
42+
orderImageView.isHidden = true
43+
}
44+
45+
let baseText = baseText(onboardingType: onboardingType, highlightText: highlightText)
46+
resultSummaryLabel.attributedText = NSAttributedString.highlighted(text: baseText, highlightText: highlightText)
47+
48+
grayLine.image = BitnagilGraphic.onboardingGrayLine
49+
}
50+
51+
private func configureLayout() {
52+
addSubview(orderImageView)
53+
addSubview(resultSummaryLabel)
54+
addSubview(grayLine)
55+
56+
orderImageView.snp.makeConstraints { make in
57+
make.leading.equalToSuperview()
58+
make.centerY.equalToSuperview()
59+
make.size.equalTo(Layout.orderImageViewSize)
60+
}
61+
62+
resultSummaryLabel.snp.makeConstraints { make in
63+
make.top.equalToSuperview()
64+
make.leading.equalTo(orderImageView.snp.trailing).offset(Layout.resultSummaryLabelLeadingSpacing)
65+
make.height.equalTo(Layout.resultSummaryLabelHeight)
66+
}
67+
68+
grayLine.snp.makeConstraints { make in
69+
make.leading.equalTo(resultSummaryLabel)
70+
make.trailing.equalToSuperview()
71+
make.bottom.equalToSuperview()
72+
}
73+
}
74+
75+
private func baseText(onboardingType: OnboardingType?, highlightText: String) -> String {
76+
switch onboardingType {
77+
case .time:
78+
return timeResultBaseText(highlightText: highlightText)
79+
80+
case .feeling:
81+
if highlightText.filter({ $0 == "," }).count >= 2 {
82+
return "\(highlightText)"
83+
} else {
84+
return "\(highlightText)을 원하는 중이에요"
85+
}
86+
87+
case .outdoor:
88+
return "\(highlightText)을 목표로 해볼게요!"
89+
90+
default:
91+
return "원하는 중이에요"
92+
}
93+
}
94+
95+
private func timeResultBaseText(highlightText: String) -> String {
96+
switch highlightText {
97+
case "아침루틴":
98+
return "아침루틴을 만들고 싶고"
99+
case "저녁루틴":
100+
return "저녁루틴을 만들고 싶고"
101+
case "전체루틴":
102+
return "전체루틴을 회복하고 싶고"
103+
default:
104+
return ""
105+
}
106+
}
107+
}

0 commit comments

Comments
 (0)