I'm getting NSContiguousString leak on Instruments in a ViewController which has only UITableView and a UINavigationBar. When viewController appears texts on UITableViewCells doesn't shown, only blank cells.
If I change it to;
cell.textLabel?.text = NSString(string: dataSource[indexPath.row]) as String
Texts seen on cells.
Controller Class:
class FirstSupportedServicesViewController: BaseViewController {
let bTableView = UITableView()
let dataSource = ["Cell Text 1","Cell Text 2"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
bTableView.delegate = self
bTableView.dataSource = self
bTableView.frame = self.view.frame
bTableView.register(BaseCell.self, forCellReuseIdentifier: "SupportedServicesBaseCell")
bTableView.tableFooterView = UIView()
//Prevent TableView Scroll Bug
if #available(iOS 11.0, *) {
bTableView.contentInsetAdjustmentBehavior = .never
} else {
// Fallback on earlier versions
}
self.view.addSubview(bTableView)
let leftButton = UIBarButtonItem(title: "< Geri", style: .plain, target: self, action: #selector(self.backBtnTapped))
self.navigationItem.leftBarButtonItem = leftButton
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#objc func backBtnTapped(){
self.dismiss(animated: true, completion: nil)
}}
extension FirstSupportedServicesViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat{
return 44
}
}
extension FirstSupportedServicesViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SupportedServicesBaseCell", for: indexPath) as! BaseCell
cell.textLabel?.text = dataSource[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let controller = SupportedServicesViewController()
self.navigationController?.pushViewController(controller, animated: true)
tableView.deselectRow(at: indexPath, animated: true)
}
}
Custom TableView Cell:
class BaseCell: UITableViewCell {
let cellText: UILabel = {
let label = UILabel()
label.textColor = UIColor.black
label.font = Fonts.font
label.adjustsFontSizeToFitWidth = true
return label
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?){
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
func setupViews() {
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Related
I have a UITableView and I made a custom cellview to add a button inside each cell, the button is supposed to change its color when clicked.
Although the data is updated and printed correctly, the view always lags one step behind i.e. when I click the first button it doesn't change its color until I click the next one.I suspect that the reloadRows() function causes this problem when called from inside the tableview Cell.
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var mlist = [["1","2","3"], ["4","5","6","7","8"],["9","10"],["11","12"],["13","14"]]
var leagues = ["LaLiga", "Premier League", "Bundesliga", "Serie A", "Ligue 1"]
var hidden = Set<Int>()
#IBOutlet weak var tbl: UITableView!
var fv = Set<IndexPath>()
func indxs(_ section:Int) -> [IndexPath] {
var indxs = [IndexPath]()
for row in 0..<mlist[section].count {
indxs.append(IndexPath(row: row, section: section))
}
return indxs
}
#objc
private func hideSection(sender: UIButton) {
let section = sender.tag
if hidden.contains(section) {
hidden.remove(section)
tbl.insertRows(at: indxs(section), with: .fade)
}else{
hidden.insert(section)
tbl.deleteRows(at: indxs(section), with: .fade)
}
}
func cellMethod(cell: UITableViewCell) {
guard let i = tbl.indexPath(for: cell) else { return }
if fv.contains(i){fv.remove(i)}else{fv.insert(i)}
tbl.reloadRows(at: [i], with: .none)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if hidden.contains(section) {
return 0
}
return mlist[section].count
}
func numberOfSections(in tableView: UITableView) -> Int {
return mlist.count
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let sectionButton = UIButton()
sectionButton.setTitle(leagues[section], for: .normal)
sectionButton.backgroundColor = .purple
sectionButton.tag = section
sectionButton.addTarget(self, action: #selector(self.hideSection(sender:)), for: .touchUpInside)
return sectionButton
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! mcell
cell.link = self
cell.textLabel?.text = mlist[indexPath.section][indexPath.row]
if fv.contains(indexPath){
cell.accessoryView?.tintColor = .orange
}else{
cell.accessoryView?.tintColor = .gray
}
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
tbl.register(mcell.self, forCellReuseIdentifier: "cell1")
}
}
import UIKit
class mcell: UITableViewCell{
var link:ViewController?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
let starButton = UIButton(type: .system)
starButton.setImage(#imageLiteral(resourceName: "fav_star"), for: .normal)
starButton.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
starButton.tintColor = .red
starButton.addTarget(self, action: #selector(handleMarkAsFavorite), for: .touchUpInside)
accessoryView = starButton
}
#objc private func handleMarkAsFavorite() {
print(self.textLabel!.text!)
link?.cellMethod(cell: self)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Giving your cell a reference to its controller is a bad pattern.
You're much better off using a closure to let the cell "call back" to the controller when you tap the star.
Here's an update to your cell class:
class mcell: UITableViewCell{
// "callback" closure to tell the controller that the Star was tapped
var starWasTapped: (() -> ())?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
let starButton = UIButton(type: .system)
starButton.setImage(#imageLiteral(resourceName: "fav_star"), for: .normal)
starButton.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
starButton.tintColor = .red
starButton.addTarget(self, action: #selector(handleMarkAsFavorite), for: .touchUpInside)
accessoryView = starButton
}
#objc private func handleMarkAsFavorite() {
print(self.textLabel!.text!)
// tell the controller the Star was tapped
starWasTapped?()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Then, your cellForRowAt will look like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! mcell
cell.textLabel?.text = mlist[indexPath.section][indexPath.row]
if fv.contains(indexPath){
cell.accessoryView?.tintColor = .orange
}else{
cell.accessoryView?.tintColor = .gray
}
// set the cell's "callback" closure
cell.starWasTapped = { [weak self] in
guard let self = self else { return }
if self.fv.contains(indexPath){self.fv.remove(indexPath)}else{self.fv.insert(indexPath)}
self.tbl.reloadRows(at: [indexPath], with: .none)
}
return cell
}
and now you have no need for the separate func cellMethod(...)
I found a solution here and it worked for me https://stackoverflow.com/a/39416618/14061160 , by adding action to the button to force triggering the delegate function (didSelectRowAtIndexPath) and inside the delegate function I apply reloadRows()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! mcell
// cell.link = self
cell.textLabel?.text = mlist[indexPath.section][indexPath.row]
if fv.contains(indexPath){
cell.accessoryView?.tintColor = .orange
}else{
cell.accessoryView?.tintColor = .gray
}
cell.starWasTapped = { [weak self] in
guard let self = self else { return }
if self.fv.contains(indexPath){self.fv.remove(indexPath)}else{self.fv.insert(indexPath)}
self.tbl.delegate!.tableView?(self.tbl, didSelectRowAt: indexPath)
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tbl.reloadRows(at: [indexPath], with: .fade)
}
I have been looking around for a solution or a best way to determine the height of a tableView row in heightForRowAt, that has a tableView based on some conditions in the data model.
When my data model has a data type called MULTISELECT, I need to display a cell with a tableView inside it. There are no problems in doing so. The inner tableView's data is assigned in outer tableView's cellForRowAt.
The question here is how to get the height of my outer tableView row for the MULTISELECT type cells, after the data is populated for the inner tableView rows?
Outer tableView code (inside a ViewController) -
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let preferenceCategories = self.preferenceCategories else {
return UITableViewCell()
}
let categoryCode = preferenceCategories[indexPath.section].code
let filteredPreferenceSet = self.preferenceSet.filter({$0.categoryCode == categoryCode}).filter({$0.dataType == "BOOLEAN"/* || $0.dataType == "MULTISELECT"*/})
if let preferenceDataType = filteredPreferenceSet[indexPath.row].dataType {
if preferenceDataType == "BOOLEAN" {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "CustPrefSetCell", for: indexPath) as! CustPrefSetCell
cell.preferenceName.text = filteredPreferenceSet[indexPath.row].name
cell.preferenceDescription.text = filteredPreferenceSet[indexPath.row].description
cell.switchDelegate = self
let propertyValue = ((filteredPreferenceSet[indexPath.row].value ?? "false") as NSString).boolValue
propertyValue ? cell.preferenceSwitch.setOn(true, animated: true) : cell.preferenceSwitch.setOn(false, animated: true)
cell.preferenceCode = filteredPreferenceSet[indexPath.row].code
return cell
}
else if preferenceDataType == "MULTISELECT" {
let multiSelectCell = self.tableView.dequeueReusableCell(withIdentifier: "CustPrefMultiSelectTableViewCell", for: indexPath) as! CustPrefMultiSelectTableViewCell
multiSelectCell.preferenceValues = filteredPreferenceSet[indexPath.row].preferenceValues
// self.rowHeight = multiSelectCell.tableView.contentSize.height
return multiSelectCell
}
else {
return UITableViewCell()
}
}
else {
return UITableViewCell()
}
}
public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
The inner tableView is inside the multiSelectCell, whose code is below -
class CustPrefMultiSelectTableViewCell: UITableViewCell {
#IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var preferenceDescription: UILabel!
#IBOutlet weak var preferenceTitle: UILabel!
#IBOutlet weak var tableView: UITableView!
var preferenceValues: [PreferenceValue]?
override func awakeFromNib() {
super.awakeFromNib()
self.tableView.delegate = self
self.tableView.dataSource = self
guard let frameworkBundle = Bundle(identifier: "com.frameworkbundle.asdf") else {
fatalError("Framework bundle identifier is incorrect.")
}
let custPrefHeaderCell = UINib(nibName: "CustPrefMultiSelectPreferenceTableViewCell", bundle: frameworkBundle)
self.tableView.register(custPrefHeaderCell, forCellReuseIdentifier: "CustPrefMultiSelectPreferenceTableViewCell")
self.tableView.rowHeight = UITableView.automaticDimension
self.tableView.estimatedRowHeight = 64.0
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
extension CustPrefMultiSelectTableViewCell: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let preferenceValues = self.preferenceValues else {
return 0
}
return preferenceValues.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let preferenceCategories = self.preferenceValues else {
return UITableViewCell()
}
let cell = self.tableView.dequeueReusableCell(withIdentifier: "CustPrefMultiSelectPreferenceTableViewCell", for: indexPath) as! CustPrefMultiSelectPreferenceTableViewCell
cell.preferenceName.text = preferenceCategories[indexPath.row].name
cell.preferenceDescription.text = preferenceCategories[indexPath.row].description
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
I thought of an approach by having a height constraint for the inner tableView, and update the outer tableView height when it is ready/reloaded with data. But where should I implement that logic? With a fixed height of inner tableView, I get an unwanted behavior of scrolling. That need to be avoided.
How do I go further with this?
Thanks in advance!
I think using nested tableView is not the best solution, anyway, I hope this example will help you.
struct Foo {
let strings: [String]
}
class NestedViewController: UIViewController {
let dataSource = [Foo(strings: ["String1", "String2"]),
Foo(strings: ["Long long long long long long long long long long long long long string"])]
let tableView: UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.register(NestedCell.self, forCellReuseIdentifier: NestedCell.identifier)
tableView.tableFooterView = UIView()
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
setupConstraints()
tableView.dataSource = self
tableView.delegate = self
tableView.reloadData()
}
func setupConstraints() {
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
}
extension NestedViewController: UITableViewDelegate & UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: NestedCell.identifier, for: indexPath) as? NestedCell else {
return UITableViewCell()
}
cell.setup(foo: dataSource[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
NestedCell.heightFor(foo: dataSource[indexPath.row])
}
}
class NestedCell: UITableViewCell {
static let identifier = "NestedCell"
let nestedTableView: UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.register(TextCell.self, forCellReuseIdentifier: TextCell.identifier)
tableView.tableFooterView = UIView()
return tableView
}()
private var foo = Foo(strings: [""])
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(nestedTableView)
setConstraints()
nestedTableView.dataSource = self
nestedTableView.delegate = self
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup(foo: Foo) {
self.foo = foo
nestedTableView.reloadData()
}
static func heightFor(foo: Foo) -> CGFloat {
foo.strings.reduce(0) { $0 + TextCell.heightFor(text: $1) }
}
private func setConstraints() {
NSLayoutConstraint.activate([
nestedTableView.topAnchor.constraint(equalTo: contentView.topAnchor),
nestedTableView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
nestedTableView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
nestedTableView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
])
}
}
extension NestedCell: UITableViewDelegate & UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
foo.strings.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: TextCell.identifier, for: indexPath) as? TextCell else {
return UITableViewCell()
}
cell.setup(text: foo.strings[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
TextCell.heightFor(text: foo.strings[indexPath.row])
}
}
class TextCell: UITableViewCell {
static let identifier = "TextCell"
static let labelOffset: CGFloat = 10
private let label: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.font = .systemFont(ofSize: 15, weight: .medium)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(label)
setConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup(text: String) {
label.text = text
}
static func heightFor(text: String) -> CGFloat {
text.height(width: UIScreen.main.bounds.width - 2 * TextCell.labelOffset,
font: .systemFont(ofSize: 15, weight: .medium)) + 2 * TextCell.labelOffset
}
private func setConstraints() {
NSLayoutConstraint.activate([
label.topAnchor.constraint(equalTo: contentView.topAnchor, constant: TextCell.labelOffset),
label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -TextCell.labelOffset),
label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: TextCell.labelOffset),
label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -TextCell.labelOffset)
])
}
}
extension String {
func height(width: CGFloat, font: UIFont) -> CGFloat {
let rect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: rect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
return ceil(boundingBox.height)
}
}
In myscenario, I am trying to add Dynamic UITableview within Static UITableview Cell. How to achieve this? I tried below code but It is not working for me.
The dynamic tableview cell count based need to readjust static tableview cell height. Please provide some sample code.
Tableview Code
import UIKit
class CustomCell: UITableViewCell,UITableViewDataSource,UITableViewDelegate {
var dataArr:[String] = []
var subMenuTable:UITableView?
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style , reuseIdentifier: reuseIdentifier)
setUpTable()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
setUpTable()
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
setUpTable()
}
func setUpTable(){
subMenuTable = UITableView(frame: CGRectZero, style:UITableViewStyle.Plain)
subMenuTable?.delegate = self
subMenuTable?.dataSource = self
self.addSubview(subMenuTable!)
}
override func layoutSubviews() {
super.layoutSubviews()
subMenuTable?.frame = CGRectMake(0.2, 0.3, self.bounds.size.width-5, self.bounds.size.height-5)
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArr.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("cellID")
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cellID")
}
cell?.textLabel?.text = dataArr[indexPath.row]
return cell!
}
}
Create a single view project add tableview inside storyboard and set up its datasource and delegate
do code as below to firstViewController.swift
import UIKit
class firstViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 3;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:CustomCell? = tableView.dequeueReusableCellWithIdentifier("Cell") as? CustomCell
if cell == nil {
cell = CustomCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
}
cell?.dataArr = ["subMenu->1","subMenu->2","subMenu->3","subMenu->4","subMenu->5"]
return cell!
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 150.0
}
}
create a new file CustomCell.swift which is the subclass of UITableViewCell and do not select with xib this file is without .xib file table and its cell will be created programatically.
do code as below to CustomCell.swift
import UIKit
class CustomCell: UITableViewCell,UITableViewDataSource,UITableViewDelegate {
var dataArr:[String] = []
var subMenuTable:UITableView?
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style , reuseIdentifier: reuseIdentifier)
setUpTable()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
setUpTable()
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
setUpTable()
}
func setUpTable(){
subMenuTable = UITableView(frame: CGRectZero, style:UITableViewStyle.Plain)
subMenuTable?.delegate = self
subMenuTable?.dataSource = self
self.addSubview(subMenuTable!)
}
override func layoutSubviews() {
super.layoutSubviews()
subMenuTable?.frame = CGRectMake(0.2, 0.3, self.bounds.size.width-5, self.bounds.size.height-5)
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArr.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("cellID")
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cellID")
}
cell?.textLabel?.text = dataArr[indexPath.row]
return cell!
}
}
I hope it will work for you ...:)
Do this in your main controller:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if tableView = self.tableView {
var cell:CustomCell? = tableView.dequeueReusableCellWithIdentifier("Cell") as? CustomCell
cell.tableView.delegate = self
cell.tableView.dataSource = self
return cell!
} else {
var cell: UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("cellID")//here you custom cell's tableView's cell
return cell
}
}
Basically you will have to calculate height of your child tableview as demonstrated below
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
{
#IBOutlet weak var parentTableView : UITableView? = nil
var sections = [[String:[[String:String]]]]()
override func viewDidLoad()
{
super.viewDidLoad()
setup()
}
func setup()
{
// Data
let section1 = ["Items" : [["Title" : "Row 1"], ["Title" : "Row 2"]]]
let section2 = ["Items" : [["Title" : "Row 1"], ["Title" : "Row 2"], ["Title" : "Row 3"]]]
let section3 = ["Items" : [["Title" : "Row 1"], ["Title" : "Row 2"], ["Title" : "Row 3"], ["Title" : "Row 4"]]]
sections.append(section1)
sections.append(section2)
sections.append(section3)
// Register header
}
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return sections.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
// Calculate the height of parent cell.
var cellHeight : CGFloat = 0.0
let aSection = sections[indexPath.row]
if let items = aSection["Items"]
{
cellHeight = CGFloat(44 * items.count)
}
return cellHeight
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
if let cell = tableView.dequeueReusableCell(withIdentifier: "ParentTableViewCell") as? ParentTableViewCell
{
if let aSection = sections[indexPath.row] as? [String:[[String:String]]], let items = aSection["Items"]
{
cell.configure(items: items)
}
return cell
}
return UITableViewCell()
}
}
Here is the link for the code
I am creating a table view programmatically using custom cell.However when I test using VoiceOver Im getting "empty list" and "content is empty" even though there is value in the cell.Any suggestions?
My tableview -
func createTable(_ choicesArray:[String]){
myTableView = UITableView(frame: CGRect(x: 10, y: self.verticalPostion, width: 340, height: 300))
self.choicesData = choicesArray
myTableView.estimatedRowHeight = 70
myTableView.rowHeight = UITableViewAutomaticDimension
myTableView.allowsMultipleSelection = true
myTableView.register(AnswerCell.self, forCellReuseIdentifier: "cell")
myTableView.dataSource = self
myTableView.delegate = self
myTableView.isAccessibilityElement = false
myTableView.shouldGroupAccessibilityChildren = true
// myTableView.reloadData()
self.view.addSubview(myTableView)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
//more code for cell selection
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return choicesData.count
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, tableView);
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! AnswerCell
cell.choiceLabel.text = self.choicesData[indexPath.row]
return cell
}
Code for Custom Cell -
class AnswerCell: UITableViewCell {
var choiceLabel = UILabel()
func configure(_ description: String) {
choiceLabel.text = description
applyAccessibility()
}
final func applyAccessibility() {
isAccessibilityElement = true
accessibilityLabel = choiceLabel.text!
accessibilityHint = "Double tap to play."
choiceLabel.isAccessibilityElement = false;
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(choiceLabel)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
choiceLabel.frame = CGRect(x: 20, y: 0, width: 70, height: 30)
}
}
I created a Uitableview in a ViewController and I want to set dynamically the size of each cell containing a subview. I try a lot of technics, the only one is using this function :
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 150.0
}
but the value is fixe so a scroll bar appear in my tableview cell and I don't want that, I want to see all the subviews without a scroll bar, here is my code for the ViewController containing the tableview :
import UIKit
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 3;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:CustomCell? = tableView.dequeueReusableCellWithIdentifier("Cell") as? CustomCell
if(cell == nil)
{
cell = CustomCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
}
cell?.dataArr = ["Test 1","Test 2","Test 3","Test 4","Test 5"]
return cell!
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 150.0
}
}
and here is the custom cell :
import UIKit
class CustomCell: UITableViewCell,UITableViewDataSource,UITableViewDelegate {
var dataArr:[String] = []
var subMenuTable:UITableView?
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style , reuseIdentifier: reuseIdentifier)
setUpTable()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
setUpTable()
}
func setUpTable()
{
subMenuTable = UITableView()
subMenuTable?.delegate = self
subMenuTable?.dataSource = self
self.addSubview(subMenuTable!)
}
override func layoutSubviews() {
super.layoutSubviews()
subMenuTable?.frame = CGRectMake(0.2, 0.3, self.bounds.size.width-5, self.bounds.size.height-5)
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArr.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("cellID")
if(cell == nil){
cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cellID")
}
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 160.0
cell?.textLabel?.text = dataArr[indexPath.row]
return cell!
}
}
How can I fixed that ?
Try
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return tableView.cellForRowAtIndexPath(indexPath)?.contentView.frame.size.height
}