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

Kako implementirati iskanje T9 v iOS

Pred nekaj leti sem delal na aplikaciji, imenovani » BOG mBank - mobilno bančništvo ”Z mojo iOS / Android ekipo. V aplikaciji je osnovna funkcija, kjer lahko s funkcijo mobilnega bančništva dopolnite lastno postpaid stanje na mobilnem telefonu ali stanje na mobilnem telefonu katerega koli stika.

Med razvojem tega modula smo opazili, da je bilo veliko lažje najti določen stik v različici aplikacije za Android kot v iOS. Zakaj? Ključni razlog za to je iskanje T9, ki v napravah Apple manjka.



Pojasnimo, kaj sploh je T9 in zakaj verjetno ni postal del sistema iOS in kako iOS razvijalci ga lahko po potrebi izvede.



Kaj je T9?

T9 je predvidevalna besedilna tehnologija za mobilne telefone, zlasti tiste, ki vsebujejo fizično številsko tipkovnico 3x4.

Prikaz iskanja T9 na numerični tipkovnici



T9 je prvotno razvil Tegic Communications , in ime pomeni Besedilo na 9 tipkah .

Ugibate lahko, zakaj T9 verjetno nikoli ni prišel do iOS-a. Med revolucijo pametnih telefonov je vnos T9 zastarel, saj so se sodobni telefoni s pametnimi telefoni zanašali na polne tipkovnice, zahvaljujoč zaslonom na dotik. Ker Apple nikoli ni imel nobenega telefona s fizičnimi tipkovnicami in v času razcveta T9 ni bil v telefonskem poslu, je razumljivo, da je bila ta tehnologija izpuščena iz iOS-a.

T9 se še vedno uporablja na nekaterih poceni telefonih brez zaslona na dotik (tako imenovani funkcijski telefoni). Kljub temu, da večina telefonov Android nikoli ni imela fizičnih tipkovnic, sodobne naprave Android podpirajo vhod T9, s katerim lahko pokličete stike z črkovanjem imena stika, ki ga poskuša poklicati.



Primer napovednega vnosa T9 v akciji

Na telefonu s številsko tipkovnico algoritem vsakič, ko pritisnete tipko (1-9) (ko je v besedilnem polju), vrne ugibanje, katere črke so najverjetneje pritisnjene na tipke do te točke.

Posnetek zaslona Xcode

Na primer, za vnos besede »the« uporabnik pritisne 8, nato 4, nato 3, na zaslonu pa se prikaže »t«, nato »th« in nato »the«. Če je predvidena manj pogosta beseda 'fore' (3673), lahko predvidevalni algoritem izbere 'Ford'. S pritiskom na tipko 'next' (običajno tipka '*') se lahko prikaže 'odmerek' in na koncu 'fore'. Če je izbrano »fore«, bo naslednjič, ko bo uporabnik pritisnil zaporedje 3673, bolj verjetno, da je prva prikazana beseda fore. Če je beseda 'Felix' namenjena, pa se pri vnosu 33549 na zaslonu prikaže 'E', nato 'De', 'Del', 'Deli' in 'Felix'.

To je primer spremembe črke med vnašanjem besed.

Programska uporaba T9 v iOS-u

Poglejmo si torej to funkcijo in napišite preprost primer vnosa T9 za iOS. Najprej moramo ustvariti nov projekt.

Predpogoji, potrebni za naš projekt, so osnovni: Orodja za gradnjo Xcode in Xcode, nameščena na vašem Macu.

Če želite ustvariti nov projekt, odprite svoj Aplikacija Xcode na vašem Macu in izberite »Ustvari nov projekt Xcode«, nato poimenujte svoj projekt in izberite vrsto aplikacije, ki jo želite ustvariti. Preprosto izberite “Single View App” in pritisnite Next.

Posnetek zaslona Xcode

Kot vidite, bo na naslednjem zaslonu nekaj informacij, ki jih morate vnesti.

Opomba: Če nimate računa razvijalca, ga lahko zaženete tudi v Simulatorju.

Pritisnite gumb Naprej in že smo pripravljeni na začetek.

Preprosta arhitektura

Kot že veste, ko ustvarite novo aplikacijo, že imate MainViewController razred in Main.Storyboard. Za namene testiranja lahko seveda uporabimo ta krmilnik.

Preden začnemo nekaj načrtovati, najprej ustvarimo vse potrebne razrede in datoteke, da zagotovimo, da imamo vse nastavljeno in zagnano, da se premaknemo na del uporabniškega vmesnika.

Nekje v svojem projektu preprosto ustvarite novo datoteko z imenom » PhoneContactsStore.swift ”V mojem primeru je videti takole.

Iskanje po stordih T9 in arhitektura

Naše prvo poslovno področje je ustvariti zemljevid z vsemi različicami številskih vhodov na tipkovnici.

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' ]

To je to. Izvedli smo celoten zemljevid z vsemi različicami. Zdaj pa nadaljujmo z ustvarjanjem našega prvega razreda, imenovanega » PhoneContact . '

Vaša datoteka naj bo videti tako:

besedilo alt alt slike

Najprej moramo v tem razredu poskrbeti, da imamo Regex filter od A-Z + 0-9.

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

V bistvu ima uporabnik privzete lastnosti, ki jih je treba prikazati:

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) } }

Prepričajte se, da ste preglasili hash in isEqual da določite logiko po meri za filtriranje seznamov.

Prav tako moramo imeti metodo replace, da v nizu ne bo ničesar razen številk.

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: '') }

Zdaj potrebujemo še eno metodo, imenovano calculateT9, za iskanje stikov, povezanih z fullname ali 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)) } }

Po izvedbi PhoneContact predmeta, moramo svoje stike shraniti nekje v spomin. V ta namen bom ustvaril nov razred z imenom PhoneContactStore.

Imeli bomo dve lokalni nepremičnini:

fileprivate let contactsStore = CNContactStore()

In:

fileprivate lazy var dataSource = Set()

Uporabljam Set da se prepričate, da med izpolnjevanjem tega vira podatkov ni podvajanja.

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

Kot lahko vidite, gre za razred Singleton, kar pomeni, da ga hranimo v spominu, dokler se aplikacija ne zažene. Za več informacij o Singletonih ali vzorcih oblikovanja lahko preberete tukaj .

Zdaj smo zelo blizu zaključku iskanja T9.

Vse skupaj

Preden dostopate do seznama stikov v podjetju Apple, morate najprej vprašati za dovoljenje.

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)) } }

Ko smo pooblaščeni za dostop do stikov, lahko napišemo metodo za pridobitev seznama iz 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 {} } }

Seznam stikov smo že naložili v pomnilnik, kar pomeni, da lahko zdaj napišemo preprosto metodo:

  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 }

To je to. Končali smo.

Zdaj lahko uporabimo iskanje po T9 znotraj 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) { } }

Izvajanje metode filtriranja:

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) }

Izvedba metode osveževanja seznama:

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

In tu je zadnji del našega kratkega vodiča, UITableView izvedba:

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) } }

Zavijanje

S tem smo zaključili našo vadnico za iskanje T9 in upamo, da ste jo našli enostavno in enostavno za uporabo v iOS-u.

Zakaj pa bi? In zakaj Apple za začetek ni vključil podpore T9 v iOS? Kot smo poudarili že v uvodu, T9 na današnjih telefonih skorajda ni ubijalska lastnost - gre bolj za naknadno premišljevanje, vrnitev v čas »neumnih« telefonov z mehanskimi številskimi ploščicami.

Vendar obstaja še nekaj tehtnih razlogov, zakaj bi morali v določenih scenarijih izvajati iskanje T9 bodisi zaradi doslednosti bodisi za izboljšanje dostopnosti in uporabniške izkušnje. Če ste bolj nostalgični, vam lahko igranje z vnosom T9 obudi lepe spomine na šolske dni.

Na koncu lahko celotno kodo za implementacijo T9 v iOS najdete na mojem Repo za GitHub .

Razumevanje osnov

Kaj je napovedno besedilo?

Predvidevanje besedila je tehnologija vnosa, pri kateri ena tipka ali gumb predstavlja veliko črk, na primer na številskih tipkovnicah, ki se uporabljajo na starejših mobilnih telefonih. Uporablja se tudi za izboljšanje dostopnosti v nekaterih scenarijih.

Zakaj se T9 tako imenuje?

T9 pomeni Besedilo na 9 tipkah, saj se za vnos besedila opira na 9-mestno številsko tipkovnico.

Kako uporabljam T9 na tipkovnici?

Tu je hiter primer. Za 'HELLO' bi morali pritisniti samo 4-3-5-5-6. To so številke, ki vsebujejo črke, ki pišejo 'HELLO'.

.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 }

To je to. Končali smo.

Zdaj lahko uporabimo iskanje po T9 znotraj 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) { } }

Izvajanje metode filtriranja:

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) }

Izvedba metode osveževanja seznama:

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

In tu je zadnji del našega kratkega vodiča, UITableView izvedba:

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) } }

Zavijanje

S tem smo zaključili našo vadnico za iskanje T9 in upamo, da ste jo našli enostavno in enostavno za uporabo v iOS-u.

Zakaj pa bi? In zakaj Apple za začetek ni vključil podpore T9 v iOS? Kot smo poudarili že v uvodu, T9 na današnjih telefonih skorajda ni ubijalska lastnost - gre bolj za naknadno premišljevanje, vrnitev v čas »neumnih« telefonov z mehanskimi številskimi ploščicami.

Vendar obstaja še nekaj tehtnih razlogov, zakaj bi morali v določenih scenarijih izvajati iskanje T9 bodisi zaradi doslednosti bodisi za izboljšanje dostopnosti in uporabniške izkušnje. Če ste bolj nostalgični, vam lahko igranje z vnosom T9 obudi lepe spomine na šolske dni.

Na koncu lahko celotno kodo za implementacijo T9 v iOS najdete na mojem Repo za GitHub .

Razumevanje osnov

Kaj je napovedno besedilo?

Predvidevanje besedila je tehnologija vnosa, pri kateri ena tipka ali gumb predstavlja veliko črk, na primer na številskih tipkovnicah, ki se uporabljajo na starejših mobilnih telefonih. Uporablja se tudi za izboljšanje dostopnosti v nekaterih scenarijih.

Zakaj se T9 tako imenuje?

T9 pomeni Besedilo na 9 tipkah, saj se za vnos besedila opira na 9-mestno številsko tipkovnico.

Kako uporabljam T9 na tipkovnici?

Tu je hiter primer. Za 'HELLO' bi morali pritisniti samo 4-3-5-5-6. To so številke, ki vsebujejo črke, ki pišejo 'HELLO'.