此用例中的UILocalizedIndexedCollat​​ion

时间:2018-08-04 14:17:12

标签: swift tableview

如何在此用例中向UITableView添加节标题索引列表

@IBOutlet var tableView: UITableView!

var detail: Detail? = nil
var list = [tabledata]()
let search = UISearchController(searchResultsController: nil)

override func viewDidLoad() {

    super.viewDidLoad()
    list = [

        tabledata(name:"something".localized, sort:"sort.something".localized, id:"something.html"),
        tabledata(name:"somethingelse".localized, sort:"sort.somethingelse".localized, id:"somethingelse.html"),
        ...

    ]

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "library", for: indexPath)
    var data: tabledata
    data = list[indexPath.row]
    cell.textLabel!.text = data.name
    return cell

}

现在的关键是要转换表数据。

请注意

  • name:是实际的单元格名称,将是 .localized
  • sort:必须帮助像单元格名称中的á é等字符正确排序(避免在字母末尾显示)
  • id:调用html文件位置以显示在detailViewController中(“原因是必须翻译名称,我们希望在此处输入静态文本)

节标题和索引列表的常规实现将导致类似

T                 // section header
translation       // cell names
transmission
...

T                 // table in
Übersetzung       // another language
Getriebe
...
  

UILocalizedIndexedCollat​​ion的正确模型是什么?


.xcodeproj 在我的github上。按需提供更多信息。

感谢帮助!

1 个答案:

答案 0 :(得分:2)

编辑:事实证明,“考虑到每一行的第一个字母用作索引”要比考虑多种语言(尤其是非拉丁语言)时所想的要复杂得多。我正在使用UILocalizedIndexedCollation来简化此任务。


我认为UILocalizedIndexedCollation比滚动自己的数据模型更令人困惑。您需要2个模型:一个代表行,另一个代表节:

// UILocalizedIndexedCollation uses a selector on the `name` property
// so we have to turn this data type in to a class.
class TableRow: NSObject {
    @objc var name: String
    var id: String
    var sectionTitle = ""

    init(name: String, id: String) {
        self.name = name
        self.id = id
    }
}

// TableSection does not have to be a class but we are making it so
// that it's consistent with `TableRow`
class TableSection: NSObject {
    var title: String
    var rows: [TableRow]

    init(title: String, rows: [TableRow]) {
        self.title = title
        self.rows = rows
    }
}

之后,填充和过滤表非常简单:

class Library: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet var tableView: UITableView!

    var detail: Detail? = nil
    var list = [TableSection]()
    var filter = [TableSection]()
    let search = UISearchController(searchResultsController: nil)
    let collation = UILocalizedIndexedCollation.current()

    override func viewDidLoad() {

        super.viewDidLoad()

        // search
        search.searchResultsUpdater = self as UISearchResultsUpdating
        search.obscuresBackgroundDuringPresentation = false
        search.searchBar.placeholder = "search".localized
        navigationItem.hidesSearchBarWhenScrolling = false
        navigationItem.searchController = search
        definesPresentationContext = true

        // Set the color of the index on the right of the table.
        // It's settable from Interface Builder as well
        tableView.sectionIndexColor = UIColor(red: 0, green: 122.0 / 255.0, blue: 1, alpha: 1)

        // I took the liberty to add a few more items to the array
        let rows = ["something", "somethingelse", "apple", "orange", "apricot", "strawberry"].map {
            TableRow(name: $0.localized, id: $0)
        }
        list = organizeIntoSections(rows: rows)
        tableView.reloadData()
    }

    // Organize rows into sections with titles
    func organizeIntoSections(rows: [TableRow]) -> [TableSection] {
        // Organize the rows into sections based on their `name` property
        let selector: Selector = #selector(getter: TableRow.name)

        // Sort the rows by `name`
        let sortedRows = collation.sortedArray(from: rows, collationStringSelector: selector) as! [TableRow]

        // Allocate rows into sections
        var sections = collation.sectionTitles.map { TableSection(title: $0, rows: []) }
        for row in sortedRows {
            let sectionNumber = collation.section(for: row, collationStringSelector: selector)
            sections[sectionNumber].rows.append(row)
        }

        // Remove empty sections
        sections.removeAll(where: { $0.rows.isEmpty })
        return sections
    }


    override func viewWillAppear(_ animated: Bool) {
        if let selection = tableView.indexPathForSelectedRow {
            tableView.deselectRow(at: selection, animated: animated)
        }
        super.viewWillAppear(animated)
    }

    // MARK: - Table View
    func numberOfSections(in tableView: UITableView) -> Int {
        return filtering() ? filter.count : list.count
    }

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        // If a section has no row, don't show its header
        let data = filtering() ? filter[section] : list[section]
        return data.rows.isEmpty ? nil : data.title
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return filtering() ? filter[section].rows.count : list[section].rows.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "library", for: indexPath)
        let data = filtering() ? filter[indexPath.section].rows[indexPath.row]
                               : list[indexPath.section].rows[indexPath.row]
        cell.textLabel!.text = data.name
        return cell
    }

    func sectionIndexTitles(for tableView: UITableView) -> [String]? {
        return filtering() ? filter.map { $0.title } : list.map { $0.title }
    }

    // MARK: - Segues
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "transporter" {
            if let indexPath = tableView.indexPathForSelectedRow {
                let selected = filtering() ? filter[indexPath.section].rows[indexPath.row]
                                           : list[indexPath.section].rows[indexPath.row]
                let controller = (segue.destination as! Detail)

                // This assumes you change `controller.result` to have type TableRow
                controller.result = selected
            }
        }
    }

    // search filter
    func filterContent(_ searchText: String) {
        let query = searchText.lowercased()

        filter = list.compactMap { section in
            let matchingRows = section.rows.filter { $0.name.lowercased().contains(query) }
            return matchingRows.isEmpty ? nil : TableSection(title: section.title, rows: matchingRows)
        }
        tableView.reloadData()
    }

    func searchEmpty() -> Bool {
        return search.searchBar.text?.isEmpty ?? true
    }

    func filtering() -> Bool {
        return search.isActive && (!searchEmpty())
    }
}

结果:

英语:

English

意大利语:

Italian

斯洛伐克语

Slovak

(我从Google翻译获得翻译,所以如果有任何疑问,我会提前道歉-我不会说意大利语或斯洛伐克语)