How can I use a custom initializer on a UITableViewCell? - ios

I have a custom UITableViewCell and I'd like to use it in my table view. Here's my code for the cell:
class ReflectionCell: UITableViewCell {
#IBOutlet weak var header: UILabel!
#IBOutlet weak var content: UILabel!
#IBOutlet weak var author: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
init(data: Reflection) {
self.header.text = data.title
self.content.text = data.content
self.author.text = data.author.name
super.init(style: UITableViewCellStyle.default, reuseIdentifier: "reflectionCell")
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
I have a model class Reflection that I'd like to initialize the cell with. However, in my view controller I need to use tableView.dequeueReusableCell(withIdentifier: "reflectionCell", for: indexPath). Is there any way for me to use a custom initializer like the one I made?

If you use dequeueReusableCell you cannot change which initializer method that is called. But you can write your own method to update the IBOutlets which you then call after you successfully dequeue a cell.
class ReflectionCell: UITableViewCell {
#IBOutlet weak var header: UILabel!
#IBOutlet weak var content: UILabel!
#IBOutlet weak var author: UILabel!
func update(for reflection: Reflection) {
header.text = reflection.title
content.text = reflection.content
author.text = reflection.author.name
}
}
And
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath) as! ReflectionCell
cell.update(for: reflections[indexPath.row])
return cell
}

Related

Having trouble connecting my CustomCell.xib to my tableView in the storyboard

I Have to do an app for recipes and it shows me different recipes in my tableView, and i just want to implement my CustomCell (from a xib file) to my storyboard and I don't know how to connect it to show my data (I already checked my identifier) here's the code of my controller :
class SearchRecipe: UIViewController, ShowAlert {
var recipeData = RecipeDataModel()
var recipe = [String]()
#IBOutlet weak var tableViewSearch: UITableView!
override func viewDidLoad() {
self.tableViewSearch.rowHeight = 130
}
func updateRecipeData(json: JSON){
if let ingredients = json["hits"][0]["recipe"]["ingredientLines"].arrayObject{
recipeData.ingredientsOfRecipe = ingredients[0] as! String
recipeData.cookingTime = json["hits"][0]["recipe"]["totalTime"].stringValue
recipeData.recipe = json["hits"][0]["recipe"]["label"].stringValue
recipeData.recipeImage = json["hits"][0]["recipe"]["image"].stringValue
}
else {
print("Problem")
}
//self.tableViewSearch.reloadData()
}
}
extension SearchRecipe: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return recipe.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customRecipeCell", for: indexPath) as! CustomRecipeCell
getRecipesDisplay(in: cell, from: recipeData , at: indexPath)
return cell
}
func getRecipesDisplay(in cell: CustomRecipeCell, from recipeModel: RecipeDataModel, at indexPath: IndexPath){
cell.recipeTitle.text = recipeData.recipe
cell.recipeInfos.text = recipeData.ingredientsOfRecipe
cell.timerLabel.text = recipeData.cookingTime
}
}
and this is the code my xib file :
class CustomRecipeCell: UITableViewCell {
#IBOutlet weak var recipeTitle: UILabel!
#IBOutlet weak var recipeInfos: UILabel!
#IBOutlet weak var cellImageBackground: UIImageView!
#IBOutlet weak var likeAndTimerView: UIView!
#IBOutlet weak var likeImage: UIImageView!
#IBOutlet weak var timerImage: UIImageView!
#IBOutlet weak var likeLabel: UILabel!
#IBOutlet weak var timerLabel: UILabel!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func awakeFromNib() {
super.awakeFromNib()
activityIndicator.isHidden = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
In ViewDidLoad, you must register your cell this way:
override func viewDidLoad() {
tableViewSearch.register(UINib(nibName:" /* NAME OF YOUR XIB FILE */ ", bundle: nil), forCellReuseIdentifier: "customRecipeCell")
}
You also have to edit the size of your cell in the attribute inspector of your custom cell and of your table view

tableview not reading label

I am trying to update the cells on a table view but the cell is not reading the text I am passing and returning nil.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! MyCell
let item = self.items[indexPath.row]
myCell.nameLabel?.text = item as? String
print( self.items[indexPath.row])
myCell.myTableViewController = self
return myCell
}
class MyCell: UITableViewCell {
var myTableViewController: ChangeFeedViewController!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#IBOutlet weak var nameLabel: UILabel! = {
let label = UILabel()
label.text = "Any Label"
return label
}()
#IBOutlet weak var actionButton: UIButton! = {
let button = UIButton()
button.setTitle("Action", for: .normal)
return button
}()
#IBAction func buttonHandleAction(_ sender: Any) {
handelAction()
}
func handelAction(){
myTableViewController.deleteCell(cell: self)
}
}
You need to modify your outlet like this and connect to your interface builder of MyCell class.
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var actionButton: UIButton!

iOS - should the UIView or the UIViewController handle the state to UI mapping?

I've read a few posts but I could not find a clear answer to this question: in the MVC framework, who should be responsible for the UI set up? I'm balancing between two options:
class ViewController: UITableViewController {
private var users: [User]
...
override func tableView(
_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = dequeueDataCell(id: "myCell") as? CellView else { return UITableViewCell() }
cell.title.text = user?.name
cell.subtitle.text = user?.nickname
cell.action.isHidden = isSelected
// other numerous UI set up on the cell
return cell
}
}
class CellView: UITableViewCell {
#IBOutlet weak var title: UILabel?
#IBOutlet weak var subtitle: UILabel?
#IBOutlet weak var action: UIButton!
}
and
class ViewController: UITableViewController {
private var users: [User]
...
override func tableView(
_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = dequeueDataCell(id: "myCell") as? CellView else { return UITableViewCell() }
cell.user = self.users[indexPath.row]
cell.isSelected = false
cell.refresh()
return cell
}
}
class CellView: UITableViewCell {
#IBOutlet private weak var title: UILabel?
#IBOutlet private weak var subtitle: UILabel?
#IBOutlet private weak var action: UIButton!
weak var user: User?
var isSelected = false
func refresh() {
title.text = user?.name
subtitle.text = user?.nickname
action.isHidden = isSelected
// other numerous UI set up on the cell
}
}
I've so far been using the first approach that is found in most iOS tutorials. But I've found that this makes the UIViewController much longer, while they already have a tendency to become long and obscure, and harder to read since it mixes UI and state logic.

I got the UITableViewCell, but cell's elements are nil. Why?

The following code is a cleansed & rehashed version of a previous post.
ref: This class is not key value coding-compliant for the key...why?
import Foundation
import UIKit
var x = 1
struct DiaryItem {
var title:String?
var subTitle:String?
var leftImage:UIImage?
var rightImage:UIImage?
init(title:String, subTitle:String) {
self.title = title
self.subTitle = subTitle
}
}
class DiaryTableViewCell: UITableViewCell {
#IBOutlet weak var TitleLabel: UILabel!
#IBOutlet weak var SubTitleLabel: UILabel!
#IBOutlet weak var leftImageView: UIImageView!
#IBOutlet weak var rightImageView: UIImageView!
}
class DiaryTableViewController: UITableViewController {
let kCellIdentifier = "DiaryCell"
var diaryCell:DiaryTableViewCell?
var objects = NSMutableArray() //...global var.
override func viewDidLoad() {
self.title = "My Diary"
tableView.registerClass(DiaryTableViewCell.self, forCellReuseIdentifier: kCellIdentifier)
}
// -----------------------------------------------------------------------------------------------------
// MARK: - UITableViewDelegate
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let controller = gStoryboard.instantiateViewControllerWithIdentifier("DiaryPlayerVC") as DiaryPlayerViewController
self.navigationController?.pushViewController(controller, animated: true)
}
// -----------------------------------------------------------------------------------------------------
// MARK: - UITableViewDataSource
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
// -----------------------------------------------------------------------------------------------------
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
// -----------------------------------------------------------------------------------------------------
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier) as DiaryTableViewCell?
// cell?.selectionStyle = .None
println("\(x++)) Inside cell")
cell!.TitleLabel?.text = "Hello"
cell!.TitleLabel?.backgroundColor = UIColor.blueColor()
cell!.SubTitleLabel?.text = "World"
cell!.contentView.backgroundColor = UIColor.redColor()
return cell!
}
...
}
I'm getting the cell, but no elements of that cell.
1) Inside cell
(lldb) po cell!.TitleLabel
nil
I cleaned up the code, it compiles & runs okay. The cell is loaded and painted with red so I can see it was loaded. But none of the cell's contents are instantiated.
why?
It's seeing the members of the cell now...
But now I'm getting:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSObject 0x7f9691594810> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key leftImageView.'
If I disconnect the outlets, I get the images:
I've added the required init() but still have the same problem:
class DiaryTableViewCell: UITableViewCell {
#IBOutlet weak var leftImageView: UIImageView!
#IBOutlet weak var rightImageView: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var subTitleLabel: UILabel!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
NSLog("init coder")
}
}
You have declared your properties as implicitly unwrapped optionals, signing that your are sure they are not nil. Thus there is no need to use optional chaining.
And please use lower case property names because upper case names are used for class names:
class DiaryTableViewCell: UITableViewCell {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var subTitleLabel: UILabel!
#IBOutlet weak var leftImageView: UIImageView!
#IBOutlet weak var rightImageView: UIImageView!
}
Then try this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier) as? DiaryTableViewCell {
// cell.selectionStyle = .None
println("\(x++)) Inside cell")
cell.titleLabel.text = "Hello"
cell.titleLabel.backgroundColor = UIColor.blueColor()
cell.subTitleLabel.text = "World"
cell.contentView.backgroundColor = UIColor.redColor()
return cell
}
return UITableViewCell()
}
If that crashes there must be something wrong with your outlet bindings.
Did you set your DiaryTableViewCell as the class for the nib in InterfaceBuilder?
Your tableCellClass lacks the awakeFromNib()method for nib:
class DiaryTableViewCell: UITableViewCell {
#IBOutlet weak var TitleLabel: UILabel!
#IBOutlet weak var SubTitleLabel: UILabel!
#IBOutlet weak var leftImageView: UIImageView!
#IBOutlet weak var rightImageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
I found the solution:
This class is not key value coding-compliant for the key...why?
Here's the repost:
I had linked the UI elements to the WRONG source:
Here's the result:

TableViewController with custom cells in swift

I'm having an issue with custom TableViewCell in Swift. I'm trying to display a custome TableViewCell in a TableView but that doesn't work well. I got a grey square on the screen... I found some answers on the Internet that recommend to disable Auto-Layout. I did it but now, my cell is displayed but there is a mess with the height of the cells. As you can see, my cell that is supposed to have red background is displayed on the height of 3 cells while the background is on only 1 cell. The height of the cell and the Row Height are both set to 150px.
With Auto-Layout: Without Auto-Layout:
Here is the Cell code:
class CharacterDescription : UITableViewCell {
#IBOutlet var imageProfile: UIImageView!
#IBOutlet var name: UILabel!
#IBOutlet var hp: UILabel!
#IBOutlet var damages: UILabel!
#IBOutlet var range: UILabel!
#IBOutlet var mp: UILabel!
#IBOutlet var critChances: UILabel!
#IBOutlet var critModifier: UILabel!
required init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}
override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
super.init(style: UITableViewCellStyle.Value1, reuseIdentifier: reuseIdentifier)
}
}
And the TableViewController code:
class CharacterDescriptionsViewController : UITableViewController, UITableViewDataSource, UITableViewDelegate {
var userProfile: UserProfile!
required init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}
override init() {
super.init()
}
override func viewDidLoad() {
var nibName = UINib(nibName: "CharacterDescription", bundle:nil)
self.tableView.registerNib(nibName, forCellReuseIdentifier: "CharacterDescription")
}
override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
return 1
}
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
return userProfile.characters!.count
}
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell? {
var cell = self.tableView.dequeueReusableCellWithIdentifier("CharacterDescription", forIndexPath: indexPath) as CharacterDescription
//!TODO: Init of cell
return cell
}
}
You have to override TableView.heightForRowAtIndexPath to return the height of your custom cell.

Resources