Skip to content

Commit 151d7fe

Browse files
authored
Merge pull request #161 from SOPT-all/feat/#149-registerScrollFeat
[�Feat] #149 - 키보드 활성화 여부에 따른 자동 스크롤 구현
2 parents 32ac69e + 96c3276 commit 151d7fe

File tree

6 files changed

+91
-40
lines changed

6 files changed

+91
-40
lines changed

Spoony-iOS/Spoony-iOS/Source/Feature/Register/Components/ChipsContainerView.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ struct ChipsContainerView: View {
1414
let horizontalSpacing: CGFloat
1515
let items: [CategoryChip]
1616
var sortedItems: [CategoryChip] {
17-
items.sorted { $0.id > $1.id }
17+
items.sorted { $0.id < $1.id }
1818
}
1919

2020
init(

Spoony-iOS/Spoony-iOS/Source/Feature/Register/State/RegisterIntent.swift

+1
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@ enum RegisterIntent {
2929
case deleteImage(UploadImage)
3030
case getCategories
3131
case didTapPhoto([PhotosPickerItem])
32+
case updateKeyboardHeight(CGFloat)
3233
}

Spoony-iOS/Spoony-iOS/Source/Feature/Register/State/RegisterState.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ struct RegisterState {
1414

1515
// MARK: - InfoStepView
1616
var placeText: String = ""
17-
var recommendTexts: [TextList] = [.init()]
1817

19-
var toast: Toast?
18+
var keyboardHeight: CGFloat = 0
2019

20+
var toast: Toast?
21+
var recommendTexts: [TextList] = [.init()]
2122
var categorys: [CategoryChip] = []
2223
var selectedCategory: [CategoryChip] = []
2324

Spoony-iOS/Spoony-iOS/Source/Feature/Register/State/RegisterStore.swift

+2
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ final class RegisterStore: ObservableObject {
146146
fetchCategories()
147147
case .didTapPhoto(let items):
148148
validateSelectedPhotoCount(item: items)
149+
case .updateKeyboardHeight(let height):
150+
state.keyboardHeight = height
149151
}
150152
}
151153
}

Spoony-iOS/Spoony-iOS/Source/Feature/Register/View/InfoStepView.swift

+77-33
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import SwiftUI
99

1010
struct InfoStepView: View {
1111
@ObservedObject private var store: RegisterStore
12+
@FocusState private var isPlaceTextFieldFocused: Bool
1213

1314
init(store: RegisterStore) {
1415
self.store = store
@@ -27,14 +28,15 @@ struct InfoStepView: View {
2728
store.dispatch(.didTapBackground(.start))
2829
hideKeyboard()
2930
}
30-
.toastView(toast: Binding(get: {
31-
store.state.toast
32-
}, set: { newValue in
33-
store.dispatch(.updateToast(newValue))
34-
}))
31+
.toastView(toast: Binding(
32+
get: { store.state.toast },
33+
set: { newValue in
34+
store.dispatch(.updateToast(newValue))
35+
}
36+
))
3537
.task {
3638
store.dispatch(.getCategories)
37-
}
39+
}
3840
}
3941
}
4042

@@ -85,6 +87,7 @@ extension InfoStepView {
8587
) {
8688
store.dispatch(.didTapButtonIcon(.place))
8789
}
90+
.focused($isPlaceTextFieldFocused)
8891
.onSubmit {
8992
store.dispatch(.didTapkeyboardEnter)
9093
}
@@ -114,7 +117,7 @@ extension InfoStepView {
114117
ChipsContainerView(
115118
selectedItem: Binding(get: {
116119
store.state.selectedCategory
117-
}, set: { newValue in
120+
}, set: { newValue in
118121
store.dispatch(.updateSelectedCategoryChip(newValue))
119122
}),
120123
items: store.state.categorys
@@ -150,7 +153,7 @@ extension InfoStepView {
150153
plusButton
151154
}
152155
}
153-
}
156+
}
154157
}
155158

156159
private var dropDownView: some View {
@@ -197,34 +200,75 @@ extension InfoStepView {
197200
}
198201

199202
private var nextButton: some View {
200-
SpoonyButton(
201-
style: .primary,
202-
size: .xlarge,
203-
title: "다음",
204-
disabled: Binding(
205-
get: {
206-
store.state.isDisableStartButton
207-
}, set: { newValue in
208-
store.dispatch(.updateButtonState(newValue, .start))
203+
ScrollViewReader { proxy in
204+
VStack {
205+
SpoonyButton(
206+
style: .primary,
207+
size: .xlarge,
208+
title: "다음",
209+
disabled: Binding(
210+
get: {
211+
store.state.isDisableStartButton
212+
}, set: { newValue in
213+
store.dispatch(.updateButtonState(newValue, .start))
214+
}
215+
)
216+
) {
217+
store.dispatch(.didTapNextButton(.start))
209218
}
210-
)
211-
) {
212-
store.dispatch(.didTapNextButton(.start))
213-
}
214-
.padding(.bottom, 20)
215-
.padding(.top, 61)
216-
.overlay(alignment: .top) {
217-
ToolTipView()
218-
.padding(.top, 5)
219-
.opacity(store.state.isToolTipPresented ? 1 : 0)
220-
.task {
221-
do {
222-
try await Task.sleep(for: .seconds(3))
223-
store.dispatch(.updateToolTipState)
224-
} catch {
225-
219+
.padding(.bottom, 20)
220+
.padding(.top, 61)
221+
}
222+
.onAppear {
223+
NotificationCenter.default.addObserver(
224+
forName: UIResponder.keyboardWillShowNotification,
225+
object: nil,
226+
queue: .main
227+
) { notification in
228+
if let frame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
229+
withAnimation(.smooth) {
230+
if !isPlaceTextFieldFocused {
231+
store.dispatch(.updateKeyboardHeight(frame.height))
232+
}
233+
}
226234
}
227235
}
236+
237+
NotificationCenter.default.addObserver(
238+
forName: UIResponder.keyboardWillHideNotification,
239+
object: nil,
240+
queue: .main
241+
) { _ in
242+
withAnimation(.smooth) {
243+
store.dispatch(.updateKeyboardHeight(0))
244+
}
245+
}
246+
}
247+
.id("nextButton")
248+
.padding(.bottom, store.state.keyboardHeight)
249+
.ignoresSafeArea(.keyboard)
250+
.onChange(of: store.state.keyboardHeight) { _, newValue in
251+
if newValue != 0 {
252+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
253+
withAnimation {
254+
proxy.scrollTo("nextButton", anchor: .bottom)
255+
}
256+
}
257+
}
258+
}
259+
.overlay(alignment: .top) {
260+
ToolTipView()
261+
.padding(.top, 5)
262+
.opacity(store.state.isToolTipPresented ? 1 : 0)
263+
.task {
264+
do {
265+
try await Task.sleep(for: .seconds(3))
266+
store.dispatch(.updateToolTipState)
267+
} catch {
268+
269+
}
270+
}
271+
}
228272
}
229273
}
230274
}

Spoony-iOS/Spoony-iOS/Source/Feature/Register/View/Register.swift

+7-4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ struct Register: View {
2626
.transition(.slide)
2727
.animation(.easeInOut, value: store.state.registerStep)
2828
}
29+
.scrollIndicators(.hidden)
30+
.simultaneousGesture(
31+
DragGesture()
32+
.onChanged { _ in
33+
hideKeyboard()
34+
}
35+
)
2936
}
3037
}
3138
}
@@ -67,7 +74,3 @@ enum RegisterStep: Int {
6774
case middle = 2
6875
case end = 3
6976
}
70-
71-
#Preview {
72-
Register(store: .init(navigationManager: .init()))
73-
}

0 commit comments

Comments
 (0)