处理 Shift+Enter 换行符,在 TextEditor SwiftUI 中输入发送

问题描述 投票:0回答:2

我正在尝试在文本框中处理换行符的shift + Enter(聊天应用程序例如WhatsApp,Discord等实现)。目前,我正在使用 SwiftUI TextEditor,但它没有任何方法可以像在 AppKit 中那样处理原始键盘事件。因此,一个黑客解决方案是检查消息的最后一个字符是否是 .onchange 中的换行符,然后发送消息。这种方法适用于“输入发送”,但如果按下 Shift 键(对于多行消息),我找不到一种发送消息的方法。

我正在尝试使用

NSViewRepresentable
来使用 AppKit API 的方法,如下所示:

struct KeyEventHandling: NSViewRepresentable {
    class KeyView: NSView {
        override var acceptsFirstResponder: Bool { true }
        
        override func keyDown(with event: NSEvent) {
            print("keydown event")
        }

        override func flagsChanged(with event: NSEvent) {
            switch event.modifierFlags.intersection(.deviceIndependentFlagsMask) {
            case [.shift]:
                print("shift key pressed")
            default:
                print("no modifier keys are pressed")
            }
        }
    }

    func makeNSView(context: Context) -> NSView {
        let view = KeyView()
        DispatchQueue.main.async { // wait till next event cycle
            view.window?.makeFirstResponder(view)
        }
        return view
    }

    func updateNSView(_ nsView: NSView, context: Context) { }
}

然后我将其设置为文本编辑器的覆盖/背景(都尝试过),如下所示:

TextEditor(text: $message)
    .overlay(KeyEventHandling())
    ...more modifiers

但是,它似乎只在文本编辑器未聚焦时处理事件。当 TextEditor(或任何其他元素,似乎)获得焦点时,按 Shift 键不会调用 flagsChanged 覆盖。

除了将整个 TextEditor 重新实现为

NSViewRepresentable
之外,是否有更好的方法来接收修饰键状态更改?谢谢!

macos swiftui appkit
2个回答
2
投票

新行的正确快捷键实际上是 option+return 并且

TextField
支持它,例如

TextField("type something...", text: $text, onEditingChanged: { _ in     
    print("changed") 
}, onCommit: { 
    print("commit") 
})

0
投票
  • 创建一个空的 iOS 应用程序 (iOS 17 +)
  • 将以下代码粘贴到 ContentView 中
  • 在模拟器中运行应用程序,现在切换到任一字段,输入一些文本,然后按 Shift + Enter 将文本换行为多行。
  • 这是一个粗略的剪辑,但你明白了。
import SwiftUI

enum FocusField: Hashable {
    case rowOne
    case rowTwo
}

struct WrappedTextEdit: View {
    @Binding var text: String
    @State var didWrapText = false
    // we need this to avoid loosing focus
    var focusField: FocusField
    var focusState: FocusState<FocusField?>.Binding

    /**
     https://www.avanderlee.com/swiftui/key-press-events-detection/
     https://onmyway133.com/posts/how-to-pass-focusstate-binding-in-swiftui/
     */
    var body: some View {
        TextField("...", text: $text, axis: .vertical)
            .onKeyPress(action: { (keyPress: KeyPress) in
                print("""
                characters: '\(keyPress.characters)', modifiers: '\(keyPress.modifiers)', phase: '\(keyPress.phase)', debugDescription: '\(keyPress.debugDescription)'
                """)

                if keyPress.key == .return && keyPress.modifiers == .shift {
                    print("shift + return")
                    text += "\n"
                    didWrapText = true
                    return .handled
                }

                if keyPress.key == .return {
                    return .ignored
                }

                // CharacterSet.alphanumerics.contains(keyPress.characters)
                text += keyPress.characters
                didWrapText = false
                return .handled
            })
            .focused(focusState, equals: focusField)
            .onChange(of: focusState.wrappedValue, { oldValue, newValue in
                print("didWrapText: '\(didWrapText)'")
                print("isFocused changed, '\(oldValue)' -> '\(newValue)'")
                if didWrapText {
                    DispatchQueue.main.async {
                        focusState.wrappedValue = focusField
                    }
                }
            })
    }
}

struct ContentView: View {
    @State var row1: String = "row one"
    @State var row2: String = "row two"
    @FocusState var focusState: FocusField?

    var body: some View {
        VStack {
            Spacer()
            HStack {
                WrappedTextEdit(text: $row1, focusField: .rowOne, focusState: $focusState)
                    .textFieldStyle(.roundedBorder)
                    .padding(5)
                Spacer()
            }
            HStack {
                WrappedTextEdit(text: $row2, focusField: .rowTwo, focusState: $focusState)
                    .textFieldStyle(.roundedBorder)
                    .padding(5)
                Spacer()
            }
            Spacer()
        }
        .onAppear(perform: {
            focusState = .rowOne
        })
        .padding()
    }
}

#Preview {
    ContentView()
}
© www.soinside.com 2019 - 2024. All rights reserved.