[#20] HandySearchBar 추가#34
Conversation
chongin12
left a comment
There was a problem hiding this comment.
searchBar를 사용하는 입장에서 leftButton에 액션을 지정할 때 leftButton.addTarget하는 방식이 조금 어색해보이는 것 같아요. 애플 기본 컴포넌트 중 contextMenu 같은 것들은 delegate 등의 방식으로 지정할 수 있어요.
물론 button에 addTarget하는 것은 더 많은 것을 커스터마이징할 수 있겠지만 아마 터치 이벤트 말고는 사용하는 일이 없을 것 같아요. 따라서 좀 더 사용하기 편하게 해주시면 좋을 것 같아요!!
textField 관련해서도 사용하기 좀 더 편하게 delegate나 프로퍼티 등을 제공할 수 있을 것 같아요. 만약 text input이 비어있을 때 rightButton을 비활성화 하고 싶다면 어떻게 로직을 작성할 수 있을까요?
-> 현재 textField가 private이기 때문에 addTarget, notification, delegate 모두 불가능할 것 같아요..ㅠ override 하는 선택지도 final이기 때문에 안될 것 같구요.
이 컴포넌트를 사용하여 검색 기능을 구현한다고 했을 때, 사용자가 입력한 내용은 어떻게 받아올 수 있나요? public 이상의 접근으로 활성된 text같은 프로퍼티나 delegate 등을 뚫어놓으면 더 좋을 것 같아요!!
| @Invalidating(wrappedValue: true, .layout) public var leftIcon: Bool { | ||
| didSet { | ||
| leftButton.isHidden = !leftIcon | ||
| } | ||
| } |
There was a problem hiding this comment.
layout을 무효화(Invalidating) 시키면 setNeedsLayout()가 불리게 됩니다. (출처)
setNeedsLayout()은 다음 UI 업데이트 사이클에 layoutSubviews() 함수를 부르게 해줍니다.
그래서, @invalidating(.layout) 에 따라서 다시 subview들을 배치해야 하는 상황일 때 추가적인 작업은 layoutSubviews() 을 오버라이딩 해서 넣는거죠.
여기에서는 @Invalidating과 didSet 중 하나는 굳이 필요하지 않을 것 같아요. @Invalidating을 사용한다면 didSet보다는 layoutSubviews() 함수를 오버라이딩해서 leftButton과 rightButton의 hidden을 결정하면 될 것 같구요, didSet을 사용한다면 @Invalidating 방식은 아예 사용하지 않는 편이 좋을 것 같아요.
더 편한 방식을 고민해서 사용해주시면 될 것 같습니다~!!
There was a problem hiding this comment.
HandySearchBarViewController 부분 (디버깅하는 뷰)에 아무 버튼을 하나 만들고 searchBar1.leftIcon.toggle() 을 해보시면 잘 동작하는지 확인할 수 있어요!
There was a problem hiding this comment.
흠 말씀해주신 내용을 바탕으로 다시 고민해보니, invalidating 접근은 레이아웃을 전반적으로 수정해야 하는 경우에 적합하지만, 버튼의 hidden 속성과 같이 간단한 UI 요소 변경에는 과도할 것 같다는 생각이 드네요!
버튼과 텍스트 필드의 레이아웃도 스택뷰로 관리하고 있으니 굳이 무효화가 필요없기 때문에 간단히 didset으로 변경해도 충분할것 같습니다!
didset으로 변경하는 방향으로 수정하겟습니다~! 좋은 답변 감사드립니다!
| import UIKit | ||
| import SnapKit | ||
|
|
||
| final public class HandySearchBar: UIView { |
There was a problem hiding this comment.
이 컴포넌트를 final로 규정짓는 것은 살짝 위험할 수도 있다고 생각해요!
HandySearchBar를 상속받아서 더 꾸밀 수도 있다고 생각하기 때문에 final public보다는 필요한 곳에 open 접근제어를 걸어두는 것도 좋다고 생각합니다~!!
There was a problem hiding this comment.
앗 맞습니다. 추가적인 커스텀이 필요한 상황이 분명 발생할거기 때문에 final은 과하다라는 생각이 듭니다. 필요한 부분에 open으로 수정했습니다!
|
변수명은 말씀해주신대로 변경이 필요해보이네요. 사용하는 입장에서 매우 혼동될 것 같아요. |
버튼과 텍스트 필드가 공존하면서 버튼의 역할이 정해지지 않은 컴포넌트는 흔하지 않아서 어떻게 버튼 이벤트를 처리해야할지 고민했는데, 처음에 addTarget을 사용했던 이유는 직관성 때문이었습니다. 예를 들어 오른쪽 버튼인 필터 버튼을 터치가 아닌 꾹 눌렀을때 필터 옵션이 나오는 기획이라면 버튼이 터치 이외의 용도가 바뀌는 상황이 생기기도 하고 또는 버튼이 아예 없는 상황도 존재하기 때문에 addTarget이 더 직관적이고 조금이라도 문서를 덜보고 활용할수 있을것 같다는 개인적인 생각을 했습니다. 컴포넌트의 핵심인 재사용성을 생각하면 delgate로 자주쓰이는 상황(터치 이벤트)를 정의하고 외부에서 바로 사용할수 있게 하는것이 더 편리할것 같습니다. 한편으로는 delegate가 조금은 과하다는 생각이 들기도 하는데 클로저로 전달하는 방식은 또 자주 사용하는 방식은 아니라서 고민입니다 ㅠ @LeeJoEun-01 은 어떻게 생각하시나욥?
uikit의 텍스트 필드에는 여러개의 델리게이트가 있는데 이중에서 자주 사용하는건 한정적이고 handy에서 모든상황을 가정하고 delegate를 만드는것 보다 외부에서 override를 사용하는게 더 괜찮을것 같다는 생각이 듭니다! 일반 텍스트 필드라면 여러 델리게이트가 도움이 될수 있지만 텍스트 필드중에서 searchBar의 성격을 띈다면 더 줄어 들것 같아요. 그래서 차라리 textField를 public으로 열어두는게 좋을지 일부 필요한 델리게이트만 고민해서 넣을지 조금만 더 고민해서 반영하겠습니다!
이부분을 미쳐 생각하지 못했습니다 ㅠ 말씀해주신대로 textField의 text를 리턴해주는 text 프로퍼티를 만들어 두었습니다! /**
텍스트 필드의 입력 값을 외부에서 가져올 수 있는 프로퍼티입니다.
*/
open var text: String? {
return textField.text
}위와같이 open으로 text 뚫어두었습니당 |
흐음 저도 평소에 버튼에 이벤트를 추가할 때 사용하는 AddTarget이 더 직관적이라고 생각해요! 물론 delegate도 양방향으로 무언가를 전달하는 대리자 역할을 하는건 맞지만 평소에 버튼을 추가할때 사용하는 방법인 AddTarget을 사용하는게 나중에 사용하는 사람들에게 더 직관적으로 와닿지 않을까요?? @hye0njuoo @chongin12 |
|
구현중에 고민인 부분이 생겼는데요. @objc public protocol HandySearchBarDelegate: NSObjectProtocol {
/**
텍스트 필드가 편집을 시작할 때 호출됩니다.
*/
@objc optional func handySearchBarDidBeginEditing(_ searchBar: HandySearchBar)
/**
텍스트 필드가 편집을 종료할 때 호출됩니다.
*/
@objc optional func handySearchBarDidEndEditing(_ searchBar: HandySearchBar)
}이 방법과 public protocol HandySearchBarDelegate: AnyObject {
// 텍스트 필드가 편집을 시작할 때 호출됩니다.
func handySearchBarDidBeginEditing(_ searchBar: HandySearchBar)
// 텍스트 필드가 편집을 종료할 때 호출됩니다.
func handySearchBarDidEndEditing(_ searchBar: HandySearchBar)
}
public extension HandySearchBarDelegate {
// 기본 구현 제공 (선택적 구현)
func handySearchBarDidBeginEditing(_ searchBar: HandySearchBar) {
// 기본 구현 (빈 함수)
}
func handySearchBarDidEndEditing(_ searchBar: HandySearchBar) {
// 기본 구현 (빈 함수)
}
}이렇게 프로토콜의 extenstion으로 하는 방법이 있던데 어떤방법이 좋을까요? 아니면 그냥 구현하도록 강제하는건 별로인가요 ㅎㅎ? |
isLeftButtonHidden, isRightButtonHidden 변수명으로 수정했습니다. figma와 다른 이름은 문서 작성시 따로 명시해 혼란 없도록 해두겠습니다! |
둘 다 일리있는 접근인 것 같아요! 모두 optional 함수라면 extension쪽이 더 일리가 있다고 생각합니다!! 둘 중 편하신 것으로 하시면 될 것 같아요~! |
📌 Summary
handySearchBar 컴포넌트를 추가했습니다.
✍️ Description
💡 PR Point
- clearButton
기본 clearButton이 아닌 handyIcon이 적용된 clearButton을 사용하기 위해 clearButton을 따로 구현했습니다.
텍스트 입력 감지:
textField에 입력이 발생할 때마다 textDidChange() 메서드가 호출됩니다.
이 메서드는 textField.text?.isEmpty 값을 확인하여 텍스트 필드가 비어 있으면 clearButton을 숨기고, 비어 있지 않으면 버튼을 표시합니다
클릭 동작:
사용자가 clearButton을 클릭하면 clearText() 메서드가 호출되어 텍스트 필드 글자를 지워줍니다.
이 메서드는 텍스트 필드의 내용을 지우고 (textField.text = ""), 다시 clearButton을 숨겨줍니다. (clearButton.isHidden = true).
- left,right 버튼 public
외부에서 이 컴포넌트를 사용할때 각 버튼에 기능을 연결하기 위해 public으로 접근할수 있도록 했습니다. 외부에서 연결할수 있도록 함수를 따로 빼두어 캡슐화를 더 견고히 할지, 지금처럼 그냥 public으로 열어둘지 고민입니다.
- 변수명 고민
현재 왼쪽 버튼, 오른쪽 버튼 여부를 피그마 properties 이름인 leftIcon,rightIcon이라는 이름으로 관리하고 있는데, 외부에서 사용할때 좋은 변수명은 아닌것 같습니다.
isLeftButtonVisible, isLeftButtunAble 이름으로 바꾸면 어떨까요?
📚 Reference
https://developer.apple.com/documentation/uikit/uitextfield/viewmode
🔥 Test
Simulator.Screen.Recording.-.iPhone.16.Pro.-.2025-01-03.at.23.49.04.mp4