Основы UITableView
UITableView — это компонент интерфейса в виде одноколоночной таблицы, преимущественно служащий для отображения и редактирования списка данных.
При создании ему задается один из двух стилей: Plain или Grouped. Разница в представлении заголовков и нижних колонтитулов секций.
Все строки (ячейки) UITableView являются экземплярами своего класса UITableViewCell, и при инициализации также стилизуются: Default, Value1 (Right Detail), Value2 (Left Detail), Subtitle. Каждому стилю присущ свой набор областей для текста и изображений.
В соответствии с парадигмой 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)
}
}
}
Кнопка удаления строки открывается свайпом влево.
Готовый проект доступен по ссылке: TableView.zip
Чтобы загрузка файлов не блокировалась политикой безопасности Xcode, нужно в Info.plist добавить группу App Transport Security Settings со свойством Allow Arbitrary Loads = YES.