EXC_BAD_ACCESS in Custom UITableViewCell - ios

I've been banging my head against a wall for the past day or so trying to figure out this problem, so I hope someone can help!
I'm just trying to create a custom subclass of a UITableViewCell, but my app keeps crashing with an EXC_BAD_ACCESS error in the init function of my custom TableViewCell. I'm on Xcode 7.01
DiscoverViewController.swift
import UIKit
class DiscoverViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let networkInterface: GfyNetwork = GfyNetwork()
var gfyArray: Array<GfyModel> = []
var tableView: UITableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.title = "Discover"
let navbar = self.navigationController!.navigationBar
navbar.tintColor = UIColor(red:0.32, green:0.28, blue:0.61, alpha:1.0)
networkInterface.getTrendingGfys("", completionHandler: printGfys)
tableView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
tableView.delegate = self
tableView.dataSource = self
tableView.separatorStyle = .None
tableView.rowHeight = 260
tableView.contentInset = UIEdgeInsetsMake(10, 0, 10, 0)
tableView.registerClass(GfyTableViewCell.self, forCellReuseIdentifier: "gfycell")
self.view.addSubview(tableView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func printGfys(gfyJSON: Array<GfyModel>) -> Array<GfyModel> {
// Array of fetched gfys
self.gfyArray = gfyJSON
// Update Tableview
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
return gfyJSON
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.gfyArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCellWithIdentifier("gfycell", forIndexPath: indexPath) as? GfyTableViewCell else { fatalError("unexpected cell dequeued from tableView") }
cell.gfy = self.gfyArray[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You selected cell #\(indexPath.row)!")
}
}
GfyTableViewCell.swift
import UIKit
class GfyTableViewCell: UITableViewCell {
let padding: CGFloat = 5
var gfy: GfyModel!
var bgView: UIView!
var imageURL: UIImageView!
var title: UILabel!
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
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
convenience override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
self.init(style: style, reuseIdentifier: reuseIdentifier) // Error happens here
backgroundColor = UIColor.whiteColor()
selectionStyle = .None
bgView.frame = CGRectMake(8, 0, contentView.frame.width-16, 250)
bgView.layer.cornerRadius = 3
bgView.layer.borderColor = UIColor(red:0, green:0, blue:0, alpha:0.4).CGColor
bgView.layer.borderWidth = 0.5
bgView.clipsToBounds = true
bgView.backgroundColor = UIColor.whiteColor()
title.frame = CGRectMake(10, 210, bgView.frame.width-100, 10)
title.text = gfy.title
title.font = UIFont.systemFontOfSize(10)
imageURL.frame = CGRectMake(0, 0, bgView.frame.width, 200)
if let url = NSURL(string: gfy.thumbUrl) {
if let data = NSData(contentsOfURL: url){
imageURL.contentMode = UIViewContentMode.ScaleAspectFill
imageURL.image = UIImage(data: data)
}
}
contentView.addSubview(bgView)
bgView.addSubview(imageURL)
}
override func prepareForReuse() {
super.prepareForReuse()
}
override func layoutSubviews() {
super.layoutSubviews()
}
}
Any help would be much appreciated. The app works when using standard UITableViewCells, but as soon as I try to add custom tableviewcells, it blows up :(
edit:
This is what my stack looks like. I'm pretty sure I'm doing something wrong in my override init() function in GfyTableViewCell.swift, but I don't know what that is:

The problem here is that the init method calls itself. Replace the following line:
self.init(style: style, reuseIdentifier: reuseIdentifier)
with:
super.init(style: style, reuseIdentifier: reuseIdentifier)
If you call a method within itself it will recursively call itself until the program eventually crashes because of a stack overflow or running out of memory. It's not obvious why this crashes with EXC_BAD_ACCESS, but it's possible this leads to one instance failing to actually be allocated.

Wow, as I expected, it was a simple error on my part.
Instead of calling:
convenience override init(style: UITableViewCellStyle, reuseIdentifier: String?) { ... }
It seems like I need to ditch the convenience and just call:
override init(style: UITableViewCellStyle, reuseIdentifier: String?) { ... }
Then I can do as Anthony posted above and call super.init(style: style, reuseIdentifier: reuseIdentifier) without any errors.

Fix:For Custom TableviewCell in Xcode 7.1.1.
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
let cellT = cell as! CustomTableViewCellName
//enter code here
}

Related

Wondering how I subclass UITableViewCell in swift 3.0 programmatically?

As the title implies I am trying to customize/subclass the UITableViewCell class in swift 3.0 programmatically and have the custom cells appears in a table.
I have the following subclass of UITableViewCell declared:
import Foundation
import UIKit
class TableViewCellCustom: UITableViewCell {
var myLabel: UILabel!
var myButton: UIButton!
let screenSize = UIScreen.main.bounds
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.backgroundColor = UIColor.blue
myLabel = UILabel()
myLabel.frame = CGRect(x: 180, y: 20.0, width: 50.0, height: 20.0)
myLabel.textColor = UIColor.black
myLabel.textAlignment = .center
contentView.addSubview(myLabel)
}
}
This should allow me to do the simple case of having a custom cell with a label in a specific location in the cell, of my designation. I am a bit confused though as to how I should integrate this into my ViewController where I am delegating the UITableView.
Currently my ViewController looks like so:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView: UITableView = UITableView()
var items: [String] = ["Party at Dougs", "Pillage at the Village", "Meow party at sams"]
let screenSize: CGRect = UIScreen.main.bounds
var appTitle: UIView! = nil
var appTitleText: UILabel! = nil
override func viewDidLoad() {
super.viewDidLoad()
let screenWidth = screenSize.width
let screenHeight = screenSize.height
tableView.frame = CGRect(origin: CGPoint(x: 0,y :screenHeight*0.12), size: CGSize(width: screenWidth, height: screenHeight*0.88))
tableView.delegate = self
tableView.dataSource = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
self.view.addSubview(tableView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "td")
cell.textLabel?.text = items[indexPath.row]
self.tableView.rowHeight = screenSize.height * 0.15
return cell
}
}
I think the core of my problem is being unsure how to connect my subclass of UITableViewCell to my TableView. How can I do this? thanks.
For custom cell created programmatically, without using xib, which is your case currently, you can use the below code -
instead of this
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
use this line
tableView.register(TableViewCellCustom.self, forCellReuseIdentifier: "cell")
Also instead of line
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "td")
use this line
let cell = TableViewCellCustom(style: UITableViewCellStyle.default, reuseIdentifier: "td")
For when using xib to create custom cell . Use the below code in your UITableViewDataSource cellForAtIndexPath if you have xib for custom cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("td") as? TableViewCellCustom
if cell == nil {
tableView.registerNib(UINib(nibName: "TableViewCellCustom", bundle: nil), forCellReuseIdentifier: "td")
cell = tableView.dequeueReusableCellWithIdentifier("td") as? TableViewCellCustom
}
cell.textLabel?.text = items[indexPath.row]
self.tableView.rowHeight = screenSize.height * 0.15
return cell
}

Empty Cells in TableView with Custom TableViewCell - Swift

I am currently learning Swift programatically. I am wanting to add a tableView to a viewController (for the purpose of being able to manipulate the constraints later) and customize the cells with a TableViewCell.
I can do this with my eyes closed when using the storyboard, but when I try to do it with just straight code I have empty cells.
My storyboard is comprised of one (1) empty viewController that has the custom class of ViewController
I have looked at others with similar issues but non of the solutions have worked. Would love to know what I am overlooking (probably something simple). Thanks in advance for the help!
ViewController.swift:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
var tableView: UITableView = UITableView()
var items: [String] = ["Viper", "X", "Games"]
override func viewDidLoad() {
tableView.frame = CGRectMake(0, 0, view.frame.size.width, view.frame.size.height)
tableView.delegate = self
tableView.dataSource = self
tableView.registerClass(TableViewCell.self, forCellReuseIdentifier: "cell")
self.view.addSubview(tableView)
}
func tableView(tableView:UITableView, heightForRowAtIndexPath indexPath:NSIndexPath)->CGFloat
{
return 50
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.items.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableViewCell
//cell.textLabel?.text = self.items[indexPath.row]
cell.companyName.text = "name"
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
TableViewCell:
import UIKit
class TableViewCell: UITableViewCell {
var companyName = UILabel()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
companyName.frame = CGRectMake(0, 0, 200, 20)
companyName.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(companyName)
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Hello #Michael First thing is you should only use awakeFromNib when you are using a .xib(Nib) and in your case you are using custom class without such xib so, you should use
override init(style: UITableViewCellStyle, reuseIdentifier: String?){
super.init(style: style, reuseIdentifier: reuseIdentifier)
companyName = UILabel(frame: CGRectMake(0, 0, 200, 20))
contentView.addSubview(companyName)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
also you should initialise your label before using it.
this will solve your problem.
Read apple's documentation for subclassing UITableViewCell here.
If you want your custom cell to load from some custom xib you do sometimes like:
tableView.registerNib(UINib(nibName: "CustomTableViewCell", bundle: NSBundle.mainBundle()), forCellReuseIdentifier: "CustomTableViewCell")
And you should have CustomTableViewCell.xib file where you have table view cell with reuse identifier CustomTableViewCell
Checkout how your cell's companyLabel is laid out. Does it exist or no?
In your code, I replaced companyLabel with default textLabel and it worked for me.
cell.textLabel!.text = self.items[indexPath.row]
I think awakeFromNib is not called because you do not register a nib but a class. Try this instead:
class TableViewCell: UITableViewCell {
let companyName = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// Initialization code
companyName.frame = CGRectMake(0, 0, 200, 20)
companyName.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(companyName)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}

UITableViewCell Subclass in swift not appearing in TableView

Alright so I've created a subclass of UITableViewCell called TwoColumnCell, and all I want is a cell with two UILabels in it.
Here is my subclass:
import UIKit
class TwoColumnCell: UITableViewCell
{
var firstNameLabel:UILabel = UILabel()
var lastNameLabel:UILabel = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?)
{
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(firstNameLabel)
self.contentView.addSubview(lastNameLabel)
}
override func layoutSubviews()
{
super.layoutSubviews()
firstNameLabel = UILabel(frame: CGRectMake(20.0, 10, self.bounds.size.width/2, self.bounds.size.height))
lastNameLabel = UILabel(frame: CGRectMake(self.bounds.size.width/2 + 5, 10, self.bounds.size.width/2, self.bounds.size.height))
}
override init() { super.init(); }
required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
override init(frame: CGRect) { super.init(frame: frame) }
}
Here is my Controller:
import UIKit
class ViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate
{
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.tableView.registerClass(TwoColumnCell.self, forCellReuseIdentifier: "TwoColumnCell")
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 2;
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cellIdendifier: String = "TwoColumnCell"
var cell:TwoColumnCell = tableView.dequeueReusableCellWithIdentifier(cellIdendifier, forIndexPath: indexPath) as! TwoColumnCell
cell.firstNameLabel.text = "Jony"
cell.lastNameLabel.text = "Ive"
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
But when I run the application, the table view is empty. It's probably a CGRect issue, but I can't seem to figure it out. Any ideas?
Your layoutSubviews is creating new UILabels rather than updating the frames on the existing ones. Just needs some tweaks:
override func layoutSubviews()
{
super.layoutSubviews()
firstNameLabel.frame = CGRectMake(20.0, 10, self.bounds.size.width/2, self.bounds.size.height)
lastNameLabel.frame = CGRectMake(self.bounds.size.width/2 + 5, 10, self.bounds.size.width/2, self.bounds.size.height)
}

Swift Custom UITableViewCell Programmatically works and shows in cell but Storyboard IBOutlet is nil and does not show in cell

In my custom cell swift file CustomTableView.swift, I can create programmatically the labels for my TableView but when I use IBOutlet in Storyboard the label becomes always nil
I am very sure that my cell is not nil
import UIKit
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var lblPostDate: UILabel!
var message: UILabel = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.message.frame = CGRectMake(0, 0, 100, 40);
self.message.backgroundColor = UIColor.brownColor()
self.message.text = "bla bla bla bla bla"
self.addSubview(self.message)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
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
}
func setCell(postDate: String)
{
self.lblPostDate?.text = postDate
}
}
In the above code, the message can be seen in the cells but lblPostDate which is the IBOutlet can not be seen
I am sure about the delegate and datasource and custom cell identifier what so ever but it seems that the IBOutlets don't get initialized correctly. I can see that lblPostDate becomes nil when I debug
Is this a bug of XCode 6?
Here is how I call from my Controller
import UIKit
class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var items = ["one","two","three","four"]
let kCellIdentifier: String = "CustomCell"
#IBOutlet weak var mTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.mTableView.registerClass(CustomTableViewCell.classForCoder(), forCellReuseIdentifier:kCellIdentifier)
//self.mTableView.registerClass(CustomTableViewCell.self, forCellReuseIdentifier: kCellIdentifier)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:CustomTableViewCell! = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier, forIndexPath: indexPath) as? CustomTableViewCell
if (cell == nil) {
self.mTableView.registerClass(CustomTableViewCell.classForCoder(), forCellReuseIdentifier: kCellIdentifier)
cell = CustomTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: kCellIdentifier)
}
if var label = cell.lblPostDate{
label.text = items[indexPath.row]
}
else{
cell.setCell(items[indexPath.row])
}
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 44
}
}
You have to link the lblPostDate with one label in your cell xib file.
That is insane, I deleted the register class code and it worked, I can't believe it
//self.mTableView.registerClass(CustomTableViewCell.classForCoder(), forCellReuseIdentifier:kCellIdentifier)
After commenting out the registerClass in my Controller, it doesn't call anymore the init function but creates the lblPostDate correctly indeed
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.message.frame = CGRectMake(0, 0, 100, 40);
self.message.backgroundColor = UIColor.brownColor()
self.message.text = "bla bla bla bla bla"
self.addSubview(self.message)
}
So I don't need anymore to create hard-code input objects.
It is interesting that all the tutorials, everywhere they suggest to register the class. Anyway,it worked!

Assigning property of custom UITableViewCell

I am using the below custom UITableViewCell without nib file.
I have no problem in viewing it in my UITableViewController.
My problem is with assigning a text to "name" label cell.name.text = "a Name" .. noting is assigned
Can you help me?
import UIKit
class ACMenuCell: UITableViewCell {
#IBOutlet var name : UILabel!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
name = UILabel(frame: CGRectMake(20, 10, self.bounds.size.width , 25))
name.backgroundColor = .whiteColor()
self.contentView.addSubview(name)
self.contentView.backgroundColor = .blueColor()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
}
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
}
}
You might want to try this: Create a public function in a subclass of UITableViewCell, and inside this funxtion set the text to label.
(void)initializeCell
{
do whatever like setting the text for label which is a subview of tableViewCell
}
And from cellForRowAtIndexPAth, call this function on cell object:
// taking your code for demo
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let reuseIdentifierAC:NSString = "ACMenuCell"; var cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifierAC, forIndexPath:indexPath) as ACMenuCell cell = ACMenuCell(style: UITableViewCellStyle.Default, reuseIdentifier: reuseIdentifierAC) [cell initializeCell] return cell }
This approach has worked for me.

Resources