diff --git a/Bitkit/AppScene.swift b/Bitkit/AppScene.swift index 180dad34..71d8767d 100644 --- a/Bitkit/AppScene.swift +++ b/Bitkit/AppScene.swift @@ -27,6 +27,7 @@ struct AppScene: View { @StateObject private var transferTracking: TransferTrackingManager @StateObject private var channelDetails = ChannelDetailsViewModel.shared @StateObject private var migrations = MigrationsService.shared + @State private var keyboardManager = KeyboardManager() @State private var hideSplash = false @State private var removeSplash = false @@ -134,6 +135,7 @@ struct AppScene: View { .environmentObject(tagManager) .environmentObject(transferTracking) .environmentObject(channelDetails) + .environment(keyboardManager) .onAppear { if !settings.pinEnabled { isPinVerified = true diff --git a/Bitkit/Components/TabBar/TabBar.swift b/Bitkit/Components/TabBar/TabBar.swift index 5b81eaa7..c1db33b3 100644 --- a/Bitkit/Components/TabBar/TabBar.swift +++ b/Bitkit/Components/TabBar/TabBar.swift @@ -35,7 +35,6 @@ struct TabBar: View { } } .animation(.easeInOut, value: shouldShow) - .ignoresSafeArea(.keyboard) .bottomSafeAreaPadding() } diff --git a/Bitkit/Components/Widgets/CalculatorWidget.swift b/Bitkit/Components/Widgets/CalculatorWidget.swift index ee17f0e6..1e50d071 100644 --- a/Bitkit/Components/Widgets/CalculatorWidget.swift +++ b/Bitkit/Components/Widgets/CalculatorWidget.swift @@ -102,7 +102,7 @@ struct CalculatorWidget: View { CurrencyInputRow( icon: CircularIcon( - icon: BodyMSBText(currency.symbol, textColor: .brandAccent), + icon: BodyMSBText(currency.symbol.count > 2 ? String(currency.symbol.prefix(1)) : currency.symbol, textColor: .brandAccent), backgroundColor: .gray6, size: 32 ), @@ -134,6 +134,14 @@ struct CalculatorWidget: View { } } } + .toolbar { + ToolbarItemGroup(placement: .keyboard) { + Spacer() + Button(t("common__done")) { + focusedField = nil + } + } + } } .onAppear { // Initialize fiat amount on first load diff --git a/Bitkit/Managers/KeyboardManager.swift b/Bitkit/Managers/KeyboardManager.swift new file mode 100644 index 00000000..97095b51 --- /dev/null +++ b/Bitkit/Managers/KeyboardManager.swift @@ -0,0 +1,40 @@ +import SwiftUI +import UIKit + +@Observable +final class KeyboardManager { + var isPresented = false + var height: CGFloat = 0 + + private var notificationTokens: [NSObjectProtocol] = [] + + init() { + let willShowToken = NotificationCenter.default.addObserver( + forName: UIResponder.keyboardWillShowNotification, + object: nil, + queue: .main + ) { [weak self] notification in + guard let self else { return } + let frame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect + height = frame?.height ?? 0 + isPresented = true + } + + let willHideToken = NotificationCenter.default.addObserver( + forName: UIResponder.keyboardWillHideNotification, + object: nil, + queue: .main + ) { [weak self] _ in + guard let self else { return } + height = 0 + isPresented = false + } + + notificationTokens = [willShowToken, willHideToken] + } + + deinit { + // Remove observers to prevent memory leaks + notificationTokens.forEach { NotificationCenter.default.removeObserver($0) } + } +} diff --git a/Bitkit/Views/Home/HomeWidgetsView.swift b/Bitkit/Views/Home/HomeWidgetsView.swift index 3f6b4f69..fea1bcf9 100644 --- a/Bitkit/Views/Home/HomeWidgetsView.swift +++ b/Bitkit/Views/Home/HomeWidgetsView.swift @@ -2,13 +2,21 @@ import SwiftUI struct HomeWidgetsView: View { @EnvironmentObject var app: AppViewModel + @Environment(KeyboardManager.self) private var keyboard @EnvironmentObject var navigation: NavigationViewModel @EnvironmentObject var settings: SettingsViewModel @EnvironmentObject var suggestionsManager: SuggestionsManager @EnvironmentObject var wallet: WalletViewModel @EnvironmentObject var widgets: WidgetsViewModel + @Binding var isEditingWidgets: Bool + private var bottomPadding: CGFloat { + // Keep the calculator widget fully scrollable above the keyboard. + let inset = keyboard.height + ScreenLayout.bottomSpacing + return keyboard.isPresented ? inset : ScreenLayout.bottomPaddingWithSafeArea + } + /// Widgets to display; suggestions widget is hidden when it would show no cards (unless editing). private var widgetsToShow: [Widget] { widgets.savedWidgets.filter { widget in @@ -46,9 +54,11 @@ struct HomeWidgetsView: View { } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) .padding(.top, ScreenLayout.topPaddingWithSafeArea) - .padding(.bottom, ScreenLayout.bottomPaddingWithSafeArea) + .padding(.bottom, bottomPadding) .padding(.horizontal) } + // Dismiss (calculator widget) keyboard when scrolling + .scrollDismissesKeyboard(.interactively) } private func rowContent(_ widget: Widget) -> some View { diff --git a/CHANGELOG.md b/CHANGELOG.md index 72c889ad..2e4dbd23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,4 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- Stabilize Home widgets calculator keyboard behavior by improving keyboard avoidance and adding a keyboard "Done" button. #513 +- In the calculator widget, show only the first character of multi-character currency symbols. #513 + [Unreleased]: https://github.com/synonymdev/bitkit-ios/compare/v2.1.2...HEAD