.t9String.contains(t9String) }) } class func findWith(str: String) -> [PhoneContact] { return PhoneContactStore.instance .dataSource.filter({

Como implementar a pesquisa T9 no iOS



Alguns anos atrás, eu estava trabalhando em um aplicativo chamado “ BOG mBank - Mobile Banking ”Com minha equipe iOS / Android. Há um recurso básico no aplicativo onde você pode usar a funcionalidade de mobile banking para recarregar o saldo pós-pago do seu próprio celular ou o saldo do celular de qualquer contato.

Durante o desenvolvimento deste módulo, notamos que era muito mais fácil encontrar um contato específico na versão Android do aplicativo do que na versão iOS. Por quê? A principal razão por trás disso é a pesquisa T9, que está faltando nos dispositivos da Apple.



Vamos explicar do que se trata o T9, por que ele provavelmente não se tornou parte do iOS e como desenvolvedores iOS pode implementá-lo, se necessário.



O que é T9?

T9 é uma tecnologia de texto preditivo para telefones celulares, especificamente aqueles que contêm um teclado numérico 3x4 físico.



Ilustração da pesquisa T9 no teclado numérico

T9 foi originalmente desenvolvido por Tegic Communications , e o nome significa Texto em 9 teclas .



Você pode imaginar por que o T9 provavelmente nunca chegou ao iOS. Durante a revolução do smartphone, a entrada T9 se tornou obsoleta, já que os smartphones modernos contavam com teclados completos, cortesia de suas telas sensíveis ao toque. Como a Apple nunca teve telefones com teclados físicos e não estava no negócio de telefonia durante o apogeu do T9, é compreensível que essa tecnologia tenha sido omitida do iOS.

O T9 ainda é usado em alguns telefones baratos sem tela sensível ao toque (os chamados telefones com recursos). No entanto, apesar do fato de a maioria dos telefones Android nunca apresentar teclados físicos, os dispositivos Android modernos oferecem suporte para entrada T9, que pode ser usada para discar para contatos soletrando o nome do contato que se está tentando ligar.



Um exemplo de entrada preditiva T9 em ação

Em um telefone com teclado numérico, cada vez que uma tecla (1-9) é pressionada (quando em um campo de texto), o algoritmo retorna uma estimativa de quais letras são mais prováveis ​​para as teclas pressionadas até aquele ponto.

Captura de tela do Xcode



Por exemplo, para inserir a palavra “the”, o usuário pressionaria 8, depois 4 e, em seguida, 3, e o visor exibiria “t”, depois “th” e “the”. Se a palavra menos comum 'for' for pretendida (3673), o algoritmo preditivo pode selecionar 'Ford'. Pressionar a tecla “próximo” (normalmente a tecla “*”) pode trazer “dose” e, finalmente, “frente”. Se “anterior” for selecionado, na próxima vez que o usuário pressionar a sequência 3673, será mais provável que seja a primeira palavra exibida. Se a palavra 'Felix' for pretendida, no entanto, ao inserir 33549, o visor mostra 'E', depois 'De', 'Del', 'Deli' e 'Felix'.

Este é um exemplo de mudança de letra ao inserir palavras.



Uso programático de T9 no iOS

Então, vamos mergulhar neste recurso e escrever um exemplo fácil de entrada T9 para iOS. Em primeiro lugar, precisamos criar um novo projeto.

Os pré-requisitos necessários para nosso projeto são básicos: Ferramentas de compilação Xcode e Xcode instaladas em seu Mac.



Para criar um novo projeto, abra seu Aplicativo Xcode em seu Mac e selecione “Criar um novo projeto Xcode”, nomeie seu projeto e escolha o tipo de aplicativo a ser criado. Simplesmente selecione “Single View App” e pressione Next.

Captura de tela do Xcode

Na próxima tela, como você pode ver, haverá algumas informações que você precisa fornecer.

Nota: Se você não tiver uma conta de desenvolvedor, também poderá executá-la no Simulador.

Pressione o botão Avançar e estamos prontos para começar.

Arquitetura Simples

Como você já sabe, ao criar um novo aplicativo, você já tem MainViewController classe e Main.Storyboard. Para fins de teste, é claro, podemos usar este controlador.

Antes de começarmos a projetar algo, vamos primeiro criar todas as classes e arquivos necessários para garantir que tenhamos tudo configurado e funcionando para mover para a parte da IU do trabalho.

Em algum lugar dentro do seu projeto, basta criar um novo arquivo chamado “ PhoneContactsStore.swift ”No meu caso, é assim.

T9 search storboard e arquitetura

Nossa primeira tarefa é criar um mapa com todas as variações de entradas do teclado numérico.

import Contacts import UIKit fileprivate let T9Map = [ ' ' : '0', 'a' : '2', 'b' : '2', 'c' : '2', 'd' : '3', 'e' : '3', 'f' : '3', 'g' : '4', 'h' : '4', 'i' : '4', 'j' : '5', 'k' : '5', 'l' : '5', 'm' : '6', 'n' : '6', 'o' : '6', 'p' : '7', 'q' : '7', 'r' : '7', 's' : '7', 't' : '8', 'u' : '8', 'v' : '8', 'w' : '9', 'x' : '9', 'y' : '9', 'z' : '9', '0' : '0', '1' : '1', '2' : '2', '3' : '3', '4' : '4', '5' : '5', '6' : '6', '7' : '7', '8' : '8', '9' : '9' ]

É isso aí. Implementamos o mapa completo com todas as variações. Agora, vamos continuar criando nossa primeira classe chamada “ PhoneContact . '

Seu arquivo deve ter a seguinte aparência:

texto alt da imagem

Primeiro, nesta classe, precisamos ter certeza de que temos um Filtro Regex de A-Z + 0-9.

private let regex = try! NSRegularExpression(pattern: '[^ a-z()0-9+]', options: .caseInsensitive)

Basicamente, o usuário tem propriedades padrão que precisam ser exibidas:

var firstName : String! var lastName : String! var phoneNumber : String! var t9String : String = '' var image : UIImage? var fullName: String! { get { return String(format: '%@ %@', self.firstName, self.lastName) } }

Certifique-se de que você substituiu hash e isEqual para especificar sua lógica customizada para filtragem de lista.

Além disso, precisamos ter o método replace para evitar qualquer coisa exceto números na string.

override var hash: Int { get { return self.phoneNumber.hash } } override func isEqual(_ object: Any?) -> Bool { if let obj = object as? PhoneContact { return obj.phoneNumber == self.phoneNumber } return false } private func replace(str : String) -> String { let range = NSMakeRange(0, str.count) return self.regex.stringByReplacingMatches(in: str, options: [], range: range, withTemplate: '') }

Agora precisamos de mais um método chamado calculateT9, para localizar contatos relacionados a fullname ou phonenumber.

func calculateT9() { for c in self.replace(str: self.fullName) { t9String.append(T9Map[String(c).localizedLowercase] ?? String(c)) } for c in self.replace(str: self.phoneNumber) { t9String.append(T9Map[String(c).localizedLowercase] ?? String(c)) } }

Depois de implementar o PhoneContact objeto, precisamos armazenar nossos contatos em algum lugar da memória. Para isso, vou criar uma nova classe chamada PhoneContactStore.

Teremos duas propriedades locais:

fileprivate let contactsStore = CNContactStore()

E:

fileprivate lazy var dataSource = Set()

Estou usando Set para garantir que não haja duplicação durante o preenchimento desta fonte de dados.

final class PhoneContactStore { fileprivate let contactsStore = CNContactStore() fileprivate lazy var dataSource = Set() static let instance : PhoneContactStore = { let instance = PhoneContactStore() return instance }() }

Como você pode ver, esta é uma classe Singleton, o que significa que a mantemos na memória até que o aplicativo seja executado. Para obter mais informações sobre Singletons ou padrões de design, você pode ler Aqui .

Estamos agora muito próximos de ter a pesquisa T9 finalizada.

Juntando tudo

Antes de acessar a lista de contatos na Apple, você precisa primeiro pedir permissão.

class func hasAccess() -> Bool { let authorizationStatus = CNContactStore.authorizationStatus(for: CNEntityType.contacts) return authorizationStatus == .authorized } class func requestForAccess(_ completionHandler: @escaping (_ accessGranted: Bool, _ error : CustomError?) -> Void) { let authorizationStatus = CNContactStore.authorizationStatus(for: CNEntityType.contacts) switch authorizationStatus { case .authorized: self.instance.loadAllContacts() completionHandler(true, nil) case .denied, .notDetermined: weak var wSelf = self.instance self.instance.contactsStore.requestAccess(for: CNEntityType.contacts, completionHandler: { (access, accessError) -> Void in var err: CustomError? if let e = accessError { err = CustomError(description: e.localizedDescription, code: 0) } else { wSelf?.loadAllContacts() } completionHandler(access, err) }) default: completionHandler(false, CustomError(description: 'Common Error', code: 100)) } }

Depois de ter autorizado o acesso aos contatos, podemos escrever o método para obter a lista do sistema.

fileprivate func loadAllContacts() { if self.dataSource.count == 0 { let keys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactThumbnailImageDataKey, CNContactPhoneNumbersKey] do { let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor]) request.sortOrder = .givenName request.unifyResults = true if #available(iOS 10.0, *) { request.mutableObjects = false } else {} // Fallback on earlier versions try self.contactsStore.enumerateContacts(with: request, usingBlock: {(contact, ok) in DispatchQueue.main.async { for phone in contact.phoneNumbers { let local = PhoneContact() local.firstName = contact.givenName local.lastName = contact.familyName if let data = contact.thumbnailImageData { local.image = UIImage(data: data) } var phoneNum = phone.value.stringValue let strArr = phoneNum.components(separatedBy: CharacterSet.decimalDigits.inverted) phoneNum = NSArray(array: strArr).componentsJoined(by: '') local.phoneNumber = phoneNum local.calculateT9() self.dataSource.insert(local) } } }) } catch {} } }

Já carregamos a lista de contatos na memória, o que significa que agora podemos escrever um método simples:

  1. findWith - t9String
  2. findWith - str
class func findWith(t9String: String) -> [PhoneContact] { return PhoneContactStore.instance.dataSource.filter({ $0.t9String.contains(t9String) }) } class func findWith(str: String) -> [PhoneContact] { return PhoneContactStore.instance .dataSource.filter({ $0.fullName.lowercased() .contains(str.lowercased()) }) } class func count() -> Int { let request = CNContactFetchRequest(keysToFetch: []) var count = 0; do { try self.instance.contactsStore.enumerateContacts( with: request, usingBlock: {(contact, ok) in count += 1; }) } catch {} return count }

É isso aí. Acabamos.

Agora podemos usar a pesquisa T9 dentro de UIViewController.

fileprivate let cellIdentifier = 'contact_list_cell' final class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! @IBOutlet weak var searchBar: UISearchBar! fileprivate lazy var dataSource = [PhoneContact]() fileprivate var searchString : String? fileprivate var searchInT9 : Bool = true override func viewDidLoad() { super.viewDidLoad() self.tableView.register( UINib( nibName: 'ContactListCell', bundle: nil ), forCellReuseIdentifier: 'ContactListCell' ) self.searchBar.keyboardType = .numberPad PhoneContactStore.requestForAccess { (ok, err) in } } func filter(searchString: String, t9: Bool = true) { } func reloadListSection(section: Int, animation: UITableViewRowAnimation = .none) { } }

Implementação do método de filtro:

func filter(searchString: String, t9: Bool = true) { self.searchString = searchString self.searchInT9 = t9 if let str = self.searchString { if t9 { self.dataSource = PhoneContactStore.findWith(t9String: str) } else { self.dataSource = PhoneContactStore.findWith(str: str) } } else { self.dataSource = [PhoneContact]() } self.reloadListSection(section: 0) }

Implementação do método Reload List:

func reloadListSection(section: Int, animation: UITableViewRowAnimation = .none) { if self.tableView.numberOfSections <= section { self.tableView.beginUpdates() self.tableView.insertSections(IndexSet(integersIn:0..

E aqui está a última parte de nosso breve tutorial, UITableView implementação:

extension ViewController: UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return tableView.dequeueReusableCell(withIdentifier: 'ContactListCell')! } func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.dataSource.count } func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { guard let contactCell = cell as? ContactListCell else { return } let row = self.dataSource[indexPath.row] contactCell.configureCell( fullName: row.fullName, t9String: row.t9String, number: row.phoneNumber, searchStr: searchString, img: row.image, t9Search: self.searchInT9 ) } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 55 } func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { self.filter(searchString: searchText) } }

Empacotando

Isso conclui nosso tutorial de pesquisa T9 e, com sorte, você o achou simples e fácil de implementar no iOS.

Mas por que você deveria? E por que a Apple não incluiu suporte T9 no iOS para começar? Como apontamos na introdução, o T9 dificilmente é um recurso matador nos telefones de hoje - é mais uma reflexão tardia, um retorno aos dias dos telefones 'burros' com teclados numéricos mecânicos.

No entanto, ainda existem alguns motivos válidos pelos quais você deve implementar a pesquisa T9 em determinados cenários, seja por uma questão de consistência ou para melhorar a acessibilidade e a experiência do usuário. Em uma nota mais alegre, se você é do tipo nostálgico, brincar com a entrada T9 pode trazer de volta boas lembranças de seus dias de escola.

Por fim, você pode encontrar o código completo para implementação T9 no iOS em meu Repositório GitHub .

Compreender o básico

O que é texto preditivo?

Texto preditivo é uma tecnologia de entrada em que uma tecla ou botão representa muitas letras, como em teclados numéricos usados ​​em telefones celulares mais antigos. Também é usado para melhorar a acessibilidade em determinados cenários.

Por que T9 é chamado assim?

T9 significa Texto em 9 teclas, pois se baseia em um teclado numérico de 9 dígitos para a entrada de texto.

Como eu uso o T9 no meu teclado?

Aqui está um exemplo rápido. Para 'OLÁ', você só precisa pressionar 4-3-5-5-6. Estes são os números que contêm as letras que soletram 'OLÁ'.

.fullName.lowercased() .contains(str.lowercased()) }) } class func count() -> Int { let request = CNContactFetchRequest(keysToFetch: []) var count = 0; do { try self.instance.contactsStore.enumerateContacts( with: request, usingBlock: {(contact, ok) in count += 1; }) } catch {} return count }

É isso aí. Acabamos.

Agora podemos usar a pesquisa T9 dentro de UIViewController.

fileprivate let cellIdentifier = 'contact_list_cell' final class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! @IBOutlet weak var searchBar: UISearchBar! fileprivate lazy var dataSource = [PhoneContact]() fileprivate var searchString : String? fileprivate var searchInT9 : Bool = true override func viewDidLoad() { super.viewDidLoad() self.tableView.register( UINib( nibName: 'ContactListCell', bundle: nil ), forCellReuseIdentifier: 'ContactListCell' ) self.searchBar.keyboardType = .numberPad PhoneContactStore.requestForAccess { (ok, err) in } } func filter(searchString: String, t9: Bool = true) { } func reloadListSection(section: Int, animation: UITableViewRowAnimation = .none) { } }

Implementação do método de filtro:

func filter(searchString: String, t9: Bool = true) { self.searchString = searchString self.searchInT9 = t9 if let str = self.searchString { if t9 { self.dataSource = PhoneContactStore.findWith(t9String: str) } else { self.dataSource = PhoneContactStore.findWith(str: str) } } else { self.dataSource = [PhoneContact]() } self.reloadListSection(section: 0) }

Implementação do método Reload List:

func reloadListSection(section: Int, animation: UITableViewRowAnimation = .none) { if self.tableView.numberOfSections <= section { self.tableView.beginUpdates() self.tableView.insertSections(IndexSet(integersIn:0..

E aqui está a última parte de nosso breve tutorial, UITableView implementação:

extension ViewController: UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return tableView.dequeueReusableCell(withIdentifier: 'ContactListCell')! } func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.dataSource.count } func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { guard let contactCell = cell as? ContactListCell else { return } let row = self.dataSource[indexPath.row] contactCell.configureCell( fullName: row.fullName, t9String: row.t9String, number: row.phoneNumber, searchStr: searchString, img: row.image, t9Search: self.searchInT9 ) } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 55 } func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { self.filter(searchString: searchText) } }

Empacotando

Isso conclui nosso tutorial de pesquisa T9 e, com sorte, você o achou simples e fácil de implementar no iOS.

Mas por que você deveria? E por que a Apple não incluiu suporte T9 no iOS para começar? Como apontamos na introdução, o T9 dificilmente é um recurso matador nos telefones de hoje - é mais uma reflexão tardia, um retorno aos dias dos telefones 'burros' com teclados numéricos mecânicos.

No entanto, ainda existem alguns motivos válidos pelos quais você deve implementar a pesquisa T9 em determinados cenários, seja por uma questão de consistência ou para melhorar a acessibilidade e a experiência do usuário. Em uma nota mais alegre, se você é do tipo nostálgico, brincar com a entrada T9 pode trazer de volta boas lembranças de seus dias de escola.

Por fim, você pode encontrar o código completo para implementação T9 no iOS em meu Repositório GitHub .

Compreender o básico

O que é texto preditivo?

Texto preditivo é uma tecnologia de entrada em que uma tecla ou botão representa muitas letras, como em teclados numéricos usados ​​em telefones celulares mais antigos. Também é usado para melhorar a acessibilidade em determinados cenários.

Por que T9 é chamado assim?

T9 significa Texto em 9 teclas, pois se baseia em um teclado numérico de 9 dígitos para a entrada de texto.

Como eu uso o T9 no meu teclado?

Aqui está um exemplo rápido. Para 'OLÁ', você só precisa pressionar 4-3-5-5-6. Estes são os números que contêm as letras que soletram 'OLÁ'.