on master help sign in
Add iOS keyboard avoidance by tracking keyboard height and adjusting page padding
commit8560d1f8fa2475f292c9caed55460a95edc87548 authoriko <mail@iko.soy> dateSat 18 Apr 2026, 10:04 (6 months ago) parentc4559f6
5 files changed+45−4
client/App.fs+12−3 client/AppTypes.fs+4−1 client/Keyboard.fs+6 client/Platform/iOS/AppDelegate.fs+22 client/Skrepka.fsproj+1
client/App.fs+12−3
@@ -127,8 +127,9 @@ module App =
127127 PollCursor = 0L
128128 Profile = None
129129 FlushingOutbox = false
130− PollRetries = 0 },
131− [ CmdLoadState ]
130+ PollRetries = 0
131+ KeyboardHeight = 0. },
132+ [ CmdLoadState; CmdSubscribeKeyboard ]
132133
133134 // ── Update ──
134135
@@ -333,6 +334,8 @@ module App =
333334 []
334335 | StateLoaded(None, _, _) -> model, []
335336
337+ | KeyboardHeightChanged h -> { model with KeyboardHeight = h }, []
338+
336339 // ── mapCmd ──
337340
338341 let private log msg = eprintfn $"[skrepka] {msg}"
@@ -594,6 +597,10 @@ module App =
594597
595598 | CmdSaveProfile profile -> Cmd.ofEffect (fun _ -> Store.saveProfile profile |> Async.Start)
596599
600+ | CmdSubscribeKeyboard ->
601+ Cmd.ofEffect (fun dispatch ->
602+ Keyboard.heightChanged.Add(fun h -> dispatch (KeyboardHeightChanged h)))
603+
597604 // ── View ──
598605
599606 let private pageContent model =
@@ -606,7 +613,9 @@ module App =
606613 | AddContact(pk, nn) -> AnyView(viewAddContact model pk nn)
607614 | EditProfile(displayName, bio, photo) -> AnyView(viewEditProfile model displayName bio photo)
608615
609− Border(page).background (SolidColorBrush(Colors.White))
616+ Border(page)
617+ .background(SolidColorBrush(Colors.White))
618+ .padding(Avalonia.Thickness(0., 0., 0., model.KeyboardHeight))
610619
611620 let view model =
612621#if MOBILE
client/AppTypes.fs+4−1
@@ -59,7 +59,8 @@ module AppTypes =
5959 PollCursor: int64
6060 Profile: Profile option
6161 FlushingOutbox: bool
62− PollRetries: int }
62+ PollRetries: int
63+ KeyboardHeight: float }
6364
6465 // ── Msg ──
6566
@@ -83,6 +84,7 @@ module AppTypes =
8384 | StartFlush
8485 | FlushResult of sent: bool
8586 | Search of string
87+ | KeyboardHeightChanged of float
8688
8789 // ── CmdMsg ──
8890
@@ -97,6 +99,7 @@ module AppTypes =
9799 | CmdSaveData of Data
98100 | CmdPickPhoto
99101 | CmdSaveProfile of Profile
102+ | CmdSubscribeKeyboard
100103
101104 // ── Shared Helpers ──
102105
client/Keyboard.fsnew file+6
@@ -0,0 +1,6 @@
1+namespace Skrepka
2+
3+module Keyboard =
4+ let private evt = Event<float>()
5+ let heightChanged = evt.Publish
6+ let triggerHeightChanged h = evt.Trigger h
client/Platform/iOS/AppDelegate.fs+22
@@ -1,5 +1,6 @@
11namespace Skrepka.iOS
22
3+open System
34open Foundation
45open UIKit
56open Avalonia
@@ -17,6 +18,27 @@ type AppDelegate() =
1718 this.Window.BackgroundColor <- UIColor.White)
1819 |> ignore
1920
21+ NSNotificationCenter.DefaultCenter.AddObserver(
22+ UIKeyboard.WillShowNotification,
23+ Action<NSNotification>(fun n ->
24+ let frame =
25+ (n.UserInfo.ObjectForKey(UIKeyboard.FrameEndUserInfoKey) :?> NSValue)
26+ .CGRectValue
27+
28+ let h = float frame.Height
29+
30+ let safeBottom =
31+ let w = UIApplication.SharedApplication.KeyWindow
32+ if isNull w then 0. else float w.SafeAreaInsets.Bottom
33+
34+ Keyboard.triggerHeightChanged (max 0. (h - safeBottom))))
35+ |> ignore
36+
37+ NSNotificationCenter.DefaultCenter.AddObserver(
38+ UIKeyboard.WillHideNotification,
39+ Action<NSNotification>(fun _ -> Keyboard.triggerHeightChanged 0.))
40+ |> ignore
41+
2042 App.create().UseiOS()
2143
2244module Main =
client/Skrepka.fsproj+1
@@ -37,6 +37,7 @@
3737 <Compile Include="TextFields.fs" />
3838 <Compile Include="Buttons.fs" />
3939 <Compile Include="$(iOSPlatformFolder)PhotoPicker.fs" Condition="$(AvaloniaPlatform) == 'iOS'" />
40+ <Compile Include="Keyboard.fs" />
4041 <Compile Include="AppTypes.fs" />
4142 <Compile Include="Protocol.fs" />
4243 <Compile Include="ViewSetup.fs" />