Skip to content

Commit b77aed0

Browse files
authored
[NL-57]: 번호 상세 화면 UI 구현 (#30)
* [NL-57]: 로또 번호 추천 셀 구현 * [NL-57]: AI 분석 결과 셀 구현 * [NL-57]: 추천 설명 셀 구현 * [NL-57]: 제외 번호 셀 구현 * [NL-57]: 번호 상세 화면 UI 구현
1 parent 68603af commit b77aed0

10 files changed

+939
-1
lines changed

DesignSystem/DesignSystem/Sources/Ball.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ public final class Ball: UIView {
1818
$0.color = STColors.white.color
1919
}
2020
}
21-
private lazy var backgroundImageView = UIImageView()
21+
private lazy var backgroundImageView = UIImageView().then {
22+
$0.contentMode = .scaleAspectFit
23+
}
2224
public var number: String? { // TODO: 타입 확인
2325
get { numberLabel.text }
2426
set {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//
2+
// DashedLineView.swift
3+
// DesignSystem
4+
//
5+
// Created by ttozzi on 8/7/25.
6+
//
7+
8+
import UIKit
9+
10+
public final class DashedLineView: UIView {
11+
12+
private let color: UIColor
13+
14+
public init(color: UIColor) {
15+
self.color = color
16+
super.init(frame: .zero)
17+
}
18+
19+
required init?(coder: NSCoder) {
20+
fatalError("init(coder:) has not been implemented")
21+
}
22+
23+
public override func layoutSubviews() {
24+
super.layoutSubviews()
25+
layer.sublayers?.forEach {
26+
$0.removeFromSuperlayer()
27+
}
28+
29+
let shapeLayer = CAShapeLayer()
30+
shapeLayer.strokeColor = color.cgColor
31+
shapeLayer.lineWidth = 1
32+
shapeLayer.lineDashPattern = [4, 4]
33+
34+
let path = CGMutablePath()
35+
path.addLines(between: [CGPoint(x: 0, y: bounds.midY),
36+
CGPoint(x: bounds.width, y: bounds.midY)])
37+
shapeLayer.path = path
38+
layer.addSublayer(shapeLayer)
39+
}
40+
}
41+
42+
@available(iOS 17.0, *)
43+
#Preview {
44+
DashedLineView(color: STColors.gray6.color)
45+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//
2+
// AIAnalysisResultCollectionViewCell.swift
3+
// Home
4+
//
5+
// Created by ttozzi on 8/8/25.
6+
//
7+
8+
import UIKit
9+
import Extension
10+
import DesignSystem
11+
12+
struct AIAnalysisResultCollectionViewCellModel: RecommendationDetailCellModel {
13+
let description: NSAttributedString // TODO: 확인 필요
14+
let items: [NumberRecommendationItem]
15+
}
16+
17+
final class AIAnalysisResultCollectionViewCell: UICollectionViewCell {
18+
19+
private lazy var headerTitleLabel = UILabel().then {
20+
$0.style = Typography.Body_14_B
21+
$0.textColor = STColors.gray2.color
22+
$0.styledText = "AI 분석 결과, 이 번호가 뽑힌 이유는..."
23+
}
24+
private lazy var contentStackView = UIStackView().then {
25+
$0.spacing = 16
26+
$0.axis = .vertical
27+
$0.alignment = .fill
28+
$0.backgroundColor = STColors.white.color
29+
$0.layer.cornerRadius = 12
30+
$0.clipsToBounds = true
31+
$0.isLayoutMarginsRelativeArrangement = true
32+
$0.layoutMargins = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
33+
}
34+
private lazy var descriptionLabel = UILabel().then {
35+
$0.style = Typography.Body_14_B
36+
$0.textColor = STColors.gray1.color
37+
$0.numberOfLines = .zero
38+
}
39+
private lazy var recommendationStackView = UIStackView().then {
40+
$0.spacing = 8
41+
$0.axis = .vertical
42+
}
43+
44+
override init(frame: CGRect) {
45+
super.init(frame: frame)
46+
setupUI()
47+
}
48+
49+
required init?(coder: NSCoder) {
50+
fatalError("init(coder:) has not been implemented")
51+
}
52+
53+
private func setupUI() {
54+
contentView.backgroundColor = .clear
55+
56+
contentView.addSubview(headerTitleLabel)
57+
headerTitleLabel.snp.makeConstraints { make in
58+
make.top.leading.trailing.equalToSuperview()
59+
}
60+
61+
contentView.addSubview(contentStackView)
62+
contentStackView.snp.makeConstraints { make in
63+
make.top.equalTo(headerTitleLabel.snp.bottom).offset(8)
64+
make.leading.trailing.bottom.equalToSuperview()
65+
}
66+
67+
contentStackView.addArrangedSubview(descriptionLabel)
68+
69+
contentStackView.addArrangedSubview(recommendationStackView)
70+
}
71+
72+
func update(with model: AIAnalysisResultCollectionViewCellModel) {
73+
descriptionLabel.attributedText = model.description
74+
75+
recommendationStackView.arrangedSubviews.forEach {
76+
$0.removeFromSuperview()
77+
}
78+
model.items.forEach { item in
79+
let recommendationView = NumberRecommendationItemView()
80+
recommendationView.update(with: item)
81+
recommendationStackView.addArrangedSubview(recommendationView)
82+
}
83+
}
84+
}
85+
86+
@available(iOS 17.0, *)
87+
#Preview {
88+
let description = "콩떡님은 화(火) 기운이 강하여\n‘지존 만수르’ 예요"
89+
let attributedString = NSMutableAttributedString(
90+
string: description,
91+
attributes: Typography.Body_14_B.color(STColors.gray1.color).attributes
92+
)
93+
if let range = (description as NSString).range(of: "‘지존 만수르’") as NSRange? {
94+
attributedString.addAttributes(Typography.Body_14_B.color(STColors.primary2.color).attributes, range: range)
95+
}
96+
let cellModel = AIAnalysisResultCollectionViewCellModel(
97+
description: attributedString,
98+
items: [
99+
.init(title: "화(火) 기운과 잘 맞는 숫자", numbers: [9, 11]),
100+
.init(title: "재물운 좋을 때 잘 나오는 숫자", numbers: [24, 33]),
101+
.init(title: "최근 자주 나온 번호", numbers: [18, 42])
102+
]
103+
)
104+
let cell = AIAnalysisResultCollectionViewCell()
105+
cell.update(with: cellModel)
106+
cell.snp.makeConstraints { make in
107+
make.height.equalTo(329)
108+
}
109+
return cell
110+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//
2+
// AvoidNumberCollectionViewCell.swift
3+
// Home
4+
//
5+
// Created by ttozzi on 8/9/25.
6+
//
7+
8+
import UIKit
9+
import Extension
10+
import DesignSystem
11+
12+
struct AvoidNumberCollectionViewCellModel: RecommendationDetailCellModel {
13+
let items: [NumberCardItem]
14+
}
15+
16+
final class AvoidNumberCollectionViewCell: UICollectionViewCell {
17+
18+
private lazy var headerTitleLabel = UILabel().then {
19+
$0.style = Typography.Body_14_B
20+
$0.textColor = STColors.gray2.color
21+
$0.styledText = "제외 번호도 알려드릴게요"
22+
}
23+
private lazy var contentStackView = UIStackView().then {
24+
$0.spacing = 20
25+
$0.axis = .vertical
26+
$0.alignment = .fill
27+
$0.backgroundColor = STColors.white.color
28+
$0.layer.cornerRadius = 12
29+
$0.clipsToBounds = true
30+
$0.isLayoutMarginsRelativeArrangement = true
31+
$0.layoutMargins = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
32+
}
33+
private lazy var descriptionLabel = UILabel().then {
34+
$0.style = Typography.Body_14_B.lineHeightMultiple(1.2)
35+
$0.textColor = STColors.gray2.color
36+
$0.numberOfLines = .zero
37+
$0.styledText = "혹시 다른 번호도 고민 중이라면,\n이 번호들은 피해주세요!"
38+
}
39+
private lazy var numberCardStackView = UIStackView().then {
40+
$0.spacing = 6
41+
$0.axis = .horizontal
42+
$0.distribution = .fillEqually
43+
}
44+
45+
override init(frame: CGRect) {
46+
super.init(frame: frame)
47+
setupUI()
48+
}
49+
50+
required init?(coder: NSCoder) {
51+
fatalError("init(coder:) has not been implemented")
52+
}
53+
54+
private func setupUI() {
55+
contentView.backgroundColor = .clear
56+
57+
contentView.addSubview(headerTitleLabel)
58+
headerTitleLabel.snp.makeConstraints { make in
59+
make.top.leading.trailing.equalToSuperview()
60+
}
61+
62+
contentView.addSubview(contentStackView)
63+
contentStackView.snp.makeConstraints { make in
64+
make.top.equalTo(headerTitleLabel.snp.bottom).offset(8)
65+
make.leading.trailing.bottom.equalToSuperview()
66+
}
67+
68+
contentStackView.addArrangedSubview(descriptionLabel)
69+
descriptionLabel.snp.makeConstraints { make in
70+
make.height.equalTo(42)
71+
}
72+
73+
contentStackView.addArrangedSubview(numberCardStackView)
74+
}
75+
76+
func update(with model: AvoidNumberCollectionViewCellModel) {
77+
numberCardStackView.arrangedSubviews.forEach {
78+
$0.removeFromSuperview()
79+
}
80+
model.items.forEach { item in
81+
let numberCardView = NumberCardItemView()
82+
numberCardView.update(with: item)
83+
numberCardStackView.addArrangedSubview(numberCardView)
84+
}
85+
}
86+
}
87+
88+
@available(iOS 17.0, *)
89+
#Preview {
90+
let cellModel = AvoidNumberCollectionViewCellModel(
91+
items: [
92+
.init(title: "수(水) 기운과\n상충하는 숫자", numbers: [9, 11, 18]),
93+
.init(title: "최근 100회 동안\n거의 안 나온 숫자", numbers: [24, 33])
94+
]
95+
)
96+
let cell = AvoidNumberCollectionViewCell()
97+
cell.update(with: cellModel)
98+
cell.snp.makeConstraints { make in
99+
make.height.equalTo(253)
100+
}
101+
return cell
102+
}
103+
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//
2+
// DescriptionListCollectionViewCell.swift
3+
// Home
4+
//
5+
// Created by ttozzi on 8/9/25.
6+
//
7+
8+
import UIKit
9+
import Extension
10+
import DesignSystem
11+
12+
struct DescriptionListCollectionViewCellModel: RecommendationDetailCellModel {
13+
let descriptions: [String]
14+
}
15+
16+
final class DescriptionListCollectionViewCell: UICollectionViewCell {
17+
18+
private lazy var headerTitleLabel = UILabel().then {
19+
$0.style = Typography.Body_14_B
20+
$0.textColor = STColors.gray2.color
21+
$0.styledText = "그럼 이 번호, 믿어도 될까요?"
22+
}
23+
private lazy var contentStackView = UIStackView().then {
24+
$0.spacing = .zero
25+
$0.axis = .vertical
26+
$0.alignment = .fill
27+
$0.backgroundColor = STColors.white.color
28+
$0.layer.cornerRadius = 12
29+
$0.clipsToBounds = true
30+
$0.isLayoutMarginsRelativeArrangement = true
31+
$0.layoutMargins = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
32+
}
33+
34+
override init(frame: CGRect) {
35+
super.init(frame: frame)
36+
setupUI()
37+
}
38+
39+
required init?(coder: NSCoder) {
40+
fatalError("init(coder:) has not been implemented")
41+
}
42+
43+
private func setupUI() {
44+
contentView.backgroundColor = .clear
45+
46+
contentView.addSubview(headerTitleLabel)
47+
headerTitleLabel.snp.makeConstraints { make in
48+
make.top.leading.trailing.equalToSuperview()
49+
}
50+
51+
contentView.addSubview(contentStackView)
52+
contentStackView.snp.makeConstraints { make in
53+
make.top.equalTo(headerTitleLabel.snp.bottom).offset(8)
54+
make.leading.trailing.bottom.equalToSuperview()
55+
}
56+
}
57+
58+
func update(with model: DescriptionListCollectionViewCellModel) {
59+
contentStackView.arrangedSubviews.forEach {
60+
$0.removeFromSuperview()
61+
}
62+
model.descriptions.forEach { description in
63+
let descriptionView = makeDescriptionView(text: description)
64+
contentStackView.addArrangedSubview(descriptionView)
65+
}
66+
}
67+
68+
private func makeDescriptionView(text: String) -> UIStackView {
69+
let descriptionStackView = UIStackView().then {
70+
$0.spacing = 6
71+
$0.axis = .horizontal
72+
$0.alignment = .center
73+
}
74+
descriptionStackView.snp.makeConstraints { make in
75+
make.height.equalTo(33)
76+
}
77+
78+
let checkImage = STImages.check.image.withTintColor(STColors.primary2.color)
79+
let imageView = UIImageView(image: checkImage).then {
80+
$0.contentMode = .scaleAspectFit
81+
}
82+
descriptionStackView.addArrangedSubview(imageView)
83+
84+
let descriptionLabel = UILabel().then {
85+
$0.style = Typography.Body_14_SB
86+
$0.textColor = STColors.gray3.color
87+
$0.setContentHuggingPriority(UILayoutPriority(.zero), for: .horizontal)
88+
}
89+
descriptionLabel.styledText = text
90+
descriptionStackView.addArrangedSubview(descriptionLabel)
91+
92+
return descriptionStackView
93+
}
94+
}
95+
96+
@available(iOS 17.0, *)
97+
#Preview {
98+
let cellModel = DescriptionListCollectionViewCellModel(
99+
descriptions: [
100+
"요즘 많이 나오는 번호가 들어 있어요",
101+
"끝자리가 같은 숫자가 1쌍 있어요",
102+
"연속 숫자 3개 이상 없이 안정적인 조합이에요",
103+
"홀수랑 짝수가 고르게 섞였어요"
104+
]
105+
)
106+
let cell = DescriptionListCollectionViewCell()
107+
cell.update(with: cellModel)
108+
cell.snp.makeConstraints { make in
109+
make.height.equalTo(201)
110+
}
111+
return cell
112+
}
113+

0 commit comments

Comments
 (0)