fix: Improve keyboard behavior in ShareBookmarkView

- Add focus state management for proper text field scrolling
- Fix keyboard padding to prevent content overlap
- Smooth animations and remove visual artifacts
This commit is contained in:
Ilyas Hallak 2025-08-06 21:44:57 +02:00
parent e4f055a6af
commit 4915a773d6

View File

@ -3,7 +3,7 @@ import SwiftUI
struct ShareBookmarkView: View { struct ShareBookmarkView: View {
@ObservedObject var viewModel: ShareBookmarkViewModel @ObservedObject var viewModel: ShareBookmarkViewModel
@State private var keyboardHeight: CGFloat = 0 @State private var keyboardHeight: CGFloat = 0
@State private var shouldScrollToTitle = false @FocusState private var focusedField: AddBookmarkFieldFocus?
private func dismissKeyboard() { private func dismissKeyboard() {
NotificationCenter.default.post(name: NSNotification.Name("DismissKeyboard"), object: nil) NotificationCenter.default.post(name: NSNotification.Name("DismissKeyboard"), object: nil)
@ -17,19 +17,20 @@ struct ShareBookmarkView: View {
logoSection logoSection
urlSection urlSection
tagManagementSection tagManagementSection
.id(AddBookmarkFieldFocus.labels)
titleSection titleSection
.id("titleField") .id(AddBookmarkFieldFocus.title)
statusSection statusSection
Spacer(minLength: 100) // Space for button Spacer(minLength: 100) // Space for button
} }
} }
.padding(.bottom, keyboardHeight / 2) .padding(.bottom, max(0, keyboardHeight - 120))
.onChange(of: shouldScrollToTitle) { shouldScroll, _ in .onChange(of: focusedField) { newField, _ in
if shouldScroll { guard let field = newField else { return }
withAnimation(.easeInOut(duration: 0.3)) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
proxy.scrollTo("titleField", anchor: .center) withAnimation(.easeInOut(duration: 0.25)) {
proxy.scrollTo(field, anchor: .center)
} }
shouldScrollToTitle = false
} }
} }
} }
@ -39,27 +40,23 @@ struct ShareBookmarkView: View {
.background(Color(.systemGroupedBackground)) .background(Color(.systemGroupedBackground))
.onAppear { viewModel.onAppear() } .onAppear { viewModel.onAppear() }
.ignoresSafeArea(.keyboard, edges: .bottom) .ignoresSafeArea(.keyboard, edges: .bottom)
.background(
Color.clear
.contentShape(Rectangle()) .contentShape(Rectangle())
.onTapGesture { .onTapGesture {
// Fallback for extensions: tap anywhere to dismiss keyboard
dismissKeyboard() dismissKeyboard()
} }
)
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { notification in .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { notification in
if let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect { if let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
withAnimation(.easeInOut(duration: 0.3)) {
keyboardHeight = keyboardFrame.height keyboardHeight = keyboardFrame.height
// Scroll to title field when keyboard appears
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
shouldScrollToTitle = true
} }
} }
} }
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in
withAnimation(.easeInOut(duration: 0.3)) {
keyboardHeight = 0 keyboardHeight = 0
} }
} }
}
// MARK: - View Components // MARK: - View Components
@ -103,6 +100,7 @@ struct ShareBookmarkView: View {
.padding(.horizontal, 4) .padding(.horizontal, 4)
.frame(maxWidth: 420) .frame(maxWidth: 420)
.frame(maxWidth: .infinity, alignment: .center) .frame(maxWidth: .infinity, alignment: .center)
.focused($focusedField, equals: .title)
.toolbar { .toolbar {
ToolbarItemGroup(placement: .keyboard) { ToolbarItemGroup(placement: .keyboard) {
Spacer() Spacer()
@ -123,6 +121,7 @@ struct ShareBookmarkView: View {
isLabelsLoading: false, isLabelsLoading: false,
availableLabelPages: convertToBookmarkLabelPages(viewModel.availableLabelPages), availableLabelPages: convertToBookmarkLabelPages(viewModel.availableLabelPages),
filteredLabels: convertToBookmarkLabels(viewModel.filteredLabels), filteredLabels: convertToBookmarkLabels(viewModel.filteredLabels),
searchFieldFocus: $focusedField,
onAddCustomTag: { onAddCustomTag: {
addCustomTag() addCustomTag()
}, },
@ -176,7 +175,6 @@ struct ShareBookmarkView: View {
.padding(.top, 16) .padding(.top, 16)
.padding(.bottom, 32) .padding(.bottom, 32)
.disabled(viewModel.isSaving) .disabled(viewModel.isSaving)
.background(Color(.systemGroupedBackground))
} }
// MARK: - Helper Functions // MARK: - Helper Functions