Skip to content

Commit 4818c31

Browse files
authored
Merge pull request #116 from MONEYMONG/fix/codeView
초대코드 입력창 붙여넣기, 지우기 기능 구현
2 parents 52d5b6f + a34fe1b commit 4818c31

2 files changed

Lines changed: 96 additions & 34 deletions

File tree

Projects/Feature/Agency/Sources/Scene/JoinAgency/Components/CodeView.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,20 @@ import PinLayout
88
import FlexLayout
99

1010
final class CodeView: UIView {
11+
weak var delegate: UITextFieldDelegate? {
12+
didSet {
13+
numberTextField.delegate = delegate
14+
}
15+
}
16+
1117
enum State {
1218
case plain // 입력대기
1319
case focused // 입력중
1420
case done // 입력완료
1521
case error // 에러
1622
}
1723

24+
private(set) var state: State
1825
private var disposeBag = DisposeBag()
1926
private let rootContainer = UIView()
2027
private let mongCodeImageView: UIImageView = {
@@ -33,6 +40,7 @@ final class CodeView: UIView {
3340
}()
3441

3542
init(state: State) {
43+
self.state = state
3644
super.init(frame: .zero)
3745
setupConstraints()
3846
setState(state)
@@ -64,17 +72,20 @@ final class CodeView: UIView {
6472
}
6573

6674
func setState(_ state: State) {
75+
self.state = state
6776
switch state {
6877
case .plain:
6978
rootContainer.flex.border(1, Colors.Gray._3).backgroundColor(Colors.White._1)
7079
mongCodeImageView.isHidden = true
7180
case .focused:
7281
rootContainer.flex.border(1, Colors.Blue._4).backgroundColor(Colors.White._1)
73-
numberTextField.becomeFirstResponder()
7482
mongCodeImageView.isHidden = true
7583
case .done:
76-
rootContainer.flex.border(0, Colors.White._1).backgroundColor(Colors.White._1)
77-
mongCodeImageView.isHidden = false
84+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
85+
guard let self else { return }
86+
rootContainer.flex.border(0, Colors.White._1).backgroundColor(Colors.White._1)
87+
mongCodeImageView.isHidden = false
88+
}
7889
case .error:
7990
rootContainer.flex.border(1, Colors.Red._3).backgroundColor(Colors.Gray._1)
8091
mongCodeImageView.isHidden = true

Projects/Feature/Agency/Sources/Scene/JoinAgency/JoinAgencyVC.swift

Lines changed: 82 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ final class JoinAgencyVC: BaseVC, ReactorKit.View {
5959
}
6060
}
6161

62+
override func viewDidAppear(_ animated: Bool) {
63+
super.viewDidAppear(animated)
64+
codeviews[0].numberTextField.becomeFirstResponder()
65+
}
66+
6267
func bind(reactor: JoinAgencyReactor) {
6368
setRightItem(.closeBlack)
6469

@@ -74,45 +79,18 @@ final class JoinAgencyVC: BaseVC, ReactorKit.View {
7479
.disposed(by: disposeBag)
7580

7681
codeviews.enumerated().forEach { index, codeView in
77-
codeView.numberTextField.rx.text
78-
.orEmpty.skip(1)
79-
.map { Reactor.Action.textFieldDidChange(text: $0, index: index)}
80-
.bind(to: reactor.action)
81-
.disposed(by: disposeBag)
82-
}
83-
84-
codeviews.indices.forEach { index in
85-
reactor.pulse(\.$codes)
86-
.map { $0[index] }
87-
.distinctUntilChanged()
88-
.observe(on: MainScheduler.instance)
89-
.bind(with: self) { owner, code in
90-
owner.codeviews[index].numberTextField.text = code
91-
92-
if code.isEmpty {
93-
owner.codeviews[index].setState(.plain)
94-
} else {
95-
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
96-
owner.codeviews[index].setState(.done)
97-
owner.codeviews[safe: index+1]?.setState(.focused)
98-
99-
if !owner.codeviews.indices.contains(index+1) {
100-
owner.view.endEditing(true)
101-
}
102-
}
103-
}
104-
}
105-
.disposed(by: disposeBag)
82+
codeView.delegate = self
10683
}
10784

10885
reactor.pulse(\.$codes)
10986
.distinctUntilChanged()
11087
.filter { $0.joined().count == 6 }
11188
.delay(.seconds(1), scheduler: MainScheduler.instance)
89+
.do { [weak self] _ in self?.view.endEditing(true) }
11290
.map { _ in Reactor.Action.requestJoinAgency}
11391
.bind(to: reactor.action)
11492
.disposed(by: disposeBag)
115-
93+
11694
reactor.pulse(\.$snackBarMessage)
11795
.compactMap { $0 }
11896
.observe(on: MainScheduler.instance)
@@ -121,10 +99,15 @@ final class JoinAgencyVC: BaseVC, ReactorKit.View {
12199

122100
SnackBarManager.show(title: message) {
123101
owner.reactor?.action.onNext(.tapRetryButton)
102+
owner.codeviews.enumerated().forEach { index, codeView in
103+
codeView.numberTextField.text = ""
104+
codeView.setState(index == 0 ? .focused : .plain)
105+
}
106+
owner.codeviews[0].numberTextField.becomeFirstResponder()
124107
}
125108
}
126109
.disposed(by: disposeBag)
127-
110+
128111
reactor.pulse(\.$errorMessage)
129112
.compactMap { $0 }
130113
.observe(on: MainScheduler.instance)
@@ -146,3 +129,71 @@ final class JoinAgencyVC: BaseVC, ReactorKit.View {
146129
.disposed(by: disposeBag)
147130
}
148131
}
132+
133+
extension JoinAgencyVC: UITextFieldDelegate {
134+
func textFieldDidBeginEditing(_ textField: UITextField) {
135+
textField.selectedTextRange = nil
136+
}
137+
138+
func textField(
139+
_ textField: UITextField,
140+
shouldChangeCharactersIn range: NSRange,
141+
replacementString string: String
142+
) -> Bool {
143+
guard Int(string) != nil else { return false }
144+
guard let idx = codeviews.map(\.numberTextField).firstIndex(of: textField) else { return false }
145+
146+
if let text = textField.text, !text.isEmpty, !string.isEmpty {
147+
guard idx < codeviews.count - 1 else { return false }
148+
codeviews[idx+1].numberTextField.becomeFirstResponder()
149+
codeviews[idx+1].numberTextField.text = string
150+
reactor?.action.onNext(.textFieldDidChange(text: string, index: idx+1))
151+
codeviews[idx+1].setState(.done)
152+
if idx < codeviews.count - 2 {
153+
codeviews[idx+2].setState(.focused)
154+
}
155+
return false
156+
}
157+
158+
if string.isEmpty {
159+
textField.text = ""
160+
reactor?.action.onNext(.textFieldDidChange(text: "", index: idx))
161+
codeviews.forEach {
162+
if $0.state == .focused {
163+
$0.setState(.plain)
164+
}
165+
}
166+
codeviews[idx].setState(.focused)
167+
168+
if idx > 0 {
169+
let prevField = codeviews[idx-1].numberTextField
170+
prevField.becomeFirstResponder()
171+
prevField.selectedTextRange = prevField.textRange(from: prevField.endOfDocument, to: prevField.endOfDocument)
172+
}
173+
return false
174+
} else if string.count > 1 {
175+
let numbers = string.compactMap { $0.isNumber ? String($0) : nil }
176+
guard !numbers.isEmpty else { return false }
177+
178+
for (index, number) in numbers.prefix(6).enumerated() {
179+
let codeview = codeviews[index]
180+
codeview.numberTextField.text = number
181+
reactor?.action.onNext(.textFieldDidChange(text: number, index: index))
182+
codeview.setState(.done)
183+
}
184+
185+
let nextIndex = min(codeviews.count - 1, idx + numbers.count)
186+
let lastTextField = codeviews[nextIndex].numberTextField
187+
lastTextField.becomeFirstResponder()
188+
lastTextField.selectedTextRange = lastTextField.textRange(from: lastTextField.endOfDocument, to: lastTextField.endOfDocument)
189+
return false
190+
}
191+
192+
codeviews[idx].setState(.done)
193+
if idx < codeviews.count - 1 {
194+
codeviews[idx+1].setState(.focused)
195+
}
196+
reactor?.action.onNext(.textFieldDidChange(text: string, index: idx))
197+
return true
198+
}
199+
}

0 commit comments

Comments
 (0)