Custom UITableViewCell programmatically using Swift - ios

Hey all I am trying to create a custom UITableViewCell, but I see nothing on the simulator. Can you help me please.
I can see the label only if I var labUserName = UILabel(frame: CGRectMake(0.0, 0.0, 130, 30));
but it overlaps the cell. I don't understand, Auto Layout should know the preferred size/minimum size of each cell?
Thanks
import Foundation
import UIKit
class TableCellMessages: UITableViewCell {
var imgUser = UIImageView();
var labUserName = UILabel();
var labMessage = UILabel();
var labTime = UILabel();
override init(style: UITableViewCellStyle, reuseIdentifier: String) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
imgUser.layer.cornerRadius = imgUser.frame.size.width / 2;
imgUser.clipsToBounds = true;
contentView.addSubview(imgUser)
contentView.addSubview(labUserName)
contentView.addSubview(labMessage)
contentView.addSubview(labTime)
//Set layout
var viewsDict = Dictionary <String, UIView>()
viewsDict["image"] = imgUser;
viewsDict["username"] = labUserName;
viewsDict["message"] = labMessage;
viewsDict["time"] = labTime;
//Image
//contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[image(100)]-'", options: nil, metrics: nil, views: viewsDict));
//contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[image(100)]-|", options: nil, metrics: nil, views: viewsDict));
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[username]-[message]-|", options: nil, metrics: nil, views: viewsDict));
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[username]-|", options: nil, metrics: nil, views: viewsDict));
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[message]-|", options: nil, metrics: nil, views: viewsDict));
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Let's make a few assumptions:
You have an iOS8 project with a Storyboard that contains a single UITableViewController. Its tableView has a unique prototype UITableViewCell with custom style and identifier: "cell".
The UITableViewController will be linked to Class TableViewController, the cell will be linked to Class CustomTableViewCell.
You will then be able to set the following code (updated for Swift 2):
CustomTableViewCell.swift:
import UIKit
class CustomTableViewCell: UITableViewCell {
let imgUser = UIImageView()
let labUserName = UILabel()
let labMessage = UILabel()
let labTime = UILabel()
override func awakeFromNib() {
super.awakeFromNib()
imgUser.backgroundColor = UIColor.blueColor()
imgUser.translatesAutoresizingMaskIntoConstraints = false
labUserName.translatesAutoresizingMaskIntoConstraints = false
labMessage.translatesAutoresizingMaskIntoConstraints = false
labTime.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(imgUser)
contentView.addSubview(labUserName)
contentView.addSubview(labMessage)
contentView.addSubview(labTime)
let viewsDict = [
"image": imgUser,
"username": labUserName,
"message": labMessage,
"labTime": labTime,
]
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[image(10)]", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[labTime]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[username]-[message]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[username]-[image(10)]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[message]-[labTime]-|", options: [], metrics: nil, views: viewsDict))
}
}
TableViewController.swift:
import UIKit
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Auto-set the UITableViewCells height (requires iOS8+)
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomTableViewCell
cell.labUserName.text = "Name"
cell.labMessage.text = "Message \(indexPath.row)"
cell.labTime.text = NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .ShortStyle, timeStyle: .ShortStyle)
return cell
}
}
You will expect a display like this (iPhone landscape):

This is the update for swift 3 of the answer Imanou Petit.
CustomTableViewCell.swift:
import Foundation
import UIKit
class CustomTableViewCell: UITableViewCell {
let imgUser = UIImageView()
let labUerName = UILabel()
let labMessage = UILabel()
let labTime = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
imgUser.backgroundColor = UIColor.blue
imgUser.translatesAutoresizingMaskIntoConstraints = false
labUerName.translatesAutoresizingMaskIntoConstraints = false
labMessage.translatesAutoresizingMaskIntoConstraints = false
labTime.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(imgUser)
contentView.addSubview(labUerName)
contentView.addSubview(labMessage)
contentView.addSubview(labTime)
let viewsDict = [
"image" : imgUser,
"username" : labUerName,
"message" : labMessage,
"labTime" : labTime,
] as [String : Any]
contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[image(10)]", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[labTime]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[username]-[message]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[username]-[image(10)]-|", options: [], metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[message]-[labTime]-|", options: [], metrics: nil, views: viewsDict))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Settigns.swift:
import Foundation
import UIKit
class Settings: UIViewController, UITableViewDelegate, UITableViewDataSource {
private var myTableView: UITableView!
private let sections: NSArray = ["fruit", "vegitable"] //Profile network audio Codecs
private let fruit: NSArray = ["apple", "orange", "banana", "strawberry", "lemon"]
private let vegitable: NSArray = ["carrots", "avocado", "potato", "onion"]
override func viewDidLoad() {
super.viewDidLoad()
// get width and height of View
let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
let navigationBarHeight: CGFloat = self.navigationController!.navigationBar.frame.size.height
let displayWidth: CGFloat = self.view.frame.width
let displayHeight: CGFloat = self.view.frame.height
myTableView = UITableView(frame: CGRect(x: 0, y: barHeight+navigationBarHeight, width: displayWidth, height: displayHeight - (barHeight+navigationBarHeight)))
myTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell") // register cell name
myTableView.dataSource = self
myTableView.delegate = self
//Auto-set the UITableViewCells height (requires iOS8+)
myTableView.rowHeight = UITableViewAutomaticDimension
myTableView.estimatedRowHeight = 44
self.view.addSubview(myTableView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// return the number of sections
func numberOfSections(in tableView: UITableView) -> Int{
return sections.count
}
// return the title of sections
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section] as? String
}
// called when the cell is selected.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Num: \(indexPath.row)")
if indexPath.section == 0 {
print("Value: \(fruit[indexPath.row])")
} else if indexPath.section == 1 {
print("Value: \(vegitable[indexPath.row])")
}
}
// return the number of cells each section.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return fruit.count
} else if section == 1 {
return vegitable.count
} else {
return 0
}
}
// return cells
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
if indexPath.section == 0 {
cell.labUerName.text = "\(fruit[indexPath.row])"
cell.labMessage.text = "Message \(indexPath.row)"
cell.labTime.text = DateFormatter.localizedString(from: NSDate() as Date, dateStyle: .short, timeStyle: .short)
} else if indexPath.section == 1 {
cell.labUerName.text = "\(vegitable[indexPath.row])"
cell.labMessage.text = "Message \(indexPath.row)"
cell.labTime.text = DateFormatter.localizedString(from: NSDate() as Date, dateStyle: .short, timeStyle: .short)
}
return cell
}
}

In Swift 5.
The custom UITableViewCell:
import UIKit
class CourseCell: UITableViewCell {
let courseName = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// Set any attributes of your UI components here.
courseName.translatesAutoresizingMaskIntoConstraints = false
courseName.font = UIFont.systemFont(ofSize: 20)
// Add the UI components
contentView.addSubview(courseName)
NSLayoutConstraint.activate([
courseName.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
courseName.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20),
courseName.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
courseName.heightAnchor.constraint(equalToConstant: 50)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The UITableViewController:
import UIKit
class CourseTableViewController: UITableViewController {
private var data: [Int] = [1]
override func viewDidLoad() {
super.viewDidLoad()
// You must register the cell with a reuse identifier
tableView.register(CourseCell.self, forCellReuseIdentifier: "courseCell")
// Change the row height if you want
tableView.rowHeight = 150
// This will remove any empty cells that are below your data filled cells
tableView.tableFooterView = UIView()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "courseCell", for: indexPath) as! CourseCell
cell.courseName.text = "Course name"
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}

Related

TableView not appearing in simulator?

I have created an iOS application with Single View Application template. Once I run the application the launch screen appears for about 2 second and then displays the blue navbar, but a blank white background.
.
I think the issue must be with the subview of the tableView, but it's not clear to me how to fix that. (I'm not using storyboard, but will implement a start screen at some point. Would that solve my problem?)
import UIKit
struct Question {
var questionString: String?
var answers: [String]?
var selectedAnswerIndex: Int?
}
class QuestionController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let cellId = "cellId"
let headerId = "headerId"
var tableView: UITableView?
static var questionsList: [Question] = [Question(questionString: "What is your favorite type of food?", answers: ["Sandwiches", "Pizza", "Seafood", "Unagi"], selectedAnswerIndex: nil), Question(questionString: "What do you do for a living?", answers: ["Paleontologist", "Actor", "Chef", "Waitress"], selectedAnswerIndex: nil), Question(questionString: "Were you on a break?", answers: ["Yes", "No"], selectedAnswerIndex: nil)]
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Question"
navigationController?.navigationBar.tintColor = UIColor.white
navigationItem.backBarButtonItem = UIBarButtonItem(title: "Back", style: .plain, target: nil, action: nil)
tableView = UITableView()
tableView?.dataSource = self
tableView?.delegate = self
self.view.addSubview(self.tableView!)
tableView?.register(AnswerCell.self, forCellReuseIdentifier: cellId)
tableView?.register(QuestionHeader.self, forHeaderFooterViewReuseIdentifier: headerId)
tableView?.sectionHeaderHeight = 50
tableView?.tableFooterView = UIView()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let index = navigationController?.viewControllers.index(of: self) {
let question = QuestionController.questionsList[index]
if let count = question.answers?.count {
return count
}
}
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath as IndexPath) as! AnswerCell
if let index = navigationController?.viewControllers.index(of: self) {
let question = QuestionController.questionsList[index]
cell.nameLabel.text = question.answers?[indexPath.row]
}
return cell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerId) as! QuestionHeader
if let index = navigationController?.viewControllers.index(of: self) {
let question = QuestionController.questionsList[index]
header.nameLabel.text = question.questionString
}
return header
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let index = navigationController?.viewControllers.index(of: self) {
QuestionController.questionsList[index].selectedAnswerIndex = indexPath.item
if index < QuestionController.questionsList.count - 1 {
let questionController = QuestionController()
navigationController?.pushViewController(questionController, animated: true)
} else {
let controller = ResultsController()
navigationController?.pushViewController(controller, animated: true)
}
}
}
}
class ResultsController: UIViewController {
let resultsLabel: UILabel = {
let label = UILabel()
label.text = "Congratulations, you'd make a great Ross!"
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .center
label.font = UIFont.boldSystemFont(ofSize: 14)
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: .plain, target: self, action: Selector(("done")))
navigationItem.title = "Results"
view.backgroundColor = UIColor.white
view.addSubview(resultsLabel)
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": resultsLabel]))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": resultsLabel]))
let names = ["Ross", "Joey", "Chandler", "Monica", "Rachel", "Phoebe"]
var score = 0
for question in QuestionController.questionsList {
score += question.selectedAnswerIndex!
}
let result = names[score % names.count]
resultsLabel.text = "Congratulations, you'd make a great \(result)!"
}
func done() {
navigationController?.popToRootViewController(animated: true)
}
}
class QuestionHeader: UITableViewHeaderFooterView {
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
setupViews()
}
let nameLabel: UILabel = {
let label = UILabel()
label.text = "Sample Question"
label.font = UIFont.boldSystemFont(ofSize: 14)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
func setupViews() {
addSubview(nameLabel)
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-16-[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": nameLabel]))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": nameLabel]))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class AnswerCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let nameLabel: UILabel = {
let label = UILabel()
label.text = "Sample Answer"
label.font = UIFont.systemFont(ofSize: 14)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
func setupViews() {
addSubview(nameLabel)
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-16-[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": nameLabel]))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": nameLabel]))
}
}
The tableView has no frame
tableView = UITableView()
tableView.frame = self.view.bounds // this for a test change it as you want
Or use constraints
As Sh_Khan stated, what you need to do is set a frame. If this is your permanent method, you could also just set constraints. If you set constraints, it will automatically change height and width.
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
You can add this code into the viewDidLoad function. Basically it's setting a constraint for the left, right, top, and bottom sides of the view that the UIViewController automatically sets. This will force the height and width to change depending on where your left, right, top, and bottom sides are positioned.

Creating a TableView Programmatically with multiple cell in swift

As I am creating a TableView programmatically with multiple cell and in each cell having UICollectionView, UITableView, UITableView.... but I am not able to find the error and every time when I run the program it Shows
" Command failed due to signal: Segmentation fault: 11".
Has any one created this type of UI using coding.
New in Swift so forgive for errors.
// ViewController.swift
// Json Parsing in Swift
//
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UITableViewController {
var dataArray = Array<JSON>()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Alamofire.request(.GET, "http://104.131.162.14:3033/api/ios/detail").validate().responseJSON { response in
switch response.result {
case .Success:
if let value = response.result.value {
let json = JSON(value)
print("JSON: \(json)")
var trafficJson = json["traffic_partners"]
trafficJson["type"] = "Traffic"
self.dataArray.append(trafficJson)
var newsJson = json["news"]
newsJson["type"] = "News"
self.dataArray.append(newsJson)
var categoryJson = json["category"]
categoryJson["type"] = "Category"
self.dataArray.append(categoryJson)
var topFreeApps = json["top_free_apps"]
topFreeApps["type"] = "TopApps"
self.dataArray.append(topFreeApps)
var topSites = json["top_sites"]
topSites["type"] = "TopSites"
self.dataArray.append(topSites)
var trendingVideos = json["tranding_video"]
trendingVideos["type"] = "TrendingVideos"
self.dataArray.append(trendingVideos)
var sports = json["sports"]
sports["type"] = "Sports"
self.dataArray.append(sports)
var jokes = json["jokes"]
jokes["type"] = "jokes"
self.dataArray.append(jokes)
print(self.dataArray[0]["detail"][0].object)
print(self.dataArray[2]["detail"].object)
self.tableView.reloadData()
}
case .Failure(let error):
print(error)
}
}
tableView.registerClass(MyCell.self, forCellReuseIdentifier: "cellId")
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier("cellId", forIndexPath: indexPath) as! MyCell
myCell.nameLabel.text = dataArray[indexPath.row]["type"].string
if (dataArray[indexPath.row]["type"].string == "News") {
myCell.newsArray = dataArray[indexPath.row]["detail"].arrayObject
}
myCell.myTableViewController = self
return myCell
}
}
class MyCell: UITableViewCell {
var newsArray :NSMutableArray=[]
var myTableViewController: ViewController?
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let newsTableView: UITableView = {
let newsTV = UITableView(frame:UIScreen.mainScreen().bounds, style: UITableViewStyle.Plain)
newsTV.registerClass(NewsTableViewCell.self, forCellReuseIdentifier: "NewsTableViewCell")
return newsTV
}()
func tableView(newsTableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return newsArray.count
}
func tableView(newsTableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = newsTableView.dequeueReusableCellWithIdentifier("cellId", forIndexPath: indexPath) as! NewsTableViewCell
myCell.nameLabel.text = newsArray[indexPath.row]["title"].string
myCell.myTableViewController = myTableViewController
return myCell
}
let nameLabel: UILabel = {
let label = UILabel()
label.text = "Sample Item"
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.boldSystemFontOfSize(14)
return label
}()
func setupViews() {
addSubview(newsTableView)
addSubview(nameLabel)
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": nameLabel]))
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": newsTableView]))
}
func handleAction() {
}
}

Dynamic UITableView row height using UIStackView?

Surprised this isn't working out of the box, as this seems to be an important use case for stack views. I have a UITableViewCell subclass which adds a UIStackView to the contentView. I'm adding labels to the stack view in tableView(_cellForRowAtIndexPath:) and the tableview is set to use dynamic row heights, but it doesn't appear to work, at least in Xcode 7.3. I was also under the impression that hiding arranged subviews in a stack view was animatable, but that seems broken as well.
Any ideas on how to get this working correctly?
class StackCell : UITableViewCell {
enum VisualFormat: String {
case HorizontalStackViewFormat = "H:|[stackView]|"
case VerticalStackViewFormat = "V:|[stackView(>=44)]|"
}
var hasSetupConstraints = false
lazy var stackView : UIStackView! = {
let stack = UIStackView()
stack.axis = .Vertical
stack.distribution = .FillProportionally
stack.alignment = .Fill
stack.spacing = 3.0
stack.translatesAutoresizingMaskIntoConstraints = false
return stack
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(stackView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func updateConstraints() {
if !hasSetupConstraints {
hasSetupConstraints = true
let viewsDictionary: [String:AnyObject] = ["stackView" : stackView]
var newConstraints = [NSLayoutConstraint]()
newConstraints += self.newConstraints(VisualFormat.HorizontalStackViewFormat.rawValue, viewsDictionary: viewsDictionary)
newConstraints += self.newConstraints(VisualFormat.VerticalStackViewFormat.rawValue, viewsDictionary: viewsDictionary)
addConstraints(newConstraints)
}
super.updateConstraints()
}
private func newConstraints(visualFormat: String, viewsDictionary: [String:AnyObject]) -> [NSLayoutConstraint] {
return NSLayoutConstraint.constraintsWithVisualFormat(visualFormat, options: [], metrics: nil, views: viewsDictionary)
}
class ViewController: UITableViewController {
private let reuseIdentifier = "StackCell"
private let cellClass = StackCell.self
override func viewDidLoad() {
super.viewDidLoad()
configureTableView(self.tableView)
}
private func configureTableView(tableView: UITableView) {
tableView.registerClass(cellClass, forCellReuseIdentifier: reuseIdentifier)
tableView.separatorStyle = .SingleLine
tableView.estimatedRowHeight = 88
tableView.rowHeight = UITableViewAutomaticDimension
}
private func newLabel(title: String) -> UILabel {
let label = UILabel()
label.text = title
return label
}
// MARK: - UITableView
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 4
}
override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44.0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath) as! StackCell
cell.stackView.arrangedSubviews.forEach({$0.removeFromSuperview()})
cell.stackView.addArrangedSubview(newLabel("\(indexPath.section)-\(indexPath.row)"))
cell.stackView.addArrangedSubview(newLabel("Second Label"))
cell.stackView.addArrangedSubview(newLabel("Third Label"))
cell.stackView.addArrangedSubview(newLabel("Fourth Label"))
cell.stackView.addArrangedSubview(newLabel("Fifth Label"))
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath) as! StackCell
for (idx, view) in cell.stackView.arrangedSubviews.enumerate() {
if idx == 0 {
continue
}
view.hidden = !view.hidden
}
UIView.animateWithDuration(0.3, animations: {
cell.contentView.layoutIfNeeded()
tableView.beginUpdates()
tableView.endUpdates()
})
}
}
It seems that for this to work the constraints need to be added in the init of the UITableViewCell and added to the contentView instead of cell's view.
The working code looks like this:
import UIKit
class StackCell : UITableViewCell {
enum VisualFormat: String {
case HorizontalStackViewFormat = "H:|[stackView]|"
case VerticalStackViewFormat = "V:|[stackView(>=44)]|"
}
var hasSetupConstraints = false
lazy var stackView : UIStackView! = {
let stack = UIStackView()
stack.axis = UILayoutConstraintAxis.Vertical
stack.distribution = .FillProportionally
stack.alignment = .Fill
stack.spacing = 3.0
stack.translatesAutoresizingMaskIntoConstraints = false
stack.setContentCompressionResistancePriority(UILayoutPriorityRequired, forAxis: .Vertical)
return stack
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(stackView)
addStackConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func addStackConstraints() {
let viewsDictionary: [String:AnyObject] = ["stackView" : stackView]
var newConstraints = [NSLayoutConstraint]()
newConstraints += self.newConstraints(VisualFormat.HorizontalStackViewFormat.rawValue, viewsDictionary: viewsDictionary)
newConstraints += self.newConstraints(VisualFormat.VerticalStackViewFormat.rawValue, viewsDictionary: viewsDictionary)
contentView.addConstraints(newConstraints)
super.updateConstraints()
}
private func newConstraints(visualFormat: String, viewsDictionary: [String:AnyObject]) -> [NSLayoutConstraint] {
return NSLayoutConstraint.constraintsWithVisualFormat(visualFormat, options: [], metrics: nil, views: viewsDictionary)
}
}

dynamic UITableViewCell height programmatically

I am trying to create a tableview cell programmatically with varying heights.
// Created by AJ Norton on 7/29/15.
// Copyright (c) 2015 AJ Norton. All rights reserved.
import UIKit
class ViewController: UITableViewController {
var arr = ["image1.jpg", "image2.jpg"]
override func viewDidLoad() {
super.viewDidLoad()
self.title = "he"
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.SingleLine
self.tableView.registerClass(CustCellTableViewCell.self, forCellReuseIdentifier: "cell")
// Do any additional setup after loading the view, typically from a nib.
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell") as! CustCellTableViewCell
cell.label.text = arr[indexPath.row]
cell.imageContainer.image = UIImage(named: arr[indexPath.row])
return cell
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arr.count
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
var i = UIImage(named: arr[indexPath.row])!
return CGFloat(i.size.height + 15.0)
}
}
this is my Custom cell:
// Created by AJ Norton on 7/29/15.
// Copyright (c) 2015 AJ Norton. All rights reserved.
import UIKit
class CustCellTableViewCell: UITableViewCell {
lazy var imageContainer: UIImageView = {
var i = UIImageView()
i.setTranslatesAutoresizingMaskIntoConstraints(false)
return i
}()
lazy var label: UILabel = {
var l = UILabel()
l.setTranslatesAutoresizingMaskIntoConstraints(false)
l.textColor = UIColor.redColor()
return l
}()
override func layoutSubviews() {
self.addSubview(label)
self.addSubview(imageContainer)
self.backgroundColor = UIColor.greenColor()
var viewDict = ["label": label, "image": imageContainer]
var constraint1 = NSLayoutConstraint.constraintsWithVisualFormat("H:|-5-[image]-5-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewDict)
var constraint2 = NSLayoutConstraint.constraintsWithVisualFormat("V:|[label]-[image]|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewDict)
var constraint3 = NSLayoutConstraint.constraintsWithVisualFormat("H:|[label]|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewDict)
self.addConstraints(constraint1)
self.addConstraints(constraint2)
self.addConstraints(constraint3)
self.sizeToFit()
}
}
When I run the app I get this: . I want the images to take up their maximum height (300px) for the cell (Also the whole background should be green.
just delete self.sizeToFit() and it works.

UITableViewCell selection in editing mode.

I am making a custom UIViewController that I want to show a message when a UITableViewCell is tapped. I create a UITableView and set the property
tableView.allowsSelectionDuringEditing = true
Even though that property has been set to true, tableView(:didSelectRowAtIndexPath) is not being called to display the message. Why is this and how do I fix it?
Here is my UITableViewController:
class GameListViewController: UIViewController, UITableViewDataSource, GameListViewDelegate, UITableViewDelegate
{
private var _games: [GameObject] = []
var tableView: UITableView {return (view as GameListView).tableView}
var gameListView: GameListView {return (view as GameListView) }
override func loadView()
{
view = GameListView(frame: UIScreen.mainScreen().bounds)
gameListView.delegate = self
}
override func viewDidLoad()
{
super.viewDidLoad()
tableView.dataSource = self
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return _games.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var index: Int = indexPath.row
let currentGame = _games[index]
var cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: nil)
cell.textLabel?.lineBreakMode = NSLineBreakMode.ByCharWrapping
cell.textLabel?.numberOfLines = 2
if currentGame.isFinished == true
{
cell.imageView?.image = UIImage(named: "Finished.png")
cell.textLabel?.text = "Winner: Player\(currentGame.playerMakingMove)\nMissiles Launched: \(currentGame.missileCount)"
}
else
{
cell.imageView?.image = UIImage(named: "Resume.png")
cell.textLabel?.text = "Turn: Player\(currentGame.playerMakingMove)\nMissiles Launched: \(currentGame.missileCount)"
}
return cell
}
/*Handles deleting the cells*/
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
{
if (editingStyle == UITableViewCellEditingStyle.Delete)
{
var index: Int = indexPath.row
_games.removeAtIndex(index)
tableView.deleteRowsAtIndexPaths(NSArray(array: [indexPath]), withRowAnimation: UITableViewRowAnimation.Left)
}
tableView.reloadData()
}
/* Performs when a cell is touched */
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
var index: Int = indexPath.row
tableView.selectRowAtIndexPath(indexPath, animated: true, scrollPosition: UITableViewScrollPosition.None)
println("inside tableView: didSelectRowAtIndexPath")
var message = UIAlertView(title: "Row selected", message: "You have selected a row", delegate: nil, cancelButtonTitle: "Click ME!", otherButtonTitles: "no other buttons")
message.show()
}
func makeNewGame()
{
addGames(1)
tableView.reloadData()
}
func addGames(gameNumber: Int)
{
var p1 = Player()
var p2 = Player()
for var i = 0; i < gameNumber; i++
{
var randBool = Bool( round(drand48())) // True/False
var randPlayer:Int = Int( round( (drand48() + 1)) ) // 1 or 2
var randMissleCount:Int = Int( arc4random_uniform(5000) + 1 ) //1 - 5001
_games.append(GameObject(isFinished: randBool, playerMakingMove: randPlayer, missileCount: randMissleCount, player1: p1, player2: p2))
}
}
}
Here is my UIView that contains the UITableView:
protocol GameListViewDelegate: class
{
func makeNewGame()
}
class GameListView: UIView, PictureButtonDelegate
{
var newGameButton: PictureButton!
var tableView: UITableView!
weak var delegate: GameListViewDelegate? = nil
override init(frame: CGRect)
{
super.init(frame: frame)
newGameButton = PictureButton(frame: CGRect(), fileName: "NewGame.png")
newGameButton.backgroundColor = UIColor.grayColor()
newGameButton.delegate = self
newGameButton.setTranslatesAutoresizingMaskIntoConstraints(false)
tableView = UITableView()
tableView.allowsSelectionDuringEditing = true
tableView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.addSubview(newGameButton)
self.addSubview(tableView)
}
override func layoutSubviews()
{
let views: [String : UIView] = ["button": newGameButton, "tableView": tableView]
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[button]-|", options: .allZeros, metrics: nil, views: views))
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[tableView]-|", options: .allZeros, metrics: nil, views: views))
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-(>=15,<=25)-[button]-[tableView]-|", options: .allZeros, metrics: nil, views: views))
}
func buttonPushed()
{
delegate?.makeNewGame()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
You have to set a delegate for the UITableView and implement func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath).
You only set the dataSource, but never the delegate (only a GameListViewDelegate for GameListView).
Change your viewDidLoad function in GameListViewController to:
override func viewDidLoad()
{
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}

Resources