@@ -11,7 +11,8 @@ struct ComposeScreen: View {
1111 @FocusState private var cwKeyboardFocused : Bool
1212 @StateObject private var presenter : KotlinPresenter < ComposeState >
1313 @State private var viewModel = ComposeInputViewModel ( )
14- @State private var uiTextView : UITextField ?
14+ @State private var uiTextView : UITextView ?
15+ @State private var pendingCursor : Int ?
1516
1617 var body : some View {
1718 VStack (
@@ -82,15 +83,14 @@ struct ComposeScreen: View {
8283 TextField ( text: $viewModel. text, axis: . vertical) {
8384 Text ( " compose_placeholder " )
8485 }
85- . introspect ( . textField, on: . iOS( . v13 , . v14 , . v15 , . v16, . v17, . v18, . v26) ) { textField in
86+ . introspect ( . textField( axis : . vertical ) , on: . iOS( . v16, . v17, . v18, . v26) ) { textField in
8687 self . uiTextView = textField
88+ applyCursorIfPossible ( )
8789 }
8890 . textFieldStyle ( . plain)
8991 . focused ( $keyboardFocused)
9092 . onAppear {
91- DispatchQueue . main. asyncAfter ( deadline: . now( ) + 0.1 ) {
92- keyboardFocused = true
93- }
93+ keyboardFocused = true
9494 }
9595 Spacer ( )
9696 if viewModel. mediaViewModel. items. count > 0 {
@@ -265,6 +265,7 @@ struct ComposeScreen: View {
265265 . popover ( isPresented: $viewModel. showEmoji) {
266266 EmojiPopup ( data: emojis) { item in
267267 viewModel. addEmoji ( emoji: item)
268+ insert ( item. insertText)
268269 }
269270 }
270271 }
@@ -290,15 +291,10 @@ struct ComposeScreen: View {
290291 . onChange ( of: presenter. state. initialTextState) { oldValue, newValue in
291292 if case . success( let initialText) = onEnum ( of: newValue) {
292293 viewModel. text = initialText. data. text
294+ pendingCursor = Int ( initialText. data. cursorPosition)
295+ applyCursorIfPossible ( )
293296 }
294297 }
295- . onChange ( of: uiTextView, { oldValue, newValue in
296- if case . success( let initialText) = onEnum ( of: presenter. state. initialTextState) ,
297- let textField = newValue,
298- let newPosition = textField. position ( from: textField. beginningOfDocument, offset: Int ( initialText. data. cursorPosition) ) {
299- textField. selectedTextRange = textField. textRange ( from: newPosition, to: newPosition)
300- }
301- } )
302298 . toolbarTitleDisplayMode ( . inline)
303299 . toolbar {
304300 ToolbarItem ( placement: . principal) {
@@ -334,6 +330,35 @@ struct ComposeScreen: View {
334330 }
335331 }
336332 }
333+
334+ private func applyCursorIfPossible( ) {
335+ guard let textView = uiTextView, textView. isFirstResponder, let pendingCursor else { return }
336+ let length = textView. text. count
337+ let clamped = NSRange ( location: max ( 0 , min ( pendingCursor, length) ) ,
338+ length: 0 )
339+ textView. selectedRange = clamped
340+ textView. scrollRangeToVisible ( clamped)
341+ self . pendingCursor = nil
342+ }
343+
344+ private func insert( _ s: String ) {
345+ guard let textView = uiTextView else { return }
346+
347+ if textView. markedTextRange != nil { return }
348+
349+ let sel = textView. selectedRange
350+ let current = textView. text ?? " "
351+ let ns = current as NSString
352+ let newText = ns. replacingCharacters ( in: sel, with: s)
353+
354+ textView. text = newText
355+ viewModel. text = newText
356+
357+ let newLocation = sel. location + ( s as NSString ) . length
358+ textView. selectedRange = NSRange ( location: newLocation, length: 0 )
359+ textView. scrollRangeToVisible ( NSRange ( location: max ( 0 , newLocation - 1 ) , length: 1 ) )
360+ }
361+
337362}
338363
339364struct EmojiPopup : View {
0 commit comments