如何在Swift中使UITextField的行为类似于UISearchBar?

11

我正在使用Swift中的UITableView和UISearchBar开发一个新的应用程序,它已经正常运行。但由于最终项目必须具有完全定制的搜索栏,因此我不得不将输入输出口移动到UITextField上,以便可以进行自定义。

问题在于,我无法想象如何编写代码,让UITextField的行为类似于UISearchBar。我不知道如何过滤UITextField的输入并使字符串可用于UISearchBarDelegate方法。

所以有人能帮帮我吗?

编辑:我按照Daniel的帮助编写了这段代码,但返回值为“nil”,无法工作。

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate {

@IBOutlet weak var txtSearchBar: UITextField!
var searchTxt = ""
let prods = ["água", "terra", "ar", "fogo"]
var searchResults:[String] = []

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    
    txtSearchBar.delegate = self
}


func textFieldDidEndEditing(textField: UITextField) {
    searchTxt = textField.text
    println(searchTxt)
    searchResults = prods.filter({(produtos:String) -> Bool in
        
        let nameMatch = produtos.rangeOfString(self.searchTxt, options: NSStringCompareOptions.CaseInsensitiveSearch)
        
        println(nameMatch)
        return nameMatch != nil})
}

我的输入是字母“ar”,但返回了“nil”,这是不应该的,因为数组中有一个对象是“ar”。


这是一个非常广泛的问题。你是在问如何检测用户在文本字段中输入,并对其进行一些操作吗?还是在问如何制作一个范围条?或者其他什么问题? - Aaron Brager
@AaronBrager 我需要的是,当用户在键盘上点击搜索按钮时,表格视图将重新加载与搜索字符串匹配的项目。我想要一个UITextField,它的工作方式就像UISearchBar一样。 - Marco Almeida
5个回答

13
创建 uitextfield 对象:
@IBOutlet var SearchTxt: UITextField!
var search:String=""

@IBOutlet var ListTable: UITableView!
var AllData:Array<Dictionary<String,String>> = []
var SearchData:Array<Dictionary<String,String>> = []

视图加载完成:

override func viewDidLoad()
{
    super.viewDidLoad()

    AllData = [["pic":"list0.jpg", "name":"Angel Mark", "msg":"Hi there, I would like read your...", "time":"just now", "unread":"12"],
               ["pic":"list1.jpg", "name":"John Doe", "msg":"I would prefer reading on night...", "time":"56 second ago", "unread":"2"],
               ["pic":"list2.jpg", "name":"Krishta Hide", "msg":"Okey Great..!", "time":"2m ago", "unread":"0"],
               ["pic":"list3.jpg", "name":"Keithy Pamela", "msg":"I am waiting there", "time":"5h ago", "unread":"0"]
               ]

    SearchData=AllData
}

搜索文本字段代理方法:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
{
    if string.isEmpty
    {
        search = String(search.characters.dropLast())
    }
    else
    {
        search=textField.text!+string
    }

    print(search)
    let predicate=NSPredicate(format: "SELF.name CONTAINS[cd] %@", search)
    let arr=(AllData as NSArray).filtered(using: predicate)

    if arr.count > 0
    {
        SearchData.removeAll(keepingCapacity: true)
        SearchData=arr as! Array<Dictionary<String,String>>
    }
    else
    {
        SearchData=AllData
    }
    ListTable.reloadData()
    return true
}

搜索数据在表视图中展示:

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

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    let cell = tableView.dequeueReusableCell(withIdentifier: "ListCell") as! ListCell

    var Data:Dictionary<String,String> = SearchData[indexPath.row]

    cell.Pic.image=UIImage(named: Data["pic"]!)
    cell.Name.text = Data["name"]
    cell.Msg.text = Data["msg"]
    cell.Time.text = Data["time"]

    cell.selectionStyle = .none
    return cell
}

嗨Jayesh,你的回答真的很好,但是有一个小问题,如果用户按下退格键(X),它会将字符串视为空,因为要更改的新内容是空的,所以这部分search = String(search.characters.dropLast())最好改为search = String(textfield.text.dropLast())。 - IsPha
嗨Jayesh,如何基于名称、消息和时间拥有多个搜索选项? - kiran

3

Xcode 9.2 / Swift 4 -- 工作代码

 @IBOutlet var tfSearchWorker: UITextField!
 @IBOutlet var tblView: UITableView!

 var AllData :Array<Dictionary<String,String>> = []
 var SearchedData:Array<Dictionary<String,String>> = []

@ViewDidLoad

 AllData = [["pic":"list0.jpg", "name":"Angel Mark", "msg":"Hi there, I would like read your...", "time":"just now", "unread":"12"],
           ["pic":"list1.jpg", "name":"John Doe", "msg":"I would prefer reading on night...", "time":"56 second ago", "unread":"2"],
           ["pic":"list2.jpg", "name":"Krishta Hide", "msg":"Okey Great..!", "time":"2m ago", "unread":"0"],
           ["pic":"list3.jpg", "name":"Keithy Pamela", "msg":"I am waiting there", "time":"5h ago", "unread":"0"]
           ]

self.SearchedData = self.AllData
self.tfSearchWorker.addTarget(self, action: #selector(searchWorkersAsPerText(_ :)), for: .editingChanged)

@功能

@objc func searchWorkersAsPerText(_ textfield:UITextField) {
    self.SearchedData.removeAll()
    if textfield.text?.count != 0 {
        for dicData in self.AllData {
            let isMachingWorker : NSString = (dicData.name!) as NSString
            let range = isMachingWorker.lowercased.range(of: textfield.text!, options: NSString.CompareOptions.caseInsensitive, range: nil,   locale: nil)
            if range != nil {
                SearchedData.append(dicData)
            }
        }
    } else {
        self.SearchedData = self.AllData
    }
    self.tblView.reloadData()
}

@UITableView

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

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
     let cell = tableView.dequeueReusableCell(withIdentifier: "ListCell") as! ListCell

     var Data:Dictionary<String,String> = SearchedData[indexPath.row]

     cell.Pic.image=UIImage(named: Data["pic"]!)
     cell.Name.text = Data["name"]
     cell.Msg.text = Data["msg"]
     cell.Time.text = Data["time"]

     cell.selectionStyle = .none
     return cell
}

你为什么要使用Objective-C方法来实现searchWorkersAsPerText函数? - KFDoom

3

您的主要问题似乎是想问如何实现一个与UISearchBar完全相同的类。这将是一项巨大的工作!

然而,在您的笔记中,您只是询问如何在搜索按钮被点击时做出反应。

向您的视图控制器添加一个IBAction和第二个数组。将第二个数组命名为“foundItems”。将IBAction连接到搜索按钮。当调用该操作时,从文本字段中读取文本并基于该文本筛选项目。将符合过滤器的项目放入foundItems数组中,然后在您的表视图上调用reloadData()。

在您的表视图数据源方法中,检查foundItems是否不为空。如果不是,则显示它们而不是您的主要项目数组。

您还需要某种取消按钮。在该按钮的操作中,将foundItems数组设置为nil并在您的表视图上调用reloadData()。


我知道IBAction的东西,但我不知道如何进行过滤。这是我的问题。 - Marco Almeida
那取决于数据的性质。但是Array类型有一个“filter”方法。也许你应该从那里开始。 - Daniel T.

1

在@Daniel T.的帮助下,我用以下代码解决了这个问题:

func textFieldShouldReturn(textField: UITextField) -> Bool {
    isFiltered = true
    searchResults = prods.filter({(coisas:String) -> Bool in
        let stringMatch = coisas.rangeOfString(textField.text)
        return stringMatch != nil

    })
    println(searchResults.description)
    textField.resignFirstResponder()
    table.reloadData()
    return true
}

感谢 @Daniel T.

0

这是如何获取实际更新的字符串。上面的答案中有许多未处理的情况,例如:

  1. 如果您从中间删除任何字符。
  2. 如果您在字符串中删除多个字符。
  3. 如果您在字符串中间添加内容。
  4. 如果您替换子字符串。

在下面的函数中,您将在任何情况下获得更新后的字符串。

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    var search = textField.text!
    if let r = Range(range, in: search){
        search.removeSubrange(r) // it will delete any deleted string.
    }
    search.insert(contentsOf: string, at: search.index(search.startIndex, offsetBy: range.location)) // it will insert any text.
    print(search)
    return true
}

如果您在文本字段内点击叉号按钮,则将调用以下函数

func textFieldShouldClear(_ textField: UITextField) -> Bool {
        print("search text field will be empty here")
        return true
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接