Wondering how I subclass UITableViewCell in swift 3.0 programmatically? - ios

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
}

Related

Adding double tap gesture recognizer to UIImageView in an UITableViewCell Swift 4+

(Edited with working solution)
So I'm trying to add a double tap gesture to an UIImageView I created in a custom UITableViewCell but can't seem to get it working.
Here is my custom UITableViewCell:
protocol CustomCellDelegate: class {
func didTapImage()
}
class CustomCell: UITableViewCell {
//change let to lazy var
lazy var userImage: UIImageView = {
let newView = UIIMageView()
newView.layer.cornerRadius = 24
newView.layer.masksToBounds = true
newView.image = UIImage(named: "samplePic")
newView.contentMode = .scaleAspectFill
newView.isUserInteractionEnabled = true
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(myFunc))
doubleTap.numberOfTouchesRequired = 1
doubleTap.numberOfTapsRequired = 2
newView.addGestureRecognizer(doubleTap)
newView.translatesAutoresizingMaskIntoConstraints = false
return newView
}
weak var delegate: CustomCellDelegate?
#objc func myFunc() {
delegate?.didTapImage()
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .subTitle, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
//changed addSubView(userImage) to self.contentView.addSubView(userImage)
self.contentView.addSubView(userImage)
NSLayoutConstraint.activate([
userImage.centerYAnchor.constraint(equalTo: self.centerYAnchor),
userImage.leftAnchor.constraint(equalTo: self.leftAnchor),
userImage.widthAnchor.constraint(equalToConstant: 48),
userImage.heightAnchor.constraint(equalToConstant: 48),
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Here is my custom UITableViewController:
class customTableViewController: UITableViewController, CustomCellDelegate {
fileprivate let cellId = "cellId"
func didTapImage() {
print("Tapped Image")
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(CustomCell.self, forCellReuseIdentifier: cellId)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 72
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! CustomCell
cell.delegate = self
return cell
}
}
Any ideas as to why this isn't working? What am I doing wrong? Also how do I avoid having the same tap gestures recognizer added multiple times as cells are dequeue?
You may need
userImage.translatesAutoresizingMaskIntoConstraints = false
as you create constraints programmatically
lazy var userImage: UIImageView = {
let newView = UIIMageView()
userImage.translatesAutoresizingMaskIntoConstraints = false
newView.layer.cornerRadius = 24
newView.layer.masksToBounds = true
newView.image = UIImage(named: "samplePic")
newView.contentMode = .scaleAspectFill
newView.isUserInteractionEnabled = true
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(myFunc))
doubleTap.numberOfTouchesRequired = 1
doubleTap.numberOfTapsRequired = 2
newView.addGestureRecognizer(doubleTap)
return newView
}()
also make it a lazy var not a computed property for being 1 instance every access , add the imageView to
self.contentView.addSubView(userImage)
and set the constraints with it

Programmatic UITableView is showing one cell only

A Swift newbie here. I'm trying to learn how to create different UI elements programmatically. I've hit the following wall..
I have 2 .swift files, on one hand we have...
import UIKit
struct MyTableView {
let myCustomTable: UITableView = {
let aTable = UITableView()
aTable.register(MyCustomCell.self, forCellReuseIdentifier: "myCell")
return aTable
}()
}
// custom cell class, then add subviews (UI controls) to it
class MyCustomCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(aLabel)
aLabel.frame = CGRect(x:0,
y:0,
width:self.frame.width,
height:self.frame.height)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// stuff to add to the cell
let aLabel: UILabel = {
let lbl = UILabel()
lbl.text = "My Custom Cell"
lbl.backgroundColor = UIColor.yellow // just to highlight it on the screen
return lbl
}()
On the other hand, we have the following view controller...
import UIKit
class ViewControllerA: UIViewController, UITableViewDelegate, UITableViewDataSource {
private let instanceOfViewControllerTable = MyTableView()
override func loadView() {
super.loadView()
view.frame = UIScreen.main.bounds
instanceOfViewControllerTable.myCustomTable.delegate = self
instanceOfViewControllerTable.myCustomTable.dataSource = self
instanceOfViewControllerTable.myCustomTable.frame = CGRect(x:0,
y:0,
width:self.view.frame.width,
height:self.view.frame.height)
self.view.addSubview(instanceOfViewControllerTable.myCustomTable)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
}
}
It builds and runs successfully, however, I'm getting the following result:
Now, my thinking is if I'm doing something wrong, the cell shouldn't appear at all. What I don't understand, why is it showing only on one cell in the array?
Your help is highly appreciated.
You are declaring aLabel as a global variable. That way, only one instance exists from it. Move it inside your cell's class declaration.
class MyCustomCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(aLabel)
aLabel.frame = CGRect(x:0,
y:0,
width:self.frame.width,
height:self.frame.height)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let aLabel: UILabel = {
let lbl = UILabel()
lbl.text = "My Custom Cell"
lbl.backgroundColor = UIColor.yellow // just to highlight it on the screen
return lbl
}()
}

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)
}
}

EXC_BAD_ACCESS in Custom UITableViewCell

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
}

Custom TableViewCell without storyboard

I am using custom cocoa class extends from TableViewCell and it doesn't give any error message but the cells do not appear in the tableview. The scroll gets longer but the table cells are not viewable.
I typed this in my ViewController :
tableView.registerClass(CustomTableViewCell.self, forCellReuseIdentifier: "Cell")
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)->UITableViewCell
{
var cell:CustomTableViewCell? = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as? CustomTableViewCell
if cell == nil {
cell = CustomTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
}
cell!.labTime.text = filtered[indexPath.row].name!
return cell!
}
and my cell class looks like
var labTime = UILabel()
override func awakeFromNib() {
// Initialization code
super.awakeFromNib()
labTime = UILabel(frame: contentView.bounds)
labTime.font = UIFont(name:"HelveticaNeue-Bold", size: 16)
contentView.addSubview(labTime)
}
I don't use any storyBoard or xib file.
Can not find the solution,
thanks
Do this way.
All view intialization of properties should go in init method.
Add this in your custom cell class
var labTime: UILabel!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
//Do your cell set up
labTime = UILabel(frame: contentView.bounds)
labTime.font = UIFont(name:"HelveticaNeue-Bold", size: 16)
contentView.addSubview(labTime)
}
Add the below line in viewDidLoad method of your view controller.
tableView.registerClass(CustomTableViewCell.self, forCellReuseIdentifier: "Cell")
Set these two delegate - UITableViewDataSource and UITableViewDelegate

Resources