Para inserir uma sugestão sem aplicar o valor do campo, mantenha o preenchimento e o envio separados. No macOS 15 ou posterior, o SwiftUI oferece sugestões de texto nativas; use submitScope() para bloquear manipuladores de envio ancestrais e um botão explícito para a ação final.
Este tutorial cria um pequeno seletor de rótulos no seu próprio app para Mac. Digitar Bl oferece rótulos sintéticos, como Blue Tile. Escolher uma sugestão atualiza o rascunho editável; clicar em Apply Label copia esse rascunho para o valor aplicado. A distinção importa: impedir uma ação do app e consumir um comando da tecla Return são requisitos diferentes.
Escolha a API adequada à interação
O modificador textInputSuggestions(_:) da Apple apresenta opções para um campo de texto. Cada sugestão acionável precisa de textInputCompletion(_:), que fornece a string de substituição. Ambas as APIs estão documentadas para o macOS 15 e versões posteriores. Uma sugestão sem um modificador de conclusão pode aparecer, mas não responde a cliques. Apple: textInputSuggestions(_:), Apple: textInputCompletion(_:)
Para um campo de rótulo curto, substituir o rascunho inteiro é apropriado. A documentação de sugestões da Apple descreve essa substituição de todo o campo. Não presuma que essa API oferece um editor de tokens relativo à posição do cursor ou uma prévia de sufixo em linha; isso exige um projeto de edição mais específico. Apple: textInputSuggestions(_:)
| Seu requisito | Ponto de partida adequado |
|---|---|
| Oferecer rótulos completos no macOS 15 ou posterior | Sugestões nativas de texto do SwiftUI |
| Impedir que o campo acione um manipulador de envio ancestral | submitScope() no campo |
| Usar um layout de sugestões personalizado ou oferecer suporte a uma versão de implantação anterior | Botões de sugestão controlados pelo app que atualizam a associação do rascunho |
| Fazer com que Return aceite um item destacado e consuma esse comando | Um controle AppKit próprio com tratamento de comandos |
| Visualizar um sufixo ou substituir a palavra no ponto de inserção | Conclusão de texto do AppKit e um intervalo de conclusão explícito |
As duas últimas linhas são escolhas de arquitetura derivadas das APIs documentadas de comando e conclusão. Elas são úteis quando a interação exige mais controle do que uma string de substituição e um limite de envio oferecem.
Crie um seletor de rótulos completo em SwiftUI
Crie um projeto de app para macOS no Xcode com SwiftUI, usando um SDK que inclua as APIs do macOS 15. Defina como destino de implantação o macOS 15 ou posterior. Substitua as declarações geradas do app e da visualização de conteúdo pelo arquivo-fonte único a seguir, deixando apenas uma declaração @main do app no destino.
O exemplo mantém draft e appliedLabel separados. A regra local de correspondência é deliberadamente simples: correspondência de prefixo que diferencia maiúsculas de minúsculas, nenhuma sugestão para texto vazio e nenhuma sugestão para correspondência exata. O Swift documenta hasPrefix(_:) como uma comparação de prefixos de strings; regras de correspondência mais abrangentes seriam uma escolha do app. Swift: Strings and Characters
import SwiftUI
@main
struct LabelCompletionApp: App {
var body: some Scene {
WindowGroup {
LabelCompletionView()
}
}
}
struct LabelCompletionView: View {
private let labels = [
"Blue Tile",
"Blue Tray",
"Green Tile",
"Round Tray"
]
@State private var draft = ""
@State private var appliedLabel = ""
@State private var applyCount = 0
@FocusState private var fieldIsFocused: Bool
private var matches: [String] {
guard !draft.isEmpty else { return [] }
return labels.filter {
$0.hasPrefix(draft) && $0 != draft
}
}
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Choose a label")
.font(.headline)
TextField("Type Bl to see suggestions", text: $draft)
.textFieldStyle(.roundedBorder)
.focused($fieldIsFocused)
.textInputSuggestions {
ForEach(matches, id: \.self) { label in
Text(label)
.textInputCompletion(label)
}
}
.submitScope()
Button("Apply Label") {
appliedLabel = draft
applyCount += 1
}
.disabled(draft.isEmpty)
Text("Draft: \(draft)")
Text("Applied: \(appliedLabel)")
Text("Apply count: \(applyCount)")
}
.padding(24)
.frame(width: 420)
.onAppear {
fieldIsFocused = true
}
}
}
Este é um exemplo completo de app, com seus rótulos de demonstração e estado incluídos. Ele foi conferido em relação à documentação da API consultada, mas não foi compilado nem executado aqui porque não havia uma cadeia de ferramentas do Swift nem um ambiente de execução de interface do macOS disponíveis.
Por que selecionar uma sugestão não aplica o rótulo
Cada conclusão fornece uma string para o campo de texto, cuja associação aponta para draft. O único código que atribui appliedLabel ou incrementa applyCount está na ação do botão Apply Label. Portanto, neste exemplo, atualizar o rascunho não tem como acionar essa ação final.
O campo também tem submitScope(). A Apple documenta esse modificador como uma forma de impedir que gatilhos de envio cheguem aos modificadores de envio em níveis superiores da hierarquia de visualizações. Isso importa quando o componente é incorporado posteriormente a um formulário maior com um onSubmit ancestral. Apple: submitScope(_:)
O foco inicial é definido por @FocusState. A Apple documenta que atribuir um valor à propriedade associada move o foco para a visualização correspondente. Aqui, o foco é uma conveniência de edição; ele não controla se o rótulo é aplicado. Apple: FocusState
O que o exemplo permite intencionalmente
A lista de rótulos oferece sugestões em vez de restringir a entrada válida. Digitar Square Tile ainda habilita Apply Label. Se sua interface permitir apenas rótulos listados, altere explicitamente a regra de validação — por exemplo, habilite o botão somente quando o rascunho corresponder a uma entrada do catálogo. Não substitua silenciosamente um rascunho sem correspondência pela primeira sugestão.
O exemplo também substitui o rótulo inteiro. Se o rascunho contiver várias palavras, escolher uma conclusão deve ser entendido como escolher o valor final do campo. Um editor de tokens que precise preservar o texto ao redor requer uma substituição que considere a seleção.
Entenda o que significa “sem enviar”
A documentação do SwiftUI para onSubmit(of:_:) diz que um campo de texto aciona sua ação de envio quando o usuário pressiona Return. Ações de envio podem ser associadas a uma visualização individual ou a uma hierarquia maior. Verifique os dois locais quando uma seleção de preenchimento automático parecer acionar uma ação inesperada do app. Apple: onSubmit(of:_:)
submitScope() estabelece um limite para os manipuladores ancestrais. A documentação não promete que nenhum evento interno de envio ocorra, que a edição permaneça ativa ou que todo comando de teclado seja consumido. Da mesma forma, os modificadores de conclusão da Apple documentam a substituição de texto, não uma supressão universal do comportamento da tecla Return. Tenha esses limites em mente ao escolher a interação. Apple: submitScope(_:), Apple: textInputCompletion(_:)
Em um componente existente, examine separadamente estes caminhos:
- Remova qualquer ação final do app do próprio
onSubmitdo campo se Return nunca deve aplicar seu valor. - Coloque
submitScope()no campo para bloquear uma ação de envio ancestral. - Verifique separadamente os atalhos de teclado dos botões, especialmente uma ação padrão explícita.
- Verifique qualquer observador de alterações ou setter do modelo que execute a ação final sempre que o rascunho mudar.
Uma closure local onSubmit vazia não é a substituição documentada para um limite de envio. A Apple fornece especificamente submitScope() para interromper a propagação. Se o app deve enviar ao pressionar Return somente quando não houver uma conclusão ativa, essa política condicional de teclado exige estado e tratamento de comandos adicionais. Apple: onSubmit(of:_:)
Use AppKit quando Return precisar aceitar e encerrar o comando
Para um NSTextField próprio, o AppKit oferece control(_:textView:doCommandBy:). O argumento de visualização de texto é o editor de campo do controle. O delegado pode tratar um comando associado e retornar true; retornar false deixa o comando sem tratamento por esse delegado. Este é o ponto de extensão documentado para personalizar comandos em um campo de texto. Apple: control(_:textView:doCommandBy:)
Um controlador de conclusão personalizado pode usar esse ponto de extensão com esta sequência:
- Acompanhe se a lista de sugestões está aberta e qual item está destacado.
- Quando o comando Return chegar com um item destacado válido, insira esse item no rascunho.
- Feche a lista de sugestões e sincronize o modelo de rascunho do app.
- Retorne
trueporque o controlador tratou o comando. - Retorne
falsepara comandos que o controlador não trata.
Essa sequência é um projeto de implementação derivado do contrato do delegado, não uma política de preenchimento automático integrada. Decida separadamente o que Return deve fazer quando a lista estiver fechada. Se, em vez disso, você controlar um NSTextView independente, o ponto de extensão correspondente é textView(_:doCommandBy:); a Apple afirma que retornar true impede que a própria visualização de texto execute esse comando. Apple: textView(_:doCommandBy:)
Para a conclusão de texto em si, NSTextView.insertCompletion(_:forPartialWordRange:movement:isFinal:) oferece suporte à substituição e a alterações de seleção. Com isFinal: false, seleciona o sufixo proposto; com isFinal: true, recolhe a seleção após a palavra concluída. O sinalizador descreve o estado da conclusão, não o envio pelo app. O argumento movement identifica como ocorreu a navegação ou seleção da conclusão. Apple: insertCompletion(_:forPartialWordRange:movement:isFinal:)
Preserve o intervalo de texto correto
O rangeForUserCompletion do AppKit identifica a palavra parcial desde o início até o ponto de inserção. Use um intervalo adequado à substituição pretendida e recalcule-o após edições ou movimentos do cursor. Não reutilize cegamente um intervalo capturado anteriormente depois que o texto mudar. Apple: rangeForUserCompletion
Ao converter intervalos de texto, não substitua o comprimento de NSString pela contagem de caracteres do Swift. A documentação do Swift explica que String conta grupos de grafemas estendidos, enquanto o comprimento de NSString conta unidades de código UTF-16. Esses valores podem diferir. A substituição da associação do campo inteiro no exemplo SwiftUI evita completamente essa conversão de intervalo. Swift: Strings and Characters)
Verifique a inserção e a aplicação separadamente
Use o rascunho visível, o valor aplicado e o contador para verificar o resultado em cada versão compatível do macOS. Os itens a seguir são verificações esperadas para o exemplo, não resultados de testes relatados.
| Ação | Resultado observável esperado |
|---|---|
Digitar Bl |
O rascunho é Bl; são oferecidos rótulos correspondentes |
Clicar em Blue Tile |
O rascunho passa a ser Blue Tile; o valor aplicado e a contagem não mudam |
| Pressionar Return durante a edição | O app não aplica o rascunho |
Clicar em Apply Label |
O valor aplicado copia o rascunho; a contagem aumenta uma vez |
| Editar o rascunho depois | O valor aplicado anteriormente continua visível |
| Limpar o rascunho | As sugestões ficam vazias e Apply Label é desabilitado |
Digitar bl |
Não há correspondências segundo a regra do exemplo, que diferencia maiúsculas de minúsculas |
Teste também a seleção por teclado na lista nativa de sugestões e repita as verificações depois de incorporar o campo à visualização pai real. A Apple observa que eventos como mover uma janela do macOS podem fechar a interface nativa de sugestões; por isso, o rascunho deve continuar utilizável sem essa lista. Apple: textInputSuggestions(_:)
Se o destino de implantação for anterior ao macOS 15, forneça uma visualização nativa com disponibilidade condicionada e uma alternativa, ou use botões de sugestão controlados pelo app em todos os casos. Um botão alternativo pode atribuir seu rótulo diretamente ao rascunho e deixar a ação final para Apply Label. Isso oferece a mesma separação entre rascunho e aplicação, enquanto a navegação por teclado e a apresentação ficam a cargo do seu componente.
Para esta tarefa de seleção de rótulos, o limite útil é observável: aceitar uma conclusão altera o rascunho editável, e somente a ação final explícita altera o rótulo aplicado. Use as sugestões nativas do SwiftUI e o escopo de envio para estabelecer esse limite; recorra ao tratamento de comandos do AppKit quando consumir a própria tecla Return fizer parte do requisito.
Fontes
- textInputSuggestions(_:) — Apple
- textInputCompletion(_:) — Apple
- submitScope(_:) — Apple
- onSubmit(of:_:) — Apple
- FocusState — Apple
- control(_:textView:doCommandBy:) — Apple
- textView(_:doCommandBy:) — Apple
- insertCompletion(_:forPartialWordRange:movement:isFinal:) — Apple
- rangeForUserCompletion — Apple
- Strings and Characters — Swift.org