Skip to content

Commit a261e70

Browse files
committed
Feat: FloatingButton, FloatingMenuView UI 구현
1 parent adb750e commit a261e70

6 files changed

Lines changed: 167 additions & 0 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "add_routine_icon.png",
5+
"idiom" : "universal",
6+
"scale" : "1x"
7+
},
8+
{
9+
"filename" : "add_routine_icon@2x.png",
10+
"idiom" : "universal",
11+
"scale" : "2x"
12+
},
13+
{
14+
"filename" : "add_routine_icon@3x.png",
15+
"idiom" : "universal",
16+
"scale" : "3x"
17+
}
18+
],
19+
"info" : {
20+
"author" : "xcode",
21+
"version" : 1
22+
}
23+
}
630 Bytes
Loading
1.02 KB
Loading
1.41 KB
Loading
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//
2+
// FloatingButton.swift
3+
// Presentation
4+
//
5+
// Created by 최정인 on 7/28/25.
6+
//
7+
8+
import SnapKit
9+
import UIKit
10+
11+
final class FloatingButton: UIButton {
12+
13+
private enum Layout {
14+
static let floatingButtonHeight: CGFloat = 52
15+
static let plusIconSize: CGFloat = 15
16+
}
17+
18+
private let plusIcon = UIImageView()
19+
private var isToggled: Bool = false
20+
21+
init() {
22+
super.init(frame: .zero)
23+
configureAttribute()
24+
configureLayout()
25+
}
26+
27+
required init?(coder: NSCoder) {
28+
fatalError("init(coder:) has not been implemented")
29+
}
30+
31+
private func configureAttribute() {
32+
backgroundColor = BitnagilColor.navy500
33+
layer.masksToBounds = true
34+
layer.cornerRadius = Layout.floatingButtonHeight / 2
35+
36+
plusIcon.image = BitnagilIcon.plusIcon
37+
plusIcon.tintColor = BitnagilColor.gray99
38+
}
39+
40+
private func configureLayout() {
41+
addSubview(plusIcon)
42+
43+
plusIcon.snp.makeConstraints { make in
44+
make.size.equalTo(Layout.plusIconSize)
45+
make.center.equalToSuperview()
46+
}
47+
}
48+
49+
func toggle() {
50+
isToggled.toggle()
51+
52+
let angle: CGFloat = isToggled ? -.pi / 4 : 0
53+
UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseInOut]) {
54+
self.plusIcon.transform = CGAffineTransform(rotationAngle: angle)
55+
}
56+
}
57+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//
2+
// FloatingMenu.swift
3+
// Presentation
4+
//
5+
// Created by 최정인 on 7/28/25.
6+
//
7+
8+
import SnapKit
9+
import UIKit
10+
11+
protocol FloatingMenuViewDelegate: AnyObject {
12+
func floatingMenuDidTapRegisterRoutineButton(_ sender: FloatingMenuView)
13+
}
14+
15+
final class FloatingMenuView: UIView {
16+
17+
private enum Layout {
18+
static let registerRoutineButtonHeight: CGFloat = 22
19+
static let registerRoutineButtonWidth: CGFloat = 98
20+
static let registerRoutineIconSize: CGFloat = 24
21+
static let registerRoutineLabelLeadingSpacing: CGFloat = 16
22+
static let registerRoutineLabelHeight: CGFloat = 20
23+
}
24+
25+
private let containerView = UIView()
26+
private let registerRoutineButton = UIButton()
27+
private let registerRoutineIconView = UIImageView()
28+
private let registerRoutineLabel = UILabel()
29+
weak var delegate: FloatingMenuViewDelegate?
30+
31+
init() {
32+
super.init(frame: .zero)
33+
configureAttribute()
34+
configureLayout()
35+
}
36+
37+
required init?(coder: NSCoder) {
38+
fatalError("init(coder:) has not been implemented")
39+
}
40+
41+
private func configureAttribute() {
42+
containerView.backgroundColor = .white
43+
containerView.layer.masksToBounds = true
44+
containerView.layer.cornerRadius = 12
45+
46+
registerRoutineIconView.image = BitnagilIcon.addRoutineIcon
47+
48+
registerRoutineLabel.text = "루틴 등록"
49+
registerRoutineLabel.font = BitnagilFont(style: .subtitle1, weight: .medium).font
50+
registerRoutineLabel.textColor = BitnagilColor.navy500
51+
52+
registerRoutineButton.addAction(UIAction { [weak self] _ in
53+
guard let self else { return }
54+
self.delegate?.floatingMenuDidTapRegisterRoutineButton(self)
55+
}, for: .touchUpInside)
56+
}
57+
58+
private func configureLayout() {
59+
addSubview(containerView)
60+
containerView.addSubview(registerRoutineButton)
61+
[registerRoutineIconView, registerRoutineLabel].forEach {
62+
registerRoutineButton.addSubview($0)
63+
}
64+
65+
containerView.snp.makeConstraints { make in
66+
make.edges.equalToSuperview()
67+
}
68+
69+
registerRoutineButton.snp.makeConstraints { make in
70+
make.center.equalToSuperview()
71+
make.height.equalTo(Layout.registerRoutineButtonHeight)
72+
make.width.equalTo(Layout.registerRoutineButtonWidth)
73+
}
74+
75+
registerRoutineIconView.snp.makeConstraints { make in
76+
make.centerY.equalToSuperview()
77+
make.leading.equalToSuperview()
78+
make.size.equalTo(Layout.registerRoutineIconSize)
79+
}
80+
81+
registerRoutineLabel.snp.makeConstraints { make in
82+
make.leading.equalTo(registerRoutineIconView.snp.trailing).offset(Layout.registerRoutineLabelLeadingSpacing)
83+
make.centerY.equalToSuperview()
84+
make.height.equalTo(Layout.registerRoutineLabelHeight)
85+
}
86+
}
87+
}

0 commit comments

Comments
 (0)