【问题标题】:Getting autocomplete to work in swift让自动完成功能快速工作
【发布时间】:2015-01-09 05:09:02
【问题描述】:

我正在尝试实现自动补全,但找不到适用于 Swift 的示例。下面,我试图转换 Ray Wenderlich's autocompletion tutorial 和 2010 年的示例代码。最后,代码编译,但包含可能完成的表格没有出现,我没有经验知道为什么它没有被 shouldChangeCharactersInRange 隐藏.

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {

@IBOutlet weak var textField: UITextField!
let autocompleteTableView = UITableView(frame: CGRectMake(0,80,320,120), style: UITableViewStyle.Plain)

var pastUrls = ["Men", "Women", "Cats", "Dogs", "Children"]
var autocompleteUrls = [String]()

override func viewDidLoad() {
    super.viewDidLoad()

    autocompleteTableView.delegate = self
    autocompleteTableView.dataSource = self
    autocompleteTableView.scrollEnabled = true
    autocompleteTableView.hidden = true
}

func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool
{
    autocompleteTableView.hidden = false
    var substring = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)

    searchAutocompleteEntriesWithSubstring(substring)
    return true     // not sure about this - could be false
}

func searchAutocompleteEntriesWithSubstring(substring: String)
{
    autocompleteUrls.removeAll(keepCapacity: false)
    var indexOfPastUrls = 0

    for curString in pastUrls
    {
        let substringRange = curString.rangeOfString(curString)

        if (indexOfPastUrls  == 0)
        {
            autocompleteUrls.append(curString)
        }
        indexOfPastUrls = indexOfPastUrls + 1
    }
    autocompleteTableView.reloadData()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return autocompleteUrls.count
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let autoCompleteRowIdentifier = "AutoCompleteRowIdentifier"
    var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier, forIndexPath: indexPath) as UITableViewCell
    let index = indexPath.row as Int

    cell.textLabel.text = autocompleteUrls[index]
    return cell
}

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    let selectedCell : UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
    textField.text = selectedCell.textLabel.text        
}
}

【问题讨论】:

  • substringRange 类型是 NSRange 而不是 let ,之后你必须检查 substringRange.location==0 而不是 autocompleteUrls.append(curString)
  • 是的 - 我引入了 indexOfPastUrls 而不是 substringRange.location==0,因为我不知道如何使 substringRange 类型为 NSRange。我试过指定 let substringRange: NSRange 并转换等式的另一边。
  • @DrWhat..我如何以及何时调用“func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool"” 方法?
  • @dhaval shah - 这是一个委托方法,所以我的理解是它在输入时自动调用,就在字符显示之前。
  • 好的,谢谢回复

标签: ios swift autocomplete


【解决方案1】:

将您的searchAutocompleteEntriesWithSubstring 函数内容替换为以下内容。希望对你有帮助。

func searchAutocompleteEntriesWithSubstring(substring: String)
{
    autocompleteUrls.removeAll(keepCapacity: false)

    for curString in pastUrls
    {
        var myString:NSString! = curString as NSString

        var substringRange :NSRange! = myString.rangeOfString(substring)

        if (substringRange.location  == 0)
        {
            autocompleteUrls.append(curString)
        }
    }

    autocompleteTableView.reloadData()
}

【讨论】:

  • 是的!这看起来好多了。因此,在 Swift 中 myString.rangeOfString(substring) 与 myString: NSString 和 substring: String 一起工作。我不知道你是怎么想出来的——但是谢谢你!!!如果我获得足够的声誉,我会对此表示赞同。不幸的是,我的自动完成表仍然没有出现,所以我希望你不介意我留下这个问题。
  • 我最终自己回答了这个问题,所以我感谢您完成了困难的部分。再次感谢。
  • 感谢简单而精彩的解决方案...!它就像一个魅力..当 tableview 有多个部分并且每个部分有多个行时,我该如何实现这一点......?
【解决方案2】:

表格没有出现,因为 UITextField 委托不是在 viewDidLoad 中的 self。最后一个问题是表格没有显示自动完成结果,但这也得到了修复。 Ray Wenderlich 的基本 Objective-C 自动补全教程转换为 Swift:

class ViewController: UIViewController, UITextFieldDelegate, UITableViewDelegate,   UITableViewDataSource {

@IBOutlet weak var textField: UITextField!
@IBOutlet var autocompleteTableView: UITableView!

// @IBOutlet weak var autocompleteTableView = UITableView(frame: CGRectMake(0,80,320,120), style: UITableViewStyle.Plain)

var pastUrls = ["Men", "Women", "Cats", "Dogs", "Children"]
var autocompleteUrls = [String]()

override func viewDidLoad() {
    super.viewDidLoad()

    textField.delegate = self

    autocompleteTableView!.delegate = self
    autocompleteTableView!.dataSource = self
    autocompleteTableView!.scrollEnabled = true
    autocompleteTableView!.hidden = true
}

func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool
{
    println("banana")
    autocompleteTableView!.hidden = false
    var substring = (self.textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)

    searchAutocompleteEntriesWithSubstring(substring)
    return true
}

func searchAutocompleteEntriesWithSubstring(substring: String)
{
    autocompleteUrls.removeAll(keepCapacity: false)
    println(substring)

    for curString in pastUrls
    {
        println(curString)
        var myString: NSString! = curString as NSString
        var substringRange: NSRange! = myString.rangeOfString(substring)
        if (substringRange.location == 0)
        {
            autocompleteUrls.append(curString)
        }
    }

    autocompleteTableView!.reloadData()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return autocompleteUrls.count
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
    let autoCompleteRowIdentifier = "AutoCompleteRowIdentifier"
    var cell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier) as? UITableViewCell

    if let tempo1 = cell
    {
        let index = indexPath.row as Int
        cell!.textLabel.text = autocompleteUrls[index]
    } else
    {
        cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: autoCompleteRowIdentifier)
    }
    return cell!
}

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    let selectedCell : UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
    textField.text = selectedCell.textLabel.text
}

}

【讨论】:

  • 我收到一个错误:在展开 autocompleteTableView!.delegate = self 时发现 nil。您是否使用 Interface Builder 创建了 autocompleteTableView?
【解决方案3】:

这是一种添加多个标签的方法,该方法基于像 twitter 一样输入的“#”。

变量 typedSubstring 是全局子字符串。

  func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {

autocompleteTableView!.hidden = false
var changedText = (self.textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
var items = changedText.componentsSeparatedByString("#")

if (items.count > 0) {
  typedSubstring  = "#" + items.lastObject as NSString

  self.searchAutocompleteEntriesWithSubstring(typedSubstring)
}

return true

}

改进了 DrWhat 的解决方案,这样当您选择一个单元格时,它会在用户已经输入的位置正确地附加它。

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) 
let selectedCell : UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!

let selectedText = selectedCell.textLabel?.text as String!

// Remove what has been typed already
let trimmedString = selectedText.stringByReplacingOccurrencesOfString(typedSubstring, withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)

var currentTextField = textField.text

// then append to what has been typed
textField.text = currentTextField + trimmedString

【讨论】:

    【解决方案4】:

    已针对 iOS 9.0 和 Swift 2 修复:

    import UIKit
    
    class UIAutoCompleteTextField: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
        @IBOutlet weak
        var textField: UITextField!
            let autocompleteTableView = UITableView(frame: CGRectMake(0, 80, 320, 120), style: UITableViewStyle.Plain)
    
        var pastUrls = ["Men", "Women", "Cats", "Dogs", "Children"]
        var autocompleteUrls = [String]()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            autocompleteTableView.delegate = self
            autocompleteTableView.dataSource = self
            autocompleteTableView.scrollEnabled = true
            autocompleteTableView.hidden = true
        }
    
        func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) - > Bool {
            autocompleteTableView.hidden = false
            let substring = (textField.text!as NSString).stringByReplacingCharactersInRange(range, withString: string)
    
            searchAutocompleteEntriesWithSubstring(substring)
            return true // not sure about this - could be false
        }
    
        func searchAutocompleteEntriesWithSubstring(substring: String) {
            autocompleteUrls.removeAll(keepCapacity: false)
    
            for curString in pastUrls {
                let myString: NSString! = curString as NSString
    
                let substringRange: NSRange! = myString.rangeOfString(substring)
    
                if (substringRange.location == 0) {
                    autocompleteUrls.append(curString)
                }
            }
    
            autocompleteTableView.reloadData()
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
        }
    
        func tableView(tableView: UITableView, numberOfRowsInSection section: Int) - > Int {
            return autocompleteUrls.count
        }
    
        func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) - > UITableViewCell {
    
            let autoCompleteRowIdentifier = "AutoCompleteRowIdentifier"
            let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier, forIndexPath: indexPath) as UITableViewCell
            let index = indexPath.row as Int
    
            cell.textLabel!.text = autocompleteUrls[index]
            return cell
        }
    
        func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
            let selectedCell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath) !
                textField.text = selectedCell.textLabel!.text
        }
    }
    

    【讨论】:

    • 如果没有在任何其他地方完成,我会将 textField.delegate 分配给 viewDidLoad 中的 self。为了其他人的利益,我还要指出,上面的代码假定了一个现有的单元原型。如果您想使用默认单元格运行它,我将在 cellForRowAtIndexPath 方法中使用以下内容: cell = UITableViewCell(style: UITableViewCellStyle.Default , reuseIdentifier: cellIdentifier)
    • dane,感谢这段代码,非常有用。仅供参考,使用界面生成器添加一个表格视图并将其连接到名为 autocompleteTableView 的 IBOutlet 会容易得多。
    • 根据@LNI 评论,我直接在界面生成器中的文本字段下添加了一个tableview,并将其连接到一个名为autocompleteTableView 的IBOutlet。这允许我为原型单元格设置重用标识符以及表格视图本身的自动布局约束。这种方法适用于 Xcode 7.2 和 Swift 2.1。
    【解决方案5】:

    对于未来可能会使用 Swift 2 处理自动完成 texfield 的人,@dane 提供的代码运行良好。但你必须改变这一行:

    let cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier, forIndexPath: indexPath) as UITableViewCell
    

    由

    let cell = UITableViewCell(style: UITableViewCellStyle.Default , reuseIdentifier: cellIdentifier)
    

    另外,您可能会注意到它区分大小写,并且如果您默认输入小写字符串(例如猫),则它不起作用。因此,要解决此问题,您可以替换将选项“CaseSensitiveSearch”添加到 substringRange 声明(在 func searchAutocompleteEntriesWithSubstring 中)。它应该看起来像:

    let substringRange :NSRange! = myString.rangeOfString(substring,options [.CaseInsensitiveSearch])
    

    希望它可以帮助您节省一天!

    【讨论】:

    • 在 Swift 2.1.1 中,我必须调整使 @edess 示例中的搜索不区分大小写的行的语法才能编译。新的语法是这样的: let substringRange: NSRange! = myString.rangeOfString(substring, options: .CaseInsensitiveSearch)
    • 使用let cell = UITableViewCell(style: UITableViewCellStyle.Default , reuseIdentifier: cellIdentifier) 每次都会创建一个新单元格,这会降低应用程序的速度并占用大量内存。最好使用@dane的方法,但是用这个方法向tableView注册重用标识符:func registerClass(_ cellClass: AnyClass?, forCellReuseIdentifier identifier: String)
    【解决方案6】:

    得到它与下面的工作。大写/小写最初把它扔掉了。我正在使用它来自动完成国家/地区名称...

    import UIKit
    
    class billingAddressViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
    
    
        @IBOutlet var countryTextField: UITextField!
        @IBOutlet var countryTableView: UITableView!
    
        var autocompleteCountries = [String]()
    
        // Get list of countries
        let countries = NSLocale.ISOCountryCodes().map { (code:String) -> String in
            let id = NSLocale.localeIdentifierFromComponents([NSLocaleCountryCode: code])
            return NSLocale(localeIdentifier: "en_US").displayNameForKey(NSLocaleIdentifier, value: id) ?? "Country not found for code: \(code)"
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            countryTextField.delegate = self
    
            countryTableView!.delegate = self
            countryTableView!.dataSource = self
            countryTableView!.scrollEnabled = true
            countryTableView!.hidden = true
        }
    
        func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    
            print("text field has changed")
            countryTableView!.hidden = false
    
            let substring = (self.countryTextField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
            print(substring)
            searchAutocompleteEntriesWithSubstring(substring)
            return true
        }
    
        func searchAutocompleteEntriesWithSubstring(substring: String) {
            autocompleteCountries.removeAll(keepCapacity: false)
            print(substring)
    
            for curString in countries {
                //print(curString)
                let myString: NSString! = curString.lowercaseString as NSString
                let substringRange: NSRange! = myString.rangeOfString(substring.lowercaseString)
                if (substringRange.location == 0) {
                    autocompleteCountries.append(curString)
                }
            }
    
            countryTableView!.reloadData()
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
    
        func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return autocompleteCountries.count
        }
    
        func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
            let autoCompleteRowIdentifier = "AutoCompleteRowIdentifier"
            var cell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier) as UITableViewCell!
    
            if let tempo1 = cell {
                let index = indexPath.row as Int
                cell!.textLabel!.text = autocompleteCountries[index]
            }
    
            else {
                cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: autoCompleteRowIdentifier)
            }
    
            return cell!
        }
    
        func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
            let selectedCell : UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
            countryTextField.text = selectedCell.textLabel!.text
            countryTableView.hidden = true
        }
    
    }
    

    【讨论】:

      【解决方案7】:

      我整理了一个教程,其中包含有关如何重新创建这个已有 6 年历史的教程的图片

      matthewhsingleton.com/coding-with-a-rubber-ducky/2016/5/26/… – RubberDucky4444

      【讨论】:

        【解决方案8】:

        添加了没有情节提要的表格视图

        class ViewController: UIViewController  , UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
        
        
        @IBOutlet weak var textField: UITextField!
        
        var autocompleteTableView: UITableView!
        var pastUrls = ["Men", "Women", "Cats", "Dogs", "Children","aaaaaaaaa","aaaaaaaaaaaaaaaaaaa","aaaaaaaaa","a","aa","aaa"]
        var autocompleteUrls = [String]()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
        
            autocompleteTableView = UITableView(frame: CGRectMake(self.textField.bounds.minX,self.textField.bounds.maxY,self.textField.bounds.width,self.textField.bounds.height * 4), style: UITableViewStyle.Plain)
        
            textField.delegate = self
        
            autocompleteTableView.delegate = self
            autocompleteTableView.dataSource = self
            autocompleteTableView.scrollEnabled = true
            autocompleteTableView.hidden = false
        
            autocompleteTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
            self.view.addSubview(autocompleteTableView)
        }
        
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
        
        
        func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool
        {
            autocompleteTableView.hidden = false
            var substring = (self.textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
        
            searchAutocompleteEntriesWithSubstring(substring)
            return true      // not sure about this - could be false
        }
        
        func searchAutocompleteEntriesWithSubstring(substring: String)
        {
            autocompleteUrls.removeAll(keepCapacity: false)
        
            for curString in pastUrls
            {
                var myString:NSString! = curString as NSString
        
                var substringRange :NSRange! = myString.rangeOfString(substring)
        
                if (substringRange.location  == 0)
                {
                    autocompleteUrls.append(curString)
                }
            }
        
            autocompleteTableView.reloadData()
            //autocompleteTableView.hidden = false
        }
        
        func numberOfSectionsInTableView(tableView: UITableView) -> Int {
            return 1
        }
        func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return autocompleteUrls.count
        }
        
        func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        
                    let autoCompleteRowIdentifier = "cell"
                    var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier, forIndexPath: indexPath) as UITableViewCell
                    let index = indexPath.row as Int
        
                    cell.textLabel!.text = autocompleteUrls[index]
        
        
        
                    return cell
        
        
        
        
        }
        
        func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
            let selectedCell : UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
            textField.text = self.autocompleteUrls[indexPath.row]
        
            self.autocompleteTableView.hidden = true
        }
        
        
        
        
        
        
        
        
        
        
        
         }
        

        【讨论】:

        • 大家好,我正在使用搜索 API
        【解决方案9】:

        感谢这篇文章对我有帮助,以防万一你们在 swift 3 中使用 google Places API,并且您需要不区分大小写,这里是您只需输入的更新代码:

        let subStringRange : NSRange! = myString.range(of: substring, options: .caseInsensitive)
        

        【讨论】:

          【解决方案10】:

          用以下函数替换cellForRowAtIndexPath

          func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
          
              var cell = UITableViewCell()
              var data = autocompleteUrls[indexPath.row]
              cell.textLabel?.text = data as String
              return cell
          }
          

          【讨论】:

            【解决方案11】:

            这里有一个不错的库来简化这项工作:https://github.com/apasccon/SearchTextField

            【讨论】:

            • 虽然理论上这可以回答这个问题,it would be preferable 在这里包含答案的基本部分,并提供链接以供参考。
            【解决方案12】:

            添加view.addSubview(autocompleteTableView) 在你的viewdidload。它会起作用的。

            【讨论】:

            • 您好,欢迎来到 Stack Overflow,感谢您的第一个回答。为了使答案对其他人更有用,最佳做法是使您的答案更详细,例如包括一个代码示例,该示例显示在何处添加您确切建议的代码。还要用文字注释您的答案,解释为什么它解决了 OP 的原始问题。
            猜你喜欢
            • 2019-09-26
            • 2010-12-18
            • 1970-01-01
            • 2012-05-09
            • 2015-11-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多