Основы UITableView

UITableView — это компонент интерфейса в виде одноколоночной таблицы, преимущественно служащий для отображения и редактирования списка данных.

При создании ему задается один из двух стилей: Plain или Grouped. Разница в представлении заголовков и нижних колонтитулов секций.

Table view plain style Table view grouped style

Все строки (ячейки) UITableView являются экземплярами своего класса UITableViewCell, и при инициализации также стилизуются: Default, Value1 (Right Detail), Value2 (Left Detail), Subtitle. Каждому стилю присущ свой набор областей для текста и изображений.

Table view cell default style Table view cell right detail style Table view cell left detail style Table view cell subtitle style

В соответствии с парадигмой MVC, графический интерфейс должен быть отделен от данных и логики, поэтому UITableView делегирует эти роли объектам dataSource и delegate, отвечающим протоколам UITableViewDataSource и UITableViewDelegate соответственно. Первый служит для задания порядка и количества секций и строк, возможностей их редактирования; второй — для настройки внешнего вида, функций выделения, перетаскивания и прочего.

Рассмотрим все это на конкретном примере. Создадим приложение, структурированно отображающее информацию из XML-файла.

import UIKit

class ViewController: UIViewController,
                      NSXMLParserDelegate,
                      UITableViewDelegate,
                      UITableViewDataSource {
    /*
    map.xml format:

    <sitemap>
        <article>
          <title>Задание атрибутов в Interface Builder</title>
          <url>http://valery.bashkatov.org/paper/setting-attributes-in-interface-builder</url>
          <date>2015-12-28</date>
        </article>
        
        <article>
          <title>Изображение в качестве цвета</title>
          <url>http://valery.bashkatov.org/paper/image-as-color</url>
          <date>2015-12-23</date>
        </article>
    </sitemap>
    */
    struct Article {
        var title: String
        var url: String
        var date: String
    }

    class UITableViewSubtitleCell: UITableViewCell {
        override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
            super.init(style: .Subtitle, reuseIdentifier: reuseIdentifier)
        }

        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
    }

    var articles = [Article]()
    var xmlParser: NSXMLParser!
    var xmlCurrentElement = ""
    var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Create table view with grouped style
        tableView = UITableView(frame: CGRect(x: 0, y: 0, width: 0, height: 0), style: .Grouped)

        // Register UITableViewSubtitleCell class
        tableView.registerClass(UITableViewSubtitleCell.self, forCellReuseIdentifier: "cell")

        // Set delegate for UITableViewDelegate methods
        tableView.delegate = self

        // And data source for UITableViewDataSource methods
        tableView.dataSource = self

        view.addSubview(tableView)

        // Add constraints
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor).active = true
        tableView.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true
        tableView.widthAnchor.constraintEqualToAnchor(view.widthAnchor).active = true
        tableView.heightAnchor.constraintEqualToAnchor(view.heightAnchor).active = true

        // Create XML parser object
        let url = NSURL(string: "http://valery.bashkatov.org/files/uitableview-basics/map.xml")!
        xmlParser = NSXMLParser(contentsOfURL: url)!

        // For NSXMLParserDelegate methods
        xmlParser.delegate = self

        xmlParser.parse()
    }

    // Hide status bar
    override func prefersStatusBarHidden() -> Bool {
        return true
    }

    // MARK: - NSXMLParserDelegate methods
    // Called when detects a new tag
    func parser(parser: NSXMLParser,
                didStartElement elementName: String,
                namespaceURI: String?,
                qualifiedName qName: String?,
                attributes attributeDict: [String : String]) {

        xmlCurrentElement = elementName

        if elementName == "article" {
            articles.append(Article(title: "", url: "", date: ""))
        }
    }

    // Called when the text found inside tag
    func parser(parser: NSXMLParser, foundCharacters string: String) {

        // Since the text can come in parts, collect it
        switch xmlCurrentElement {
        case "title": articles[articles.count - 1].title += string
        case "url": articles[articles.count - 1].url += string
        case "date": articles[articles.count - 1].date += string
        default: return
        }
    }

    // Called when xml document was successfully parsed
    func parserDidEndDocument(parser: NSXMLParser) {
        tableView.reloadData()
    }

    // MARK: - UITableViewDataSource methods
    // Create and fill cells
    func tableView(tableView: UITableView,
                   cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
        let row = indexPath.row

        // Title as link
        cell.textLabel!.attributedText = NSAttributedString(string: articles[row].title,
                                  attributes: [NSLinkAttributeName: articles[row].url])

        cell.detailTextLabel!.text = "Дата публикации: \(articles[row].date)"

        return cell
    }

    // Set cells count
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return articles.count
    }

    // Set section title
    func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return "Записи"
    }

    // Logic on row deleting
    func tableView(tableView: UITableView,
                   commitEditingStyle editingStyle: UITableViewCellEditingStyle,
                   forRowAtIndexPath indexPath: NSIndexPath) {

        if editingStyle == .Delete {
            articles.removeAtIndex(indexPath.row)
            tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
        }
    }
}

Кнопка удаления строки открывается свайпом влево.

Table view xml sample Table view xml delete sample

Готовый проект доступен по ссылке: TableView.zip

Чтобы загрузка файлов не блокировалась политикой безопасности Xcode, нужно в Info.plist добавить группу App Transport Security Settings со свойством Allow Arbitrary Loads = YES.

Allow arbitrary loads attribute