From b4a96340ffb505e1a57427235d598c6ca9c53e84 Mon Sep 17 00:00:00 2001 From: hyeonju Date: Fri, 3 Jan 2025 22:14:34 +0900 Subject: [PATCH 1/6] =?UTF-8?q?[#20]=20HandySearchBar=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Atom/HandySearchBarViewController.swift | 58 ++++++++ Handy/Handy-Storybook/SceneDelegate.swift | 2 +- Handy/Handy.xcodeproj/project.pbxproj | 8 ++ Handy/Handy/Source/Atom/HandySearchBar.swift | 136 ++++++++++++++++++ .../Contents.json | 21 +++ .../size=36, filled=true.svg | 3 + .../Contents.json | 21 +++ .../size=36, filled=true.svg | 6 + .../ic_search_line.imageset/Contents.json | 12 ++ .../size=36, filled=false.svg | 3 + Handy/Handy/Source/Foundation/HandyIcon.swift | 4 + 11 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift create mode 100644 Handy/Handy/Source/Atom/HandySearchBar.swift create mode 100644 Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_ArrowsChevronLeft_filled.imageset/Contents.json create mode 100644 Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_ArrowsChevronLeft_filled.imageset/size=36, filled=true.svg create mode 100644 Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_filterBar_filled.imageset/Contents.json create mode 100644 Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_filterBar_filled.imageset/size=36, filled=true.svg create mode 100644 Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_search_line.imageset/Contents.json create mode 100644 Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_search_line.imageset/size=36, filled=false.svg diff --git a/Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift b/Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift new file mode 100644 index 0000000..6561f7f --- /dev/null +++ b/Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift @@ -0,0 +1,58 @@ +// +// HandySearchBarViewController.swift +// Handy-Storybook +// +// Created by 성현주 on 1/1/25. +// + +import Handy +import UIKit + +final class HandySearchBarViewController: BaseViewController { + + private let searchBar1: HandySearchBar = { + let uiSwitch = HandySearchBar() + return uiSwitch + }() + private let searchBar2: HandySearchBar = { + let uiSwitch = HandySearchBar() + uiSwitch.rightIcon = false + return uiSwitch + }() + private let searchBar3: HandySearchBar = { + let uiSwitch = HandySearchBar() + uiSwitch.leftIcon = false + uiSwitch.rightIcon = false + uiSwitch.placeholder = "custom" + return uiSwitch + }() + + + override func viewDidLoad() { + super.viewDidLoad() + + self.view.backgroundColor = .white + } + + override func setViewHierarchies() { + self.view.addSubview(searchBar1) + self.view.addSubview(searchBar2) + self.view.addSubview(searchBar3) + } + + override func setViewLayouts() { + searchBar1.snp.makeConstraints { + $0.bottom.equalTo(searchBar2.snp.top) + $0.horizontalEdges.equalToSuperview().inset(20) + } + searchBar2.snp.makeConstraints { + $0.center.equalToSuperview() + $0.horizontalEdges.equalToSuperview().inset(20) + } + searchBar3.snp.makeConstraints { + $0.top.equalTo(searchBar2.snp.bottom) + $0.horizontalEdges.equalToSuperview().inset(20) + } + } +} + diff --git a/Handy/Handy-Storybook/SceneDelegate.swift b/Handy/Handy-Storybook/SceneDelegate.swift index dd9feef..ea347ee 100644 --- a/Handy/Handy-Storybook/SceneDelegate.swift +++ b/Handy/Handy-Storybook/SceneDelegate.swift @@ -16,7 +16,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(frame: UIScreen.main.bounds) window?.windowScene = windowScene - window?.rootViewController = HansySwitchViewController() + window?.rootViewController = HandySearchBarViewController() window?.makeKeyAndVisible() } diff --git a/Handy/Handy.xcodeproj/project.pbxproj b/Handy/Handy.xcodeproj/project.pbxproj index fd42298..f6b07e2 100644 --- a/Handy/Handy.xcodeproj/project.pbxproj +++ b/Handy/Handy.xcodeproj/project.pbxproj @@ -50,6 +50,8 @@ E51FBF9D2C539BC30097B0DA /* HandyIcon.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E5650D442C4E366F002790CC /* HandyIcon.xcassets */; }; E51FBFA02C54CB260097B0DA /* HandyRadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E51FBF9F2C54CB260097B0DA /* HandyRadioButton.swift */; }; E51FBFA22C54CD350097B0DA /* RadioButtonViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E51FBFA12C54CD350097B0DA /* RadioButtonViewController.swift */; }; + E555E4C62D2522EA0091CF15 /* HandySearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E555E4C52D2522EA0091CF15 /* HandySearchBar.swift */; }; + E555E4C82D2529F10091CF15 /* HandySearchBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E555E4C72D2529F10091CF15 /* HandySearchBarViewController.swift */; }; E5650D432C4D326D002790CC /* HandyCheckBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5650D422C4D326D002790CC /* HandyCheckBox.swift */; }; E5650D452C4E366F002790CC /* HandyIcon.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E5650D442C4E366F002790CC /* HandyIcon.xcassets */; }; E5650D472C512B07002790CC /* HandyIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5650D462C512B07002790CC /* HandyIcon.swift */; }; @@ -122,6 +124,8 @@ E51FBF9A2C5399A00097B0DA /* CheckBoxViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckBoxViewController.swift; sourceTree = ""; }; E51FBF9F2C54CB260097B0DA /* HandyRadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyRadioButton.swift; sourceTree = ""; }; E51FBFA12C54CD350097B0DA /* RadioButtonViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonViewController.swift; sourceTree = ""; }; + E555E4C52D2522EA0091CF15 /* HandySearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandySearchBar.swift; sourceTree = ""; }; + E555E4C72D2529F10091CF15 /* HandySearchBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandySearchBarViewController.swift; sourceTree = ""; }; E5650D422C4D326D002790CC /* HandyCheckBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyCheckBox.swift; sourceTree = ""; }; E5650D442C4E366F002790CC /* HandyIcon.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = HandyIcon.xcassets; sourceTree = ""; }; E5650D462C512B07002790CC /* HandyIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandyIcon.swift; sourceTree = ""; }; @@ -177,6 +181,7 @@ E51FBF9A2C5399A00097B0DA /* CheckBoxViewController.swift */, E51FBFA12C54CD350097B0DA /* RadioButtonViewController.swift */, 02697A252C99DDA30027A362 /* HansySwitchViewController.swift */, + E555E4C72D2529F10091CF15 /* HandySearchBarViewController.swift */, ); path = Atom; sourceTree = ""; @@ -237,6 +242,7 @@ E5650D422C4D326D002790CC /* HandyCheckBox.swift */, 02697A232C99D7230027A362 /* HandySwitch.swift */, 02150E492CC8D7AB00EE690E /* HandySnackbar.swift */, + E555E4C52D2522EA0091CF15 /* HandySearchBar.swift */, ); path = Atom; sourceTree = ""; @@ -462,6 +468,7 @@ A5A12A7E2C57A6D900996916 /* ChipViewController.swift in Sources */, A5A12A7F2C57A92000996916 /* HandySematic.swift in Sources */, A5F6D36D2C97099C00FB961F /* DividerViewController.swift in Sources */, + E555E4C82D2529F10091CF15 /* HandySearchBarViewController.swift in Sources */, 025776392C4EA98C00272EC6 /* LabelViewController.swift in Sources */, 0257765D2C4EB9EF00272EC6 /* BaseViewController.swift in Sources */, 02ED764C2C57BD09001569F1 /* HandyBoxButtonViewController.swift in Sources */, @@ -489,6 +496,7 @@ E5669A3F2C443E7300DABC21 /* HandyBasicColor.swift in Sources */, 02ED76312C5284BB001569F1 /* HandyButtonProtocol.swift in Sources */, 02ED76352C5284F3001569F1 /* HandyTextButton.swift in Sources */, + E555E4C62D2522EA0091CF15 /* HandySearchBar.swift in Sources */, A5F6D36B2C96F32D00FB961F /* HandyDivider.swift in Sources */, 02BDB7FC2C3E99920050FB67 /* HandyFont.swift in Sources */, 02ED764A2C5779C3001569F1 /* UIImage+.swift in Sources */, diff --git a/Handy/Handy/Source/Atom/HandySearchBar.swift b/Handy/Handy/Source/Atom/HandySearchBar.swift new file mode 100644 index 0000000..86dfd10 --- /dev/null +++ b/Handy/Handy/Source/Atom/HandySearchBar.swift @@ -0,0 +1,136 @@ +// +// HandySearchBar.swift +// Handy +// +// Created by 성현주 on 1/1/25. +// + +import UIKit +import SnapKit + +final public class HandySearchBar: UIView { + + // MARK: - Properties + + /** + 왼쪽 버튼의 활성화 여부를 설정합니다. + */ + @Invalidating(wrappedValue: true, .layout) public var leftIcon: Bool { + didSet { + leftButton.isHidden = !leftIcon + } + } + + /** + 오른쪽 버튼의 활성화 여부를 설정합니다. + */ + @Invalidating(wrappedValue: true, .layout) public var rightIcon: Bool { + didSet { + rightButton.isHidden = !rightIcon + } + } + + /** + 텍스트 필드의 플레이스홀더를 설정합니다. + */ + @Invalidating(wrappedValue: "", .layout) public var placeholder: String { + didSet { + textField.placeholder = placeholder + } + } + + private let leftButton: UIButton = { + let button = UIButton() + button.setImage(HandyIcon.arrowsChevronLeftFilled.withRenderingMode(.alwaysTemplate), for: .normal) + button.tintColor = HandySemantic.iconBasicSecondary + return button + }() + + private let rightButton: UIButton = { + let button = UIButton() + button.setImage(HandyIcon.filterBarFilled.withRenderingMode(.alwaysTemplate), for: .normal) + button.tintColor = HandySemantic.iconBasicSecondary + return button + }() + + private let searchIconImageView: UIImageView = { + let imageView = UIImageView() + imageView.image = HandyIcon.searchLine.withRenderingMode(.alwaysTemplate) + imageView.tintColor = HandySemantic.iconBasicTertiary + imageView.contentMode = .scaleAspectFit + return imageView + }() + + private let textField: UITextField = { + let textField = UITextField() + textField.placeholder = "Text input" + textField.font = HandyFont.B1Rg16 + textField.textColor = HandySemantic.textBasicPrimary + textField.borderStyle = .none + textField.clearButtonMode = .whileEditing + textField.backgroundColor = HandySemantic.bgBasicLight + textField.layer.cornerRadius = HandySemantic.radiusM + textField.leftViewMode = .always + return textField + }() + + private let stackView: UIStackView = { + let stackView = UIStackView() + stackView.axis = .horizontal + stackView.alignment = .center + stackView.distribution = .fill + stackView.spacing = 8 + return stackView + }() + + // MARK: - Initialization + + override init(frame: CGRect) { + super.init(frame: frame) + setupView() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupView() + } + + // MARK: - Private Methods + + private func setupView() { + // searchIconImageView를 textField의 leftView로 설정 + let leftViewContainer = UIView() + leftViewContainer.addSubview(searchIconImageView) + searchIconImageView.snp.makeConstraints { make in + make.center.equalToSuperview() + make.width.height.equalTo(20) // 검색 아이콘 크기 + } + leftViewContainer.snp.makeConstraints { make in + make.width.height.equalTo(40) // 여백 포함 크기 + } + textField.leftView = leftViewContainer + + // 스택뷰에 서브뷰 추가 + stackView.addArrangedSubview(leftButton) + stackView.addArrangedSubview(textField) + stackView.addArrangedSubview(rightButton) + addSubview(stackView) + + // 기본 레이아웃 설정 + stackView.snp.makeConstraints { make in + make.edges.equalToSuperview().inset(8) // 내부 여백 + } + + leftButton.snp.makeConstraints { make in + make.width.height.equalTo(24) // 왼쪽 버튼 크기 + } + + rightButton.snp.makeConstraints { make in + make.width.height.equalTo(24) // 오른쪽 버튼 크기 + } + + textField.snp.makeConstraints { make in + make.height.equalTo(48) // 텍스트 필드 높이 + } + } +} diff --git a/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_ArrowsChevronLeft_filled.imageset/Contents.json b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_ArrowsChevronLeft_filled.imageset/Contents.json new file mode 100644 index 0000000..533c485 --- /dev/null +++ b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_ArrowsChevronLeft_filled.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "size=36, filled=true.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_ArrowsChevronLeft_filled.imageset/size=36, filled=true.svg b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_ArrowsChevronLeft_filled.imageset/size=36, filled=true.svg new file mode 100644 index 0000000..31ea8cf --- /dev/null +++ b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_ArrowsChevronLeft_filled.imageset/size=36, filled=true.svg @@ -0,0 +1,3 @@ + + + diff --git a/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_filterBar_filled.imageset/Contents.json b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_filterBar_filled.imageset/Contents.json new file mode 100644 index 0000000..533c485 --- /dev/null +++ b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_filterBar_filled.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "size=36, filled=true.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_filterBar_filled.imageset/size=36, filled=true.svg b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_filterBar_filled.imageset/size=36, filled=true.svg new file mode 100644 index 0000000..5075d48 --- /dev/null +++ b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_filterBar_filled.imageset/size=36, filled=true.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_search_line.imageset/Contents.json b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_search_line.imageset/Contents.json new file mode 100644 index 0000000..777366d --- /dev/null +++ b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_search_line.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "size=36, filled=false.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_search_line.imageset/size=36, filled=false.svg b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_search_line.imageset/size=36, filled=false.svg new file mode 100644 index 0000000..cca0b70 --- /dev/null +++ b/Handy/Handy/Source/Foundation/Asset/HandyIcon.xcassets/ic_search_line.imageset/size=36, filled=false.svg @@ -0,0 +1,3 @@ + + + diff --git a/Handy/Handy/Source/Foundation/HandyIcon.swift b/Handy/Handy/Source/Foundation/HandyIcon.swift index 672c404..65c48b9 100644 --- a/Handy/Handy/Source/Foundation/HandyIcon.swift +++ b/Handy/Handy/Source/Foundation/HandyIcon.swift @@ -34,6 +34,10 @@ public enum HandyIcon { public static var wifiLine: UIImage { .load(name: "ic_wifi_line") } public static var closeFilled: UIImage { .load(name: "ic_close_filled") } public static var closeLine: UIImage { .load(name: "ic_close_line") } + public static var searchLine: UIImage { .load(name: "ic_search_line") } + public static var arrowsChevronLeftFilled: UIImage { .load(name: "ic_ArrowsChevronLeft_filled") } + public static var filterBarFilled: UIImage { .load(name: "ic_filterBar_filled") } + public static var checkBoxFilled: UIImage { .load(name: "checkBoxFilled") } public static var checkBoxLine: UIImage { .load(name: "checkBoxLine") } From 16ea7c4dee2067850f75596e3e1a4606a6431582 Mon Sep 17 00:00:00 2001 From: hyeonju Date: Fri, 3 Jan 2025 23:23:00 +0900 Subject: [PATCH 2/6] =?UTF-8?q?[#20]=20ClearButton=20rightView=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Atom/HandySearchBarViewController.swift | 55 +++++++++--- Handy/Handy/Source/Atom/HandySearchBar.swift | 88 ++++++++++++++----- 2 files changed, 107 insertions(+), 36 deletions(-) diff --git a/Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift b/Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift index 6561f7f..d280c7a 100644 --- a/Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift +++ b/Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift @@ -11,20 +11,25 @@ import UIKit final class HandySearchBarViewController: BaseViewController { private let searchBar1: HandySearchBar = { - let uiSwitch = HandySearchBar() - return uiSwitch + let uiSearchBar = HandySearchBar() + return uiSearchBar }() private let searchBar2: HandySearchBar = { - let uiSwitch = HandySearchBar() - uiSwitch.rightIcon = false - return uiSwitch + let uiSearchBar = HandySearchBar() + uiSearchBar.rightIcon = false + return uiSearchBar }() private let searchBar3: HandySearchBar = { - let uiSwitch = HandySearchBar() - uiSwitch.leftIcon = false - uiSwitch.rightIcon = false - uiSwitch.placeholder = "custom" - return uiSwitch + let uiSearchBar = HandySearchBar() + uiSearchBar.leftIcon = false + return uiSearchBar + }() + private let searchBar4: HandySearchBar = { + let uiSearchBar = HandySearchBar() + uiSearchBar.leftIcon = false + uiSearchBar.rightIcon = false + uiSearchBar.placeholder = "Custom" + return uiSearchBar }() @@ -32,17 +37,20 @@ final class HandySearchBarViewController: BaseViewController { super.viewDidLoad() self.view.backgroundColor = .white + setupButtonTargets() } override func setViewHierarchies() { self.view.addSubview(searchBar1) self.view.addSubview(searchBar2) self.view.addSubview(searchBar3) + self.view.addSubview(searchBar4) + } override func setViewLayouts() { searchBar1.snp.makeConstraints { - $0.bottom.equalTo(searchBar2.snp.top) + $0.bottom.equalTo(searchBar2.snp.top).offset(-20) $0.horizontalEdges.equalToSuperview().inset(20) } searchBar2.snp.makeConstraints { @@ -50,9 +58,32 @@ final class HandySearchBarViewController: BaseViewController { $0.horizontalEdges.equalToSuperview().inset(20) } searchBar3.snp.makeConstraints { - $0.top.equalTo(searchBar2.snp.bottom) + $0.top.equalTo(searchBar2.snp.bottom).offset(20) $0.horizontalEdges.equalToSuperview().inset(20) } + searchBar4.snp.makeConstraints { + $0.top.equalTo(searchBar3.snp.bottom).offset(20) + $0.horizontalEdges.equalToSuperview().inset(20) + } + } + private func setupButtonTargets() { + // searchBar1 + searchBar1.leftButton.addTarget(self, action: #selector(leftButtonTapped(_:)), for: .touchUpInside) + searchBar1.rightButton.addTarget(self, action: #selector(rightButtonTapped(_:)), for: .touchUpInside) + + // searchBar2 + searchBar2.leftButton.addTarget(self, action: #selector(leftButtonTapped(_:)), for: .touchUpInside) + + // searchBar3 + searchBar3.rightButton.addTarget(self, action: #selector(rightButtonTapped(_:)), for: .touchUpInside) + } + + @objc private func leftButtonTapped(_ sender: UIButton) { + print("Left button tapped") + } + + @objc private func rightButtonTapped(_ sender: UIButton) { + print("Right button tapped") } } diff --git a/Handy/Handy/Source/Atom/HandySearchBar.swift b/Handy/Handy/Source/Atom/HandySearchBar.swift index 86dfd10..e077938 100644 --- a/Handy/Handy/Source/Atom/HandySearchBar.swift +++ b/Handy/Handy/Source/Atom/HandySearchBar.swift @@ -10,7 +10,7 @@ import SnapKit final public class HandySearchBar: UIView { - // MARK: - Properties + // MARK: - 외부에서 설정할 수 있는 속성 /** 왼쪽 버튼의 활성화 여부를 설정합니다. @@ -39,20 +39,30 @@ final public class HandySearchBar: UIView { } } - private let leftButton: UIButton = { + // MARK: - Properties + + public let leftButton: UIButton = { let button = UIButton() button.setImage(HandyIcon.arrowsChevronLeftFilled.withRenderingMode(.alwaysTemplate), for: .normal) button.tintColor = HandySemantic.iconBasicSecondary return button }() - private let rightButton: UIButton = { + public let rightButton: UIButton = { let button = UIButton() button.setImage(HandyIcon.filterBarFilled.withRenderingMode(.alwaysTemplate), for: .normal) button.tintColor = HandySemantic.iconBasicSecondary return button }() + private let clearButton: UIButton = { + let button = UIButton(type: .system) + button.setImage(HandyIcon.cancelFilled, for: .normal) + button.tintColor = HandySemantic.iconBasicTertiary + button.isHidden = true + return button + }() + private let searchIconImageView: UIImageView = { let imageView = UIImageView() imageView.image = HandyIcon.searchLine.withRenderingMode(.alwaysTemplate) @@ -67,10 +77,10 @@ final public class HandySearchBar: UIView { textField.font = HandyFont.B1Rg16 textField.textColor = HandySemantic.textBasicPrimary textField.borderStyle = .none - textField.clearButtonMode = .whileEditing textField.backgroundColor = HandySemantic.bgBasicLight textField.layer.cornerRadius = HandySemantic.radiusM textField.leftViewMode = .always + textField.rightViewMode = .whileEditing return textField }() @@ -98,39 +108,69 @@ final public class HandySearchBar: UIView { // MARK: - Private Methods private func setupView() { - // searchIconImageView를 textField의 leftView로 설정 - let leftViewContainer = UIView() - leftViewContainer.addSubview(searchIconImageView) - searchIconImageView.snp.makeConstraints { make in - make.center.equalToSuperview() - make.width.height.equalTo(20) // 검색 아이콘 크기 - } - leftViewContainer.snp.makeConstraints { make in - make.width.height.equalTo(40) // 여백 포함 크기 - } - textField.leftView = leftViewContainer + setupLeftView() + setupRightView() - // 스택뷰에 서브뷰 추가 stackView.addArrangedSubview(leftButton) stackView.addArrangedSubview(textField) stackView.addArrangedSubview(rightButton) addSubview(stackView) // 기본 레이아웃 설정 - stackView.snp.makeConstraints { make in - make.edges.equalToSuperview().inset(8) // 내부 여백 + stackView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + leftButton.snp.makeConstraints { + $0.width.height.equalTo(24) + } + + rightButton.snp.makeConstraints { + $0.width.height.equalTo(24) } - leftButton.snp.makeConstraints { make in - make.width.height.equalTo(24) // 왼쪽 버튼 크기 + textField.snp.makeConstraints { + $0.height.equalTo(48) } - rightButton.snp.makeConstraints { make in - make.width.height.equalTo(24) // 오른쪽 버튼 크기 + textField.addTarget(self, action: #selector(textDidChange), for: .editingChanged) + clearButton.addTarget(self, action: #selector(clearText), for: .touchUpInside) + } + + private func setupLeftView() { + let leftViewContainer = UIView() + leftViewContainer.addSubview(searchIconImageView) + searchIconImageView.snp.makeConstraints { + $0.center.equalToSuperview() + $0.width.height.equalTo(20) + } + leftViewContainer.snp.makeConstraints { + $0.width.height.equalTo(40) } + textField.leftView = leftViewContainer + } - textField.snp.makeConstraints { make in - make.height.equalTo(48) // 텍스트 필드 높이 + private func setupRightView() { + let rightViewContainer = UIView() + rightViewContainer.addSubview(clearButton) + clearButton.snp.makeConstraints { + $0.center.equalToSuperview() + $0.width.height.equalTo(20) + } + rightViewContainer.snp.makeConstraints { + $0.width.height.equalTo(40) } + textField.rightView = rightViewContainer + } +} + +private extension HandySearchBar { + @objc func clearText() { + textField.text = "" + clearButton.isHidden = true + } + + @objc func textDidChange() { + clearButton.isHidden = textField.text?.isEmpty ?? true } } From dae286d7b22f0bcb7e435fbc1ea17946517bad25 Mon Sep 17 00:00:00 2001 From: hyeonju Date: Fri, 3 Jan 2025 23:31:02 +0900 Subject: [PATCH 3/6] =?UTF-8?q?[#20]=20=ED=95=A8=EC=88=98=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Handy/Handy/Source/Atom/HandySearchBar.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Handy/Handy/Source/Atom/HandySearchBar.swift b/Handy/Handy/Source/Atom/HandySearchBar.swift index e077938..be00a66 100644 --- a/Handy/Handy/Source/Atom/HandySearchBar.swift +++ b/Handy/Handy/Source/Atom/HandySearchBar.swift @@ -137,6 +137,9 @@ final public class HandySearchBar: UIView { clearButton.addTarget(self, action: #selector(clearText), for: .touchUpInside) } + /** + 텍스트 필드의 왼쪽에 표시되는 아이콘 뷰를 설정합니다. + */ private func setupLeftView() { let leftViewContainer = UIView() leftViewContainer.addSubview(searchIconImageView) @@ -150,6 +153,9 @@ final public class HandySearchBar: UIView { textField.leftView = leftViewContainer } + /** + 텍스트 필드의 오른쪽에 표시되는 clearButton 뷰를 설정합니다. + */ private func setupRightView() { let rightViewContainer = UIView() rightViewContainer.addSubview(clearButton) @@ -165,11 +171,17 @@ final public class HandySearchBar: UIView { } private extension HandySearchBar { + /** + 텍스트 필드의 내용을 지우고, clearButton을 숨깁니다. + */ @objc func clearText() { textField.text = "" clearButton.isHidden = true } + /** + 텍스트 필드의 내용이 변경될 때 clearButton의 표시 여부를 업데이트합니다. + */ @objc func textDidChange() { clearButton.isHidden = textField.text?.isEmpty ?? true } From 4f1d325ae62e6c132b2d8fdce8fe1e4ec60d8fdd Mon Sep 17 00:00:00 2001 From: hyeonju Date: Wed, 8 Jan 2025 22:51:09 +0900 Subject: [PATCH 4/6] =?UTF-8?q?[#20]=20Invalidating=20didset=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20+=20public=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Atom/HandySearchBarViewController.swift | 4 +--- Handy/Handy/Source/Atom/HandySearchBar.swift | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift b/Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift index d280c7a..77f3830 100644 --- a/Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift +++ b/Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift @@ -32,7 +32,6 @@ final class HandySearchBarViewController: BaseViewController { return uiSearchBar }() - override func viewDidLoad() { super.viewDidLoad() @@ -45,7 +44,6 @@ final class HandySearchBarViewController: BaseViewController { self.view.addSubview(searchBar2) self.view.addSubview(searchBar3) self.view.addSubview(searchBar4) - } override func setViewLayouts() { @@ -73,7 +71,7 @@ final class HandySearchBarViewController: BaseViewController { // searchBar2 searchBar2.leftButton.addTarget(self, action: #selector(leftButtonTapped(_:)), for: .touchUpInside) - + // searchBar3 searchBar3.rightButton.addTarget(self, action: #selector(rightButtonTapped(_:)), for: .touchUpInside) } diff --git a/Handy/Handy/Source/Atom/HandySearchBar.swift b/Handy/Handy/Source/Atom/HandySearchBar.swift index be00a66..a35fe7b 100644 --- a/Handy/Handy/Source/Atom/HandySearchBar.swift +++ b/Handy/Handy/Source/Atom/HandySearchBar.swift @@ -8,14 +8,14 @@ import UIKit import SnapKit -final public class HandySearchBar: UIView { +public class HandySearchBar: UIView { // MARK: - 외부에서 설정할 수 있는 속성 /** 왼쪽 버튼의 활성화 여부를 설정합니다. */ - @Invalidating(wrappedValue: true, .layout) public var leftIcon: Bool { + public var leftIcon: Bool = true { didSet { leftButton.isHidden = !leftIcon } @@ -24,7 +24,7 @@ final public class HandySearchBar: UIView { /** 오른쪽 버튼의 활성화 여부를 설정합니다. */ - @Invalidating(wrappedValue: true, .layout) public var rightIcon: Bool { + public var rightIcon: Bool = true { didSet { rightButton.isHidden = !rightIcon } @@ -33,7 +33,7 @@ final public class HandySearchBar: UIView { /** 텍스트 필드의 플레이스홀더를 설정합니다. */ - @Invalidating(wrappedValue: "", .layout) public var placeholder: String { + public var placeholder: String = "" { didSet { textField.placeholder = placeholder } From 88ebc0caa85c569e8226a65491578d9f26b45a79 Mon Sep 17 00:00:00 2001 From: hyeonju Date: Wed, 8 Jan 2025 23:03:42 +0900 Subject: [PATCH 5/6] =?UTF-8?q?[#20]=20searchBar=20=EB=B3=80=EC=88=98?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Atom/HandySearchBarViewController.swift | 8 ++++---- Handy/Handy/Source/Atom/HandySearchBar.swift | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift b/Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift index 77f3830..b202e7f 100644 --- a/Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift +++ b/Handy/Handy-Storybook/Atom/HandySearchBarViewController.swift @@ -16,18 +16,18 @@ final class HandySearchBarViewController: BaseViewController { }() private let searchBar2: HandySearchBar = { let uiSearchBar = HandySearchBar() - uiSearchBar.rightIcon = false + uiSearchBar.isRightButtonHidden = true return uiSearchBar }() private let searchBar3: HandySearchBar = { let uiSearchBar = HandySearchBar() - uiSearchBar.leftIcon = false + uiSearchBar.isLeftButtonHidden = true return uiSearchBar }() private let searchBar4: HandySearchBar = { let uiSearchBar = HandySearchBar() - uiSearchBar.leftIcon = false - uiSearchBar.rightIcon = false + uiSearchBar.isLeftButtonHidden = true + uiSearchBar.isRightButtonHidden = true uiSearchBar.placeholder = "Custom" return uiSearchBar }() diff --git a/Handy/Handy/Source/Atom/HandySearchBar.swift b/Handy/Handy/Source/Atom/HandySearchBar.swift index a35fe7b..17adf14 100644 --- a/Handy/Handy/Source/Atom/HandySearchBar.swift +++ b/Handy/Handy/Source/Atom/HandySearchBar.swift @@ -15,18 +15,18 @@ public class HandySearchBar: UIView { /** 왼쪽 버튼의 활성화 여부를 설정합니다. */ - public var leftIcon: Bool = true { + public var isLeftButtonHidden: Bool = false { didSet { - leftButton.isHidden = !leftIcon + leftButton.isHidden = isLeftButtonHidden } } /** 오른쪽 버튼의 활성화 여부를 설정합니다. */ - public var rightIcon: Bool = true { + public var isRightButtonHidden: Bool = false { didSet { - rightButton.isHidden = !rightIcon + rightButton.isHidden = isRightButtonHidden } } From dba342e1ff9d779668a426a591ef36aa9ac4aa24 Mon Sep 17 00:00:00 2001 From: hyeonju Date: Thu, 9 Jan 2025 15:16:51 +0900 Subject: [PATCH 6/6] =?UTF-8?q?[#20]=20searchBar=20text=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=8D=BC=ED=8B=B0=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Handy/Handy/Source/Atom/HandySearchBar.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Handy/Handy/Source/Atom/HandySearchBar.swift b/Handy/Handy/Source/Atom/HandySearchBar.swift index 17adf14..52373ad 100644 --- a/Handy/Handy/Source/Atom/HandySearchBar.swift +++ b/Handy/Handy/Source/Atom/HandySearchBar.swift @@ -33,12 +33,20 @@ public class HandySearchBar: UIView { /** 텍스트 필드의 플레이스홀더를 설정합니다. */ - public var placeholder: String = "" { + open var placeholder: String = "" { didSet { textField.placeholder = placeholder } } + /** + 텍스트 필드의 입력 값을 외부에서 가져올 수 있는 프로퍼티입니다. + */ + open var text: String? { + return textField.text + } + + // MARK: - Properties public let leftButton: UIButton = {