I have a custom class that extends UIViewController and contains a table view, with an array that stores data that populates the table view. For my custom cells, I have a button that when pressed, should remove that cell from the table view. However, I haven't been able to find a way to remove the data from the array in the table view controller and reload the data from the cell's class. Some of the answers I've seen on similar posts suggest notifications or delegation, but due to the structure of my app (tab controller) and that I already use notifications for another feature, the former is inefficient, and I don't know how to use the latter in this situation.
This is the code for the custom cell class:
class CustomCell: UITableViewCell {
#IBOutlet var label: UILabel!
#IBOutlet var removeButton: UIButton!
#IBAction func remove(sender: AnyObject) {
// don't know what to put here
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
And here is the table view controller class:
class CustomController: UIViewController, UITableViewDataSource {
#IBOutlet var table: UITableView!
var data: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
table.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: CustomCell = table.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath) as! CustomCell
// put content in
return cell
}
}
The array data is what I want to access from remove in CustomCell. The closest I've gotten is using self.superview.superview.superview in CustomCell, which returns the view that CustomController controls. However, I have no way to get an instance of CustomController without instantiating a new one. How do I modify a variable in CustomController from the CustomCell class?
In your Custom Cell add this
class CustomCell: UITableViewCell {
var customControllerReference: CustomController?
}
And in your Custom Controller in cellForRowAtIndexPath add this
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: CustomCell = table.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath) as! CustomCell
cell.customControllerReference = self
return cell
}
Related
I have a strange issue with attempting to get my custom table view cell to display. It seems logically correct. It follows the Apple Documentation logic and about every site's exact logic, yet it won't display. Is there something I', missing?
class FavTableCell: UITableViewCell{
#IBOutlet weak var Testo : UILabel!
#IBOutlet weak var TestoBackgoundImage : UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
}
override func prepareForReuse() {
super.prepareForReuse()
// any extra code or logic here
}
}
class TableViewClass : UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UITableViewDelegate, UITableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
TableView.delegate = self
TableView.dataSource = self
TableView.register(CustomTableCell.self, forCellReuseIdentifier: "Table")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let TableCell = tableView.dequeueReusableCell(withIdentifier: "Table", for: indexPath) as! CustomTableCell
TableCell.TestoBackgoundImage?.image = Image Literal// image literal here
TableCell.Testo?.text = self.TableArray[indexPath.row]
return TableCell
}
}
If you have created it as a prototype cell ( design is inside the table itself ) then remove
tableView.register(CustomTableCell.self, forCellReuseIdentifier: "Table")
if it's an xib then do
tableView.register(UINib(nibName: "CustomTableCell", bundle: nil), forCellReuseIdentifier: "Table")
I have a TableView with a custom cell that requires rather lengthy configuration and is used more than once in my app. I would like to avoid duplicated code and just configure the cell in one place. Can I create a function like this?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "betterPostCell", for: indexPath) as! BetterPostCell
return configureCell(cell)
}
Ideally, I would be able to put configureCell in my BetterPostCell class. Is this possible?
Yes, you can do it, and it's a nice way to keep your table view code from blowing up, especially if you have many different types of cells in one table view.
In your BetterPostCell class, create a method called configure like so:
func configure() {
//configure your cell
}
Then in your cellForRowAt method, just call that method from your cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "betterPostCell", for: indexPath) as! BetterPostCell
cell.configure()
return cell
}
You can create a protocol with a configure function and associated type Cell. Using protocol extensions, you can add default implementations for different cell types, and additional methods.
protocol CellConfigurable {
associatedType Cell
func configure(_ cell: Cell)
}
extension CellConfigurable where Cell == SomeTableViewCell {
func configure(_ cell: SomeTableViewCell) {
...
}
}
try this code to create CustomCell:-
class CustomCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.initViews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.perform(#selector(self.initViews), with: self, afterDelay: 0)
}
//MARK: Init views
func initViews() {
//Add your code
}
//MARK: Layout subviews
override func layoutSubviews() {
super.layoutSubviews()
// Here you can code for layout subviews
}
//MARK: Update all valuesw model
func updateWithModel(_ model: AnyObject) {
//here you can update values for cell
}
}
//Call CustomCell in Tableview class
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! CustomCell
cell.updateWithModel(items[indexPath.row])
return cell
}
I am working on a todo list app that works with a table view in Swift. The table view has a UILabel called titleLabelOutlet. When I was building the app I got an error that says The titleLabelOutlet outlet from the FirstViewController to the UILabel is invalid. Outlets cannot be connected to repeating content.I have never received this error before. How do I fix it? This is my code:
import UIKit
var phoneNumberList = [String]()
var titleFieldList = [String]()
class FirstViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var titleLabelOutlet: UILabel!
#IBOutlet weak var phoneNumberLabelOutlet: UILabel!
#IBOutlet weak var myTableView: UITableView!
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return (phoneNumberList.count)
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
phoneNumberLabelOutlet.text = phoneNumberList[indexPath.row]
titleLabelOutlet.text = titleFieldList[indexPath.row]
return(cell)
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
{
if editingStyle == UITableViewCellEditingStyle.delete
{
phoneNumberList.remove(at: indexPath.row)
titleFieldList.remove(at: indexPath.row)
myTableView.reloadData()
}
}
override func viewDidAppear(_ animated: Bool) {
myTableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Your outlets should be in cell class instead of viewcontroller because labels are inside cell
And in crllfor row at delegate you have to get your outlets like that
`let cell = tableView.dequeueReusableCell(withReuseIdentifier: cellIdentifier", for: indexPath) as! TableViewCellClass
cell.lbloutlet.text = "any text"
return cell
`
Create CustomTableCell subclassing UITableViewCell.
Have Labels in cell and connect to phoneNumberLabelOutlet & titleLabelOutlet in CustomTableCell class.
use CustomTableCell in place UITableViewCell for cellForRowAtIndex function.
now you can set values for labels as
cell.phoneNumberLabelOutlet.text = phoneNumberList[indexPath.row]
cell.titleLabelOutlet.text = titleFieldList[indexPath.row]
I have a UIViewController in which I've embedded UITableView. Because I don't want the UIViewController to get too heavy I separated the UITableViewDataSource and UITableViewDelegate
class ViewController: UIViewController {
#IBOutlet var tableView: UITableView!
var dataSource : UITableViewDataSource!
var tableDelegate: UITableViewDelegate!
override func viewDidLoad() {
super.viewDidLoad()
dataSource = TableViewDataSource()
tableDelegate = TableViewDelegate()
tableView.dataSource = dataSource
tableView.delegate = tableDelegate
// Do any additional setup after loading the view, typically from a nib.
}
}
class TableViewDataSource : NSObject, UITableViewDataSource {
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "MyCellIdentifier"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)
cell.textLabel?.text = "hello"
cell.detailTextLabel?.text = "world"
return cell
}
}
class TableViewDelegate : NSObject, UITableViewDelegate {
//custom code here if required
}
In my Storyboard, I've created a prototype cell within the UITableView with the identifier
MyCellIdentifier
I use this identifier to create a cell in my UITableViewDataSource delegate method.
However, if I start the app, only the text of the left label is displayed. The detail label is not visible.
I looked into the debugger and noticed that detailLabel text is correct. The text of the right label is really "world". However, the label is not visible.
I've done a little bit of research and there has been a similar problem in the past. Whenever the text of the detailLabel was set to nil, it was not visible
However in my example, the text is not nil, it is set to "Detail":
How can I make the right label visible?
if (cell != nil)
{
cell = UITableViewCell(style: UITableViewCellStyle.Subtitle,
reuseIdentifier: reuseIdentifier)
}
add this code under let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) this line of your code.
I created a custom class for a cell in my program. When I try to use it in another class to create a cell I keep getting the error Cannot invoke 'init' with an argument list of type '(style: UITableViewCellStyle, reuseIdentifier: StringLiteralConvertible)'. Can anybody point me in the right direction here? I would really appreciate any help. I tried changing the class to inherit form UITableViewController so I can use this var cell: bookCell = self.tableView.dequeueReusableCellWithIdentifier("cell1") as bookCell but it crashes the program if I try to make the class inherit from tableviewcontroller.
import UIKit
class bookCell: UITableViewCell {
#IBOutlet var bookImage: UIImageView!
#IBOutlet var bookDescription: UILabel!
#IBOutlet var bookPosterUsername: UILabel!
}
import UIKit
class SubjectBooksViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var navigationBarTitle: UINavigationBar!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationBarTitle.topItem?.title = "\(selectedCourse)"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell : bookCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "subjectCell")
//var cell: bookCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "subjectCell")
//var cell: bookCell = self.tableView.dequeueReusableCellWithIdentifier("cell1") as bookCell
return cell
}
}
Update code:
import UIKit
class SubjectBooksViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var navigationBarTitle: UINavigationBar!
#IBOutlet var myTableView: UITableView! //Outlet for your table View
override func viewDidLoad() {
super.viewDidLoad()
self.myTableView.dataSource = self //If you have not done in IB
self.myTableView.registerNib(yourCellNib, forCellReuseIdentifier: "subjectCell")
self.navigationBarTitle.topItem?.title = "\(selectedCourse)"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell : bookCell = tableView.dequeueReusableCellWithIdentifier("subjectCell", forIndexPath: indexPath)
return cell
}
}
In viewDidLoad() in line :
self.myTableView.registerNib(yourCellNib, forCellReuseIdentifier: "subjectCell")
replace yourCellNib with loaded nib file for your custom cell.
Registering your nib file is required if you plan to reuse cell in your table view. It is always a good idea to reuse cells.
You need to override this function and get your custom cell in this manner:
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell = tableView.dequeueReusableCellWithIdentifier(
"subjectCell",
forIndexPath: indexPath) as bookCell
Your class name really should be BookCell though, with an uppercase "B". Just to keep with existing standards