Skip to content

Commit c4b7b29

Browse files
authored
Merge pull request #68 from YAPP-Github/BOOK-114-feature/#64
feat: 버튼 컴포넌트 개발
2 parents 8989ff9 + d828e9f commit c4b7b29

18 files changed

Lines changed: 1487 additions & 4 deletions
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleDevelopmentRegion</key>
6+
<string>en</string>
7+
<key>CFBundleExecutable</key>
8+
<string>$(EXECUTABLE_NAME)</string>
9+
<key>CFBundleIdentifier</key>
10+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11+
<key>CFBundleInfoDictionaryVersion</key>
12+
<string>6.0</string>
13+
<key>CFBundleName</key>
14+
<string>$(PRODUCT_NAME)</string>
15+
<key>CFBundlePackageType</key>
16+
<string>APPL</string>
17+
<key>CFBundleShortVersionString</key>
18+
<string>1.0</string>
19+
<key>CFBundleVersion</key>
20+
<string>1</string>
21+
22+
<!-- 🖼 LaunchScreen -->
23+
<key>UILaunchStoryboardName</key>
24+
<string>LaunchScreen</string>
25+
26+
<!-- 📱 iPhone만 지원 -->
27+
<key>UIDeviceFamily</key>
28+
<array>
29+
<integer>1</integer>
30+
</array>
31+
32+
<!-- 🎬 SceneDelegate 연결 -->
33+
<key>UIApplicationSceneManifest</key>
34+
<dict>
35+
<key>UIApplicationSupportsMultipleScenes</key>
36+
<false/>
37+
<key>UISceneConfigurations</key>
38+
<dict>
39+
<key>UIWindowSceneSessionRoleApplication</key>
40+
<array>
41+
<dict>
42+
<key>UISceneClassName</key>
43+
<string>UIWindowScene</string>
44+
<key>UISceneConfigurationName</key>
45+
<string>Default Configuration</string>
46+
<key>UISceneDelegateClassName</key>
47+
<string>BKDesignPreviewApp.SceneDelegate</string>
48+
</dict>
49+
</array>
50+
</dict>
51+
</dict>
52+
53+
<!-- 👟 인터페이스 방향 -->
54+
<key>UISupportedInterfaceOrientations</key>
55+
<array>
56+
<string>UIInterfaceOrientationPortrait</string>
57+
</array>
58+
59+
<!-- 🔠 폰트 등록 -->
60+
<key>UIAppFonts</key>
61+
<array>
62+
<string>Pretendard-Regular.otf</string>
63+
<string>Pretendard-Medium.otf</string>
64+
<string>Pretendard-SemiBold.otf</string>
65+
<string>Pretendard-Bold.otf</string>
66+
</array>
67+
</dict>
68+
</plist>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17150" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
3+
<dependencies>
4+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17122"/>
5+
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
6+
<capability name="System colors in document resources" minToolsVersion="11.0"/>
7+
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
8+
</dependencies>
9+
<scenes>
10+
<!--View Controller-->
11+
<scene sceneID="s0d-6b-0kx">
12+
<objects>
13+
<viewController id="Y6W-OH-hqX" sceneMemberID="viewController">
14+
<view key="view" contentMode="scaleToFill" id="5EZ-qb-Rvc">
15+
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
16+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
17+
<viewLayoutGuide key="safeArea" id="vDu-zF-Fre"/>
18+
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
19+
</view>
20+
</viewController>
21+
<placeholder placeholderIdentifier="IBFirstResponder" id="Ief-a0-LHa" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
22+
</objects>
23+
</scene>
24+
</scenes>
25+
<resources>
26+
<systemColor name="systemBackgroundColor">
27+
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
28+
</systemColor>
29+
</resources>
30+
</document>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright © 2025 Booket. All rights reserved
2+
3+
import UIKit
4+
5+
@main
6+
class AppDelegate: UIResponder, UIApplicationDelegate {
7+
func application(
8+
_ application: UIApplication,
9+
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
10+
) -> Bool {
11+
return true
12+
}
13+
14+
// MARK: UISceneSession Lifecycle
15+
16+
func application(
17+
_ application: UIApplication,
18+
configurationForConnecting connectingSceneSession: UISceneSession,
19+
options: UIScene.ConnectionOptions
20+
) -> UISceneConfiguration {
21+
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
22+
}
23+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright © 2025 Booket. All rights reserved
2+
3+
import BKDesign
4+
import UIKit
5+
6+
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
7+
var window: UIWindow?
8+
9+
func scene(
10+
_ scene: UIScene,
11+
willConnectTo session: UISceneSession,
12+
options connectionOptions: UIScene.ConnectionOptions
13+
) {
14+
guard let windowScene = scene as? UIWindowScene else { return }
15+
16+
let window = UIWindow(windowScene: windowScene)
17+
let viewController = BKButtonTestViewController()
18+
// let viewController = BKButtonGroupDemoViewController()
19+
window.rootViewController = UINavigationController(rootViewController: viewController)
20+
window.makeKeyAndVisible()
21+
22+
self.window = window
23+
}
24+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright © 2025 Booket. All rights reserved
2+
3+
import BKDesign
4+
import SnapKit
5+
import UIKit
6+
7+
public final class BKButtonGroupDemoViewController: UIViewController {
8+
9+
private let scrollView = UIScrollView()
10+
private let containerView = UIStackView()
11+
12+
public override func viewDidLoad() {
13+
super.viewDidLoad()
14+
view.backgroundColor = .white
15+
setupScrollView()
16+
setupDemoGroups()
17+
}
18+
19+
private func setupScrollView() {
20+
view.addSubview(scrollView)
21+
scrollView.snp.makeConstraints { $0.edges.equalToSuperview() }
22+
23+
scrollView.addSubview(containerView)
24+
containerView.axis = .vertical
25+
containerView.spacing = 16
26+
containerView.alignment = .fill
27+
containerView.distribution = .fill
28+
containerView.snp.makeConstraints {
29+
$0.edges.equalToSuperview().inset(20)
30+
$0.width.equalToSuperview().inset(20)
31+
}
32+
}
33+
34+
private func setupDemoGroups() {
35+
addSection(title: "Custom 구성 (직접 만든 그룹)")
36+
containerView.addArrangedSubview(BKButtonGroup(
37+
buttons: [
38+
makeButton(title: "[Custom] S", size: .small),
39+
makeButton(title: "[Custom] M", size: .medium),
40+
makeButton(title: "[Custom] L", size: .large)
41+
],
42+
layout: .horizontal
43+
))
44+
45+
addDivider()
46+
47+
addSection(title: "TwoButtonGroup")
48+
containerView.addArrangedSubview(BKButtonGroup.twoButtonGroup(
49+
leftTitle: "취소", rightTitle: "확인",
50+
leftAction: { print("취소 tapped") },
51+
rightAction: { print("확인 tapped") }
52+
))
53+
54+
addDivider()
55+
56+
addSection(title: "ThreeButtonGroup")
57+
containerView.addArrangedSubview(BKButtonGroup.threeButtonGroup(
58+
leftTitle: "이전", centerTitle: "중간", rightTitle: "다음",
59+
leftAction: { print("이전 tapped") },
60+
centerAction: { print("중간 tapped") },
61+
rightAction: { print("다음 tapped") }
62+
))
63+
64+
addDivider()
65+
66+
addSection(title: "SingleFullButton")
67+
containerView.addArrangedSubview(BKButtonGroup.singleFullButton(
68+
title: "계속하기",
69+
action: { print("계속하기 tapped") }
70+
))
71+
72+
addDivider()
73+
74+
addSection(title: "VerticalGroup (Rounded 버튼)")
75+
containerView.addArrangedSubview(BKButtonGroup.verticalButtonGroup(buttons: [
76+
makeButton(title: "둥글1", size: .rounded),
77+
makeButton(title: "둥글2", size: .rounded)
78+
]))
79+
}
80+
81+
private func makeButton(title: String, size: BKButtonSize) -> BKButton {
82+
let button = BKButton.primary(title: title, size: size)
83+
button.addAction(UIAction { _ in
84+
print("Tapped: \(title)")
85+
}, for: .touchUpInside)
86+
return button
87+
}
88+
89+
private func addSection(title: String) {
90+
let label = UILabel()
91+
label.text = title
92+
label.font = UIFont.systemFont(ofSize: 14, weight: .bold)
93+
label.textColor = .darkGray
94+
containerView.addArrangedSubview(label)
95+
}
96+
97+
private func addDivider() {
98+
let divider = UIView()
99+
divider.backgroundColor = UIColor.lightGray.withAlphaComponent(0.4)
100+
divider.snp.makeConstraints { make in
101+
make.height.equalTo(1)
102+
}
103+
containerView.addArrangedSubview(divider)
104+
}
105+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Copyright © 2025 Booket. All rights reserved
2+
3+
import BKDesign
4+
import SnapKit
5+
import UIKit
6+
7+
public final class BKButtonTestViewController: UIViewController {
8+
9+
// MARK: - UI Components
10+
private let scrollView = UIScrollView()
11+
private let containerView = UIStackView()
12+
13+
// MARK: - Lifecycle
14+
15+
public override func viewDidLoad() {
16+
super.viewDidLoad()
17+
view.backgroundColor = .white
18+
setupScrollView()
19+
// setupIndependentButtons()
20+
setupStackView()
21+
}
22+
23+
public override func viewWillAppear(_ animated: Bool) {
24+
super.viewWillAppear(animated)
25+
setupAllTestButtons()
26+
}
27+
28+
// MARK: - Setup Scroll & Stack
29+
30+
private func setupScrollView() {
31+
view.addSubview(scrollView)
32+
scrollView.snp.makeConstraints { $0.edges.equalToSuperview() }
33+
34+
scrollView.addSubview(containerView)
35+
containerView.snp.makeConstraints {
36+
$0.edges.equalToSuperview().inset(20)
37+
$0.width.equalToSuperview().inset(20)
38+
}
39+
}
40+
41+
private func setupStackView() {
42+
containerView.axis = .vertical
43+
containerView.spacing = 16
44+
containerView.alignment = .fill
45+
containerView.distribution = .equalSpacing
46+
}
47+
48+
// MARK: - Setup Buttons by Size
49+
50+
private func setupAllTestButtons() {
51+
setupButtons(for: .large)
52+
setupButtons(for: .medium)
53+
setupButtons(for: .small)
54+
setupButtons(for: .rounded)
55+
}
56+
57+
private func setupButtons(for size: BKButtonSize) {
58+
addButton("Primary", style: .primary, size: size)
59+
addButton("Secondary", style: .secondary, size: size)
60+
addButton("Tertiary", style: .tertiary, size: size)
61+
62+
addIconButton("Apple 로그인", style: .primary, size: size, left: .appleLogo)
63+
addIconButton("카카오 로그인", style: .primary, size: size, right: .kakaoLogo)
64+
addIconButton("양쪽 아이콘", style: .primary, size: size, left: .appleLogo, right: .kakaoLogo)
65+
}
66+
67+
// MARK: - Button Builders
68+
69+
private func addButton(_ title: String, style: BKButtonStyle, size: BKButtonSize) {
70+
let button = BKButton(style: style, size: size)
71+
button.title = "[\(size.label)] \(title)"
72+
containerView.addArrangedSubview(button)
73+
}
74+
75+
private func addIconButton(
76+
_ title: String,
77+
style: BKButtonStyle,
78+
size: BKButtonSize,
79+
left: BKIcon? = nil,
80+
right: BKIcon? = nil
81+
) {
82+
let button = BKButton(style: style, size: size)
83+
button.title = "[\(size.label)] \(title)"
84+
button.leftIcon = left?.image
85+
button.rightIcon = right?.image
86+
containerView.addArrangedSubview(button)
87+
}
88+
89+
private func setupIndependentButtons() {
90+
let sampleView = UIView()
91+
scrollView.addSubview(sampleView)
92+
sampleView.snp.makeConstraints {
93+
$0.top.equalTo(containerView.snp.bottom).offset(40)
94+
$0.centerX.equalToSuperview()
95+
$0.bottom.lessThanOrEqualToSuperview()
96+
}
97+
98+
let buttons: [BKButton] = [
99+
.primary(title: "[Free] Apple 로그인", size: .large),
100+
.secondary(title: "[Free] Secondary", size: .large),
101+
.tertiary(title: "[Free] Tertiary", size: .large),
102+
.primary(title: "[Free] Apple 로그인", size: .medium),
103+
.secondary(title: "[Free] Secondary", size: .small),
104+
.tertiary(title: "[Free] Tertiary", size: .rounded)
105+
]
106+
107+
buttons[0].leftIcon = BKIcon.appleLogo.image
108+
buttons[2].rightIcon = BKIcon.kakaoLogo.image
109+
110+
var last: UIView?
111+
for button in buttons {
112+
sampleView.addSubview(button)
113+
button.snp.makeConstraints {
114+
$0.centerX.equalToSuperview()
115+
$0.top.equalTo(last?.snp.bottom ?? sampleView.snp.top).offset(16)
116+
}
117+
last = button
118+
}
119+
}
120+
121+
}
122+
123+
124+
public extension BKButtonSize {
125+
var label: String {
126+
switch self {
127+
case .large: return "Large"
128+
case .medium: return "Medium"
129+
case .small: return "Small"
130+
case .rounded: return "Rounded"
131+
}
132+
}
133+
}

0 commit comments

Comments
 (0)