Создание пользовательских ячеек таблицы в Swift
У меня есть пользовательский класс с парой IBOutlets. Я добавил класс в раскадровку. Я подключил все свои розетки. моя функция cellForRowAtIndexPath выглядит так:
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as SwipeableCell
cell.mainTextLabel.text = self.venueService.mainCategoriesArray()[indexPath.row]
return cell
}
Вот мой пользовательский класс ячейки:
class SwipeableCell: UITableViewCell {
@IBOutlet var option1: UIButton
@IBOutlet var option2: UIButton
@IBOutlet var topLayerView : UIView
@IBOutlet var mainTextLabel : UILabel
@IBOutlet var categoryIcon : UIImageView
init(style: UITableViewCellStyle, reuseIdentifier: String!) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
}
Когда я запускаю приложение, все мои ячейки пусты. Я вышел self.venueService.mainCategoriesArray()
и он содержит все правильные строки. Я также попытался поставить фактическую строку равной метке, и это дает тот же результат.
Что мне не хватает? Любая помощь приветствуется.
10 ответов
Спасибо за все различные предложения, но я наконец понял это. Пользовательский класс был настроен правильно. Все, что мне нужно было сделать, - это раскадровка, где я выбираю пользовательский класс: удалите его и выберите снова. Это не имеет особого смысла, но в итоге это сработало для меня.
Пример ячейки пользовательского табличного представления
Протестировано с Xcode 9 и Swift 4
Задатчик оригинального вопроса решил их проблему. Я добавляю этот ответ в качестве самостоятельного примера проекта для тех, кто пытается сделать то же самое.
Готовый проект должен выглядеть так:
Создать новый проект
Это может быть только приложение с одним представлением.
Добавьте код
Добавьте новый файл Swift в свой проект. Назовите это MyCustomCell.swift. Этот класс будет содержать выходы для представлений, которые вы добавляете в свою ячейку в раскадровке.
import UIKit
class MyCustomCell: UITableViewCell {
@IBOutlet weak var myView: UIView!
@IBOutlet weak var myCellLabel: UILabel!
}
Мы подключим эти розетки позже.
Откройте ViewController.swift и убедитесь, что у вас есть следующее содержимое:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// These strings will be the data for the table view cells
let animals: [String] = ["Horse", "Cow", "Camel", "Sheep", "Goat"]
// These are the colors of the square views in our table view cells.
// In a real project you might use UIImages.
let colors = [UIColor.blue, UIColor.yellow, UIColor.magenta, UIColor.red, UIColor.brown]
// Don't forget to enter this in IB also
let cellReuseIdentifier = "cell"
@IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
// number of rows in table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.animals.count
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
cell.myView.backgroundColor = self.colors[indexPath.row]
cell.myCellLabel.text = self.animals[indexPath.row]
return cell
}
// method to run when table view cell is tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped cell number \(indexPath.row).")
}
}
Настройте раскадровку
Добавьте табличное представление к вашему контроллеру представления и используйте автоматическое расположение, чтобы прикрепить его к четырем сторонам View Controller. Затем перетащите ячейку табличного представления на табличное представление. А затем перетащите вид и метку на ячейку прототипа. (Возможно, вам придется выбрать ячейку табличного представления и вручную установить высоту строки на более высокий уровень в Инспекторе размеров, чтобы у вас было больше места для работы.) Используйте автоматическое расположение, чтобы исправить вид и метку так, как вы хотите, чтобы они располагались внутри представление содержимого ячейки табличного представления. Например, я сделал мой вид 100х100.
Другие настройки IB
Имя и идентификатор пользовательского класса
Выберите ячейку табличного представления и установите для пользовательского класса значение MyCustomCell
(имя класса в файле Swift мы добавили). Также установите Идентификатор, чтобы быть cell
(та же строка, которую мы использовали для cellReuseIdentifier
в коде выше.
Подключить розетки
- Перетащите элемент управления из табличного представления в раскадровке на
tableView
переменная вViewController
код. - Сделайте то же самое для вида и метки в ячейке прототипа для
myView
а такжеmyCellLabel
переменные вMyCustomCell
учебный класс.
Законченный
Вот и все. Вы должны быть в состоянии запустить свой проект сейчас.
Заметки
- Цветные виды, которые я здесь использовал, можно заменить на что угодно. Очевидным примером будет
UIImageView
, - Если вы просто пытаетесь заставить TableView работать, посмотрите этот еще более простой пример.
- Если вам нужно табличное представление с переменной высотой ячейки, посмотрите этот пример.
Это для тех, кто работает с кастомными ячейками.xib
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let identifier = "Custom"
var cell: CustomCell! = tableView.dequeueReusableCellWithIdentifier(identifier) as? CustomCel
if cell == nil {
tableView.registerNib(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: identifier)
cell =tableView.dequeueReusableCellWithIdentifier(identifier) as? CustomCell
}return cell}
У меня та же проблема.
Вообще то, что я сделал, то же самое с тобой.
class dynamicCell: UITableViewCell {
@IBOutlet var testLabel : UILabel
init(style: UITableViewCellStyle, reuseIdentifier: String) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
и в методе uitableviewcell:
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell :dynamicCell = tableView.dequeueReusableCellWithIdentifier("cell") as dynamicCell
cell.testLabel.text = "so sad"
println(cell.testLabel)
return cell;
}
и да, табличное представление ничего не показывает! Но угадайте, что это на самом деле что-то показывает... потому что журнал, который я получаю из println(cell.testLabel), показывает, что все метки действительно отображаются.
НО! их кадры странные, которые имеют что-то вроде этого:
кадр = (0 -21; 42 21);
так что он имеет (0,-21) как (x,y), так что это означает, что метка просто появляется где-то за пределами ячейки.
поэтому я пытаюсь добавить настройку кадра вручную следующим образом:
cell.testLabel.frame = CGRectMake (10, 10, 42, 21)
и, к сожалению, это не работает.
--------------- обновление через 10 минут -----------------
Я ЭТО СДЕЛАЛ. так что, похоже, проблема в классах размеров.
Нажмите на файл.storyboard и перейдите на вкладку "Инспектор файлов".
ОТКЛЮЧИТЕ флажок Размер классы
и наконец мой "такой грустный" лейбл вышел!
Последняя обновленная версия с xCode 6.1
class StampInfoTableViewCell: UITableViewCell{
@IBOutlet weak var stampDate: UILabel!
@IBOutlet weak var numberText: UILabel!
override init?(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init(coder aDecoder: NSCoder) {
//fatalError("init(coder:) has not been implemented")
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Детали
- Xcode версии 10.2.1 (10E1001), Swift 5
Решение
import UIKit
// MARK: - IdentifiableCell protocol will generate cell identifire based on the class name
protocol Identifiable: class {}
extension Identifiable { static var identifier: String { return "\(self)"} }
// MARK: - Functions which will use a cell class (conforming Identifiable protocol) to `dequeueReusableCell`
extension UITableView {
typealias IdentifiableCell = UITableViewCell & Identifiable
func register<T: IdentifiableCell>(class: T.Type) { register(T.self, forCellReuseIdentifier: T.identifier) }
func register(classes: [Identifiable.Type]) { classes.forEach { register($0.self, forCellReuseIdentifier: $0.identifier) } }
func dequeueReusableCell<T: IdentifiableCell>(aClass: T.Type, initital closure: ((T) -> Void)?) -> UITableViewCell {
guard let cell = dequeueReusableCell(withIdentifier: T.identifier) as? T else { return UITableViewCell() }
closure?(cell)
return cell
}
func dequeueReusableCell<T: IdentifiableCell>(aClass: T.Type, for indexPath: IndexPath, initital closure: ((T) -> Void)?) -> UITableViewCell {
guard let cell = dequeueReusableCell(withIdentifier: T.identifier, for: indexPath) as? T else { return UITableViewCell() }
closure?(cell)
return cell
}
}
extension Array where Element == UITableViewCell.Type {
var onlyIdentifiables: [Identifiable.Type] { return compactMap { $0 as? Identifiable.Type } }
}
Применение
// Define cells classes
class TableViewCell1: UITableViewCell, Identifiable { /*....*/ }
class TableViewCell2: TableViewCell1 { /*....*/ }
// .....
// Register cells
tableView.register(classes: [TableViewCell1.self, TableViewCell2.self]. onlyIdentifiables)
// Create/Reuse cells
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.row % 2) == 0 {
return tableView.dequeueReusableCell(aClass: TableViewCell1.self, for: indexPath) { cell in
// ....
}
} else {
return tableView.dequeueReusableCell(aClass: TableViewCell2.self, for: indexPath) { cell in
// ...
}
}
}
Полный образец
Не забудьте добавить сюда код решения
import UIKit
class ViewController: UIViewController {
private weak var tableView: UITableView?
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
}
}
// MARK: - Setup(init) subviews
extension ViewController {
private func setupTableView() {
let tableView = UITableView()
view.addSubview(tableView)
self.tableView = tableView
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
tableView.register(classes: [TableViewCell1.self, TableViewCell2.self, TableViewCell3.self].onlyIdentifiables)
tableView.dataSource = self
}
}
// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int { return 1 }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 20 }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch (indexPath.row % 3) {
case 0:
return tableView.dequeueReusableCell(aClass: TableViewCell1.self, for: indexPath) { cell in
cell.textLabel?.text = "\(cell.classForCoder)"
}
case 1:
return tableView.dequeueReusableCell(aClass: TableViewCell2.self, for: indexPath) { cell in
cell.textLabel?.text = "\(cell.classForCoder)"
}
default:
return tableView.dequeueReusableCell(aClass: TableViewCell3.self, for: indexPath) { cell in
cell.textLabel?.text = "\(cell.classForCoder)"
}
}
}
}
Полученные результаты
[1] Сначала создайте ячейку для просмотра таблицы в StoryBoard.
[2] Поместить ниже метод представления таблицы
//MARK: - методы делегата Tableview
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return <“Your Array”>
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
var totalHeight : CGFloat = <cell name>.<label name>.frame.origin.y
totalHeight += UpdateRowHeight(<cell name>.<label name>, textToAdd: <your array>[indexPath.row])
return totalHeight
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell : <cell name>! = tableView.dequeueReusableCellWithIdentifier(“<cell identifire>”, forIndexPath: indexPath) as! CCell_VideoCall
if(cell == nil)
{
cell = NSBundle.mainBundle().loadNibNamed("<cell identifire>", owner: self, options: nil)[0] as! <cell name>;
}
<cell name>.<label name>.text = <your array>[indexPath.row] as? String
return cell as <cell name>
}
//MARK: - Пользовательские методы
func UpdateRowHeight ( ViewToAdd : UILabel , textToAdd : AnyObject ) -> CGFloat{
var actualHeight : CGFloat = ViewToAdd.frame.size.height
if let strName : String? = (textToAdd as? String)
where !strName!.isEmpty
{
actualHeight = heightForView1(strName!, font: ViewToAdd.font, width: ViewToAdd.frame.size.width, DesignTimeHeight: actualHeight )
}
return actualHeight
}
Снимите флажок "Классы размера" и для меня, но вы также можете добавить отсутствующие ограничения в конструкторе интерфейса. Просто используйте встроенную функцию, если вы не хотите добавлять ограничения самостоятельно. Использование ограничений - на мой взгляд - лучший способ, потому что макет не зависит от устройства (iPhone или iPad).
Это чисто быстрое обозначение работает для меня
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cellIdentifier:String = "CustomFields"
var cell:CustomCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? CustomCell
if (cell == nil)
{
var nib:Array = NSBundle.mainBundle().loadNibNamed("CustomCell", owner: self, options: nil)
cell = nib[0] as? CustomCell
}
return cell!
}
Установить тег для просмотра изображений и метки в ячейке
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return self.tableData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("imagedataCell", forIndexPath: indexPath) as! UITableViewCell
let rowData = self.tableData[indexPath.row] as! NSDictionary
let urlString = rowData["artworkUrl60"] as? String
// Create an NSURL instance from the String URL we get from the API
let imgURL = NSURL(string: urlString!)
// Get the formatted price string for display in the subtitle
let formattedPrice = rowData["formattedPrice"] as? String
// Download an NSData representation of the image at the URL
let imgData = NSData(contentsOfURL: imgURL!)
(cell.contentView.viewWithTag(1) as! UIImageView).image = UIImage(data: imgData!)
(cell.contentView.viewWithTag(2) as! UILabel).text = rowData["trackName"] as? String
return cell
}
ИЛИ ЖЕ
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "imagedataCell")
if let rowData: NSDictionary = self.tableData[indexPath.row] as? NSDictionary,
urlString = rowData["artworkUrl60"] as? String,
imgURL = NSURL(string: urlString),
formattedPrice = rowData["formattedPrice"] as? String,
imgData = NSData(contentsOfURL: imgURL),
trackName = rowData["trackName"] as? String {
cell.detailTextLabel?.text = formattedPrice
cell.imageView?.image = UIImage(data: imgData)
cell.textLabel?.text = trackName
}
return cell
}
см. также загрузчик TableImage из github
Фактическая справочная документация Apple является довольно полной
Прокрутите вниз, пока не увидите эту часть