Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ final class PrimaryButton: UIButton {
self.buttonState = buttonState
super.init(frame: .zero)
configureAttribute(buttonTitle: buttonTitle)
self.isEnabled = buttonState != .disabled
}

required init?(coder: NSCoder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ final class WithdrawViewController: BaseViewController<WithdrawViewModel> {
private let withdrawReasonView = UIView()
private let withdrawReasonLabel = UILabel()
private let withdrawReasonStackView = UIStackView()
private var withdrawButtons: [WithdrawReason: BitnagilChoiceButton] = [:]
private var withdrawReasonButtons: [WithdrawReason: BitnagilChoiceButton] = [:]
private let withdrawReasonTextBackgroundView = UIView()
private let withdrawReasonTextViewPlaceholder = UILabel()
private let withdrawReasonTextView = UITextView()
Expand All @@ -63,6 +63,11 @@ final class WithdrawViewController: BaseViewController<WithdrawViewModel> {
}
}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
removeKeyboardNotification()
}
Comment on lines +66 to +69
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

키보드 알림 등록/해제 시점 불일치 가능성

configureKeyboardNotification()configureAttribute()에서 한 번만 호출되지만, removeKeyboardNotification()viewWillDisappear에서 매번 호출됩니다. 만약 이 화면이 navigation stack에서 다시 나타나는 경우, 키보드 알림이 재등록되지 않아 키보드 처리가 동작하지 않을 수 있습니다.

탈퇴 플로우 특성상 화면을 벗어났다가 다시 돌아오는 경우가 드물지만, 일관성을 위해 viewWillAppear에서 등록하고 viewWillDisappear에서 해제하는 패턴을 권장합니다.

제안하는 수정 방법
+ override func viewWillAppear(_ animated: Bool) {
+     super.viewWillAppear(animated)
+     configureKeyboardNotification()
+ }
+
  override func viewWillDisappear(_ animated: Bool) {
      super.viewWillDisappear(animated)
      removeKeyboardNotification()
  }

그리고 configureAttribute()에서 configureKeyboardNotification() 호출을 제거합니다.

Also applies to: 160-160, 311-323

🤖 Prompt for AI Agents
In `@Projects/Presentation/Sources/Withdraw/View/WithdrawViewController.swift`
around lines 66 - 69, viewWillDisappear에서만 removeKeyboardNotification()을 호출하고
configureKeyboardNotification()는 configureAttribute()에서 한 번만 호출되어 화면이 다시 나타날 때
키보드 알림이 등록되지 않을 수 있으니, 키보드 알림의 등록/해제를 일관된 생명주기로 옮기세요: configureAttribute()에서
configureKeyboardNotification() 호출을 제거하고 viewWillAppear(_: )에서
configureKeyboardNotification()를 등록하며 기존 viewWillDisappear(_:)의
removeKeyboardNotification()은 유지하여 알림 등록/해제 패턴을 일치시킵니다; 대상 함수/메서드:
configureKeyboardNotification(), removeKeyboardNotification(),
configureAttribute(), viewWillAppear(_:), viewWillDisappear(_:).


private func updateConstraint() {
let height = view.bounds.height
if height <= 667 {
Expand Down Expand Up @@ -121,7 +126,7 @@ final class WithdrawViewController: BaseViewController<WithdrawViewModel> {
make.height.equalTo(Layout.withdrawChoiceButtonHeight)
}
withdrawReasonStackView.addArrangedSubview(withdrawChoiceButton)
withdrawButtons[withdrawReason] = withdrawChoiceButton
withdrawReasonButtons[withdrawReason] = withdrawChoiceButton
}

withdrawReasonTextBackgroundView.backgroundColor = BitnagilColor.gray99
Expand All @@ -147,6 +152,12 @@ final class WithdrawViewController: BaseViewController<WithdrawViewModel> {
self?.viewModel.action(input: .withdrawService)
},
for: .touchUpInside)

let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
tapGesture.cancelsTouchesInView = false
view.addGestureRecognizer(tapGesture)

configureKeyboardNotification()
}

override func configureLayout() {
Expand Down Expand Up @@ -284,7 +295,7 @@ final class WithdrawViewController: BaseViewController<WithdrawViewModel> {
}

private func updateWithdrawReason(selectedWithdrawReason: WithdrawReason?) {
withdrawButtons.forEach { withdrawReason in
withdrawReasonButtons.forEach { withdrawReason in
let isSelected = withdrawReason.key == selectedWithdrawReason
withdrawReason.value.updateButtonState(isChecked: isSelected)
}
Expand All @@ -296,11 +307,74 @@ final class WithdrawViewController: BaseViewController<WithdrawViewModel> {
withdrawReasonMaxLengthLabel.isHidden = true
}
}

private func configureKeyboardNotification() {
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillAppear),
name: UIResponder.keyboardWillShowNotification,
object: nil)

NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillDisappear),
name: UIResponder.keyboardWillHideNotification,
object: nil)
}

private func removeKeyboardNotification() {
NotificationCenter.default.removeObserver(
self,
name: UIResponder.keyboardWillShowNotification,
object: nil)

NotificationCenter.default.removeObserver(
self,
name: UIResponder.keyboardWillHideNotification,
object: nil)
}

@objc private func dismissKeyboard() {
view.endEditing(true)
}

@objc private func keyboardWillAppear(_ sender: Notification) {
guard
let keyboardFrame = sender.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect,
let duration = sender.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double
else { return }

let keyboardHeight = keyboardFrame.height
let buttonFrame = withdrawButton.convert(withdrawButton.bounds, to: view)
let buttonBottom = buttonFrame.maxY
let visibleHeight = view.frame.height - keyboardHeight

if buttonBottom > visibleHeight {
let offset = buttonBottom - visibleHeight + 50

UIView.animate(withDuration: duration) {
self.view.frame.origin.y = -offset
}
}
}

@objc private func keyboardWillDisappear(_ sender: Notification) {
guard let duration = sender.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double
else { return }

UIView.animate(withDuration: duration) {
self.view.frame.origin.y = 0
}
}
}

extension WithdrawViewController: UITextViewDelegate {
func textViewDidBeginEditing(_ textView: UITextView) {
viewModel.action(input: .choiceWithdrawReason(reason: nil))

if !textView.text.isEmpty {
viewModel.action(input: .inputWithdrawReason(reason: textView.text))
}
}

func textViewDidChange(_ textView: UITextView) {
Expand Down
Loading