swift - Dynamic List of items inside tableViewCell - ios

What is the best practice to add dynamic list of items in UITableViewCell with showMore/showLess?
UITableView inside UITableViewCell
I used table view inside my UITableViewCell , but I have issue with reload row at indexPath causes jumpy scrolling.
TableViewController Code :
class ViewController: UIViewController , UITableViewDelegate , UITableViewDataSource , delegatCell {
#IBOutlet weak var tableView: UITableView!
var elementsArray: [data] = []
override func viewDidLoad() {
super.viewDidLoad()
for i in 1...20 {
elementsArray.append(data.init(title: "title\(i)", date: "8:00 PM", isShowMoreClicked: false, elements: ["ToDo\(i)","Aqraa\(i)","ToDo\(i)","ToDo\(i)" , "Mobile Team\(i)" , "Testing Data\(i)"]))
// Do any additional setup after loading the view, typically from a nib.
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return elementsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let obj = elementsArray[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! expandedTableViewCell
cell.initializeCell()
print(obj.elements)
cell.data = obj.elements
cell.delegate = self
cell.dateLabel.text = obj.date
cell.titleLabel.text = obj.title
cell.showMore.titleLabel?.font = reqularDynamicFont()
cell.showMore.titleLabel?.adjustsFontSizeToFitWidth = true
cell.showMore.tag = indexPath.row
cell.isShowMoreClicked = obj.isShowMoreClicked
cell.tableView.reloadData()
self.view.layoutIfNeeded()
return cell
}
func reqularDynamicFont()-> UIFont{
let font = UIFont.systemFont(ofSize: 15, weight: .regular)
let fontMetrics = UIFontMetrics(forTextStyle: .caption1)
return fontMetrics.scaledFont(for: font)
}
func reloadTableView(indexpath: IndexPath , isShowMoreClicked: Bool) {
elementsArray[indexpath.row].isShowMoreClicked = isShowMoreClicked
tableView.reloadRows(at: [indexpath], with: .automatic)
}
}
ExpandedTableViewCell with inner table view Code :
import UIKit
protocol delegatCell {
func reloadTableView(indexpath: IndexPath , isShowMoreClicked: Bool)
}
class expandedTableViewCell: UITableViewCell , UITableViewDelegate , UITableViewDataSource{
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var showMore: UIButton!
var data : [String] = []
var initalNum = 2
var isShowMoreClicked: Bool = false
var delegate:delegatCell?
func initializeCell(){
data.removeAll()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isShowMoreClicked{
return data.count
}else{
return initalNum
}
}
#IBAction func reloadData(_ sender: UIButton) {
delegate?.reloadTableView(indexpath: IndexPath(row: sender.tag, section: 0), isShowMoreClicked: !isShowMoreClicked)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print(data)
let cell = tableView.dequeueReusableCell(withIdentifier: "simpleTableViewCell", for: indexPath) as! simpleTableViewCell
cell.descLabel.text = data[indexPath.row]
return cell
}
}
I used auto layout to support dynamic height.

Related

Problem with adding tableview into tableview cell swift

I would like to show table view with cell where will be one label and tableview below this label. I have such layout right now:
view above my tables view will be hidden in some conditions so as a result I will have table view on whole screen. So... I found this video where developer managed to solve my task. I did everything similarly to his video but I didn't manage to show table view inside table view cell. Here is my steps:
Added all views to general view.
Attached tags to my table views: 100 for main table view and 90 for inside table view.
Created cell classes and attached them to both cells.
Added extension at main cell like in video.
Handled table view tag in main view controller.
As I see the problem is with inside table view which can't be shown. Below you can see classes for cells. Main cell:
class GeneralCell : UITableViewCell {
#IBOutlet weak var answerOptions: UITableView!
#IBOutlet weak var questionText: UILabel!
}
extension GeneralCell{
func setTableViewDataSourceDelegate <D:UITableViewDelegate & UITableViewDataSource> (_ dataSourceDelegate: D, forRow row: Int)
{
answerOptions.delegate = dataSourceDelegate
answerOptions.dataSource = dataSourceDelegate
answerOptions.reloadData()
}
}
inside cell:
class AnswersCell: UITableViewCell {
#IBOutlet weak var answerOption: UILabel!
}
and here is my view controller:
class PollsController: UIViewController, UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var questionTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
questionTableView.delegate = self
questionTableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print(tableView.tag)
if tableView.tag == 100 {
let cell: GeneralCell = tableView.dequeueReusableCell(withIdentifier: "GeneralCell") as! GeneralCell
cell.answerOptions.reloadData()
return cell
}else{
let cell: AnswersCell = tableView.dequeueReusableCell(withIdentifier: "AnswersCell") as! AnswersCell
return cell
}
}
func numberOfSections(in tableView: UITableView) -> Int {
if tableView.tag == 100 {
return 1
}else{
return 6
}
}
}
Then I did some test for getting to know where the problem is. I added print() into data loader function:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print(tableView.tag)
if tableView.tag == 100 {
print("1")
let cell: GeneralCell = tableView.dequeueReusableCell(withIdentifier: "GeneralCell") as! GeneralCell
cell.answerOptions.reloadData()
return cell
}else{
print("2")
let cell: AnswersCell = tableView.dequeueReusableCell(withIdentifier: "AnswersCell") as! AnswersCell
return cell
}
}
and in the output I saw only one tag:
100
and as I see inside table view can't be loaded but I can't understand why. Maybe someone of you will find problem or mistake?
I suggest you use only one tableview, here some example
struct Answer {
let answerText: String
}
struct Question {
let questionText: String
let answers: [Answer]
}
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var questions: [Question] = []
override func viewDidLoad() {
super.viewDidLoad()
for i in 1...10 {
let question = Question(questionText: "Question \(i)", answers: [
Answer(answerText: "Answer 1 Question \(i)"),
Answer(answerText: "Answer 2 Question \(i)"),
Answer(answerText: "Answer 3 Question \(i)"),
Answer(answerText: "Answer 4 Question \(i)")
])
questions.append(question)
}
tableView.dataSource = self
tableView.delegate = self
tableView.sectionHeaderHeight = UITableView.automaticDimension;
tableView.estimatedSectionHeaderHeight = 44
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return questions.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return questions[section].answers.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "AnswerCell", for: indexPath) as! AnswerCell
cell.answerLabel.text = questions[indexPath.section].answers[indexPath.row].answerText
return cell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionCell") as! QuestionCell
cell.questionLabel.text = questions[section].questionText
return cell
}
}
Try this
class GeneralCell : UITableViewCell {
#IBOutlet weak var answerOptions: UITableView!
#IBOutlet weak var questionText: UILabel!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print(tableView.tag)
if tableView == questionTableView{
let cell: GeneralCell = tableView.dequeueReusableCell(withIdentifier: "GeneralCell") as! GeneralCell
cell.answerOptions.delegate = self
cell.answerOptions.dataSource = self
cell.answerOptions.reloadData()
return cell
}else{
let cell: AnswersCell = tableView.dequeueReusableCell(withIdentifier: "AnswersCell") as! AnswersCell
return cell
}
}

What is the correct way to display random images on table view cell

I was trying to display random images on table view cell but when i scroll down and up images positions are changed.
I just added image on cell
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
let imagesArr = ["img1","img2","img3","img4"]
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController: UITableViewDataSource,UITabBarDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let randomNumber = Int.random(in: 0...3)
let cell = tableView.dequeueReusableCell(withIdentifier: "ImageCell") as! ImageCell
cell.ImageView.image = UIImage(named: self.imagesArr[randomNumber])
return cell
}
}
I expect to get images where they were before scroll down and up
If you want to set the image once and for all, you can do the following:
class MyCell: UICollectionViewCell {
func awakeFromNib() {
super.awakeFromNib()
let randomNumber = Int.random(in: 0...3)
let imagesArr = ["img1","img2","img3","img4"]
ImageView.image = UIImage(named: imagesArr[randomNumber])
}
}
if random images should be changed if only you leave the page, then add random number in the viewDidLoad()
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
let imagesArr = ["img1","img2","img3","img4"]
var randomNumber = 0
override func viewDidLoad() {
super.viewDidLoad()
randomNumber = Int.random(in: 0...3)
}
}
extension ViewController: UITableViewDataSource,UITabBarDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ImageCell") as! ImageCell
cell.ImageView.image = UIImage(named: self.imagesArr[randomNumber])
return cell
}

Delegate method is not being called and giving a nil value

For some reason, the delegate method is not being called. I am printing the delegate property in the console but it returns nil. I was looking for other answers here, but none of them were helpful for me.
protocol FilterSwitchDelegate {
func switchValueChanged(tagValue:Int)
}
class FilterTableViewCell: UITableViewCell {
#IBOutlet weak var filterLabel: UILabel!
#IBOutlet weak var filterSwitch: UISwitch!
var delegate:FilterSwitchDelegate?
#IBAction func filterSwitchValueChanged(_ sender: UISwitch) {
print(sender.tag)
delegate?.switchValueChanged(tagValue: sender.tag)
}
}
//In FilterViewController I am calling this delegate method
class FilterViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var dataSource = FilterTableViewCell()
var filterModel = [FilterVCDataModel]()
var ids = [Int]()
override func viewDidLoad() {
super.viewDidLoad()
dataSource.delegate = self
}
}
extension FilterViewController: FilterSwitchDelegate {
func switchValueChanged(tagValue: Int) {
ids.remove(at: tagValue)
print(ids)
}
}
TableView dataSource and delegate methods
extension FilterViewController:UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filterModel.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "filterCell", for: indexPath) as! FilterTableViewCell
cell.filterSwitch.tag = indexPath.row
cell.filterLabel.text = filterModel[indexPath.row].filterData["title"] as? String
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 66
}
}
in the cellForRow you have to dequeue your cell.
let cell = tableView.dequeueReusableCell(withIdentifier: "yourId") as? FilterTableViewCell
cell.delegate = self
return cell
and the job is done

How to navigate from MasterView's TableViewCell (Xib Cell) to DetailView's TableView

I have a master-detail view application in which MasterView's table cell is a Xib cell. I also have two TableViews in DetailViewController placed side by side as shown below:
Master and Detail Views
Now my problem is I am unable to populate DetailView's Table View when I am clicking on Master's TableViewCell. I am pasting my code below:
class MasterViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var devarlViewController: DetailViewController? = nil
var objects = [Any]()
var statesList = ["AL", "GA", "AK", "AR", "AZ", "CA", "CO", "CT", "CO"]
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "cell")
// Do any additional setup after loading the view, typically from a nib.
}
// MARK: - Table View
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return statesList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel!.text = statesList[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
DetailViewController.sharedDetail.selectedState = (self.tableView(tableView, cellForRowAt: indexPath).textLabel?.text)!
DetailViewController.sharedDetail.selectedCity = ""
}
}
class DetailViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
static let sharedDetail = DetailViewController()
#IBOutlet weak var CitiesTable: UITableView!
#IBOutlet weak var PlacesTable: UITableView!
var selectedState: String = ""{
didSet{
CitiesTable.reloadData()
}
}
var selectedCity: String = ""
var citiesStates: [String:[String]] = [ "AL": ["Auburn", "Montgomery", "Birmingham"]
]
var placesCities: [String:[String]] = [ "Auburn": ["Dominos", "Pizzahut", "McDonalds"],
"Birmingham": ["iHop", "Coldstone", "WaffleHouse"]
]
var cities: [String] = []
var places: [String] = []
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 numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(tableView == CitiesTable)
{
if(selectedState != "")
{
cities = citiesStates[selectedState]!
return cities.count
}
}
else
{
if(selectedCity != "")
{
places = placesCities[selectedCity]!
return places.count
}
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(tableView == CitiesTable)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "citiesCell", for: indexPath)
cell.textLabel?.text = cities[indexPath.row]
return cell
}
else
{
let cell = tableView.dequeueReusableCell(withIdentifier: "placesCell", for: indexPath)
cell.textLabel?.text = cities[indexPath.row]
return cell
}
}
}
I have added a protocol in masterviewcontroller shown below
protocol MasterSelectionDelegate: class {
func itemSelected(_ item: AnyObject)
}
then, as splitviewcontroller sits as a rootviewcontroller.
In app delegate or during creation of Split View Controller. I have added below line
masterViewController.delegate = detailViewController
In didSelectRowAtIndexPath of master ViewController I have called it this:
`override func tableView(tableView: UITableView,
didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedItem = self.arrString[indexPath.row]
self.delegate?.itemSelected(selectedItem)
}`
Finally you have to implement it in the delegate in detail view controller
func itemSelected(_ item: AnyObject) {
itemSelected = item as? Array<String>
tableview.reloadData()
}

tableview cell not clicking

UITableViewCell sub class:
class MenuDrawerTableViewCell: UITableViewCell {
#IBOutlet weak var Label3: UILabel!
#IBOutlet weak var Image30: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}}
I have that class, now I got my viewController which has a table.
class OpenMenu: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableV: UITableView!
#IBOutlet weak var label1: UILabel!
#IBOutlet weak var label2: UILabel!
var selected = 0
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("prepare")
var DestVC = segue.destination as! CheckinController
var indexPath: NSIndexPath = self.tableV.indexPathForSelectedRow! as NSIndexPath
let menuItem = menuList[indexPath.row]
DestVC.varView = menuItem.index
}
let newSwiftColor = UIColor(red: CGFloat(255), green: CGFloat(0), blue: CGFloat(238), alpha: CGFloat(1))
var menuList = [Menu]()
func loadMenu() {
let photo1 = UIImage(named: "checkin")!
let menu1 = Menu(name: "Check In", photo: photo1, index: 20)!
let photo2 = UIImage(named: "checkin")!
let menu2 = Menu(name: "Add Store", photo: photo2, index: 10)!
menuList += [menu1, menu2]
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menuList.count // your number of cell here
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "MenuDrawerTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath as IndexPath) as! MenuDrawerTableViewCell
let menuItem = menuList[indexPath.row]
cell.Image30.image = menuItem.photo
cell.Label3.text = menuItem.name
cell.Label3.numberOfLines = 1;
if(0 == selected)
{
selected = 1
self.tableV.selectRow(at: indexPath, animated: true, scrollPosition: UITableViewScrollPosition.none)
}
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath) {
// cell selected code here
print("cell selected")
print(indexPath)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MenuDrawerTableViewCell", for: indexPath as IndexPath) as UITableViewCell
return cell
}
#IBOutlet weak var Open: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
loadMenu()
self.tableV.delegate = self
self.tableV.dataSource = self
let size:CGFloat = 35.0 // 35.0 chosen arbitrarily
let preferences = UserDefaults.standard
let name1 = ""+preferences.string(forKey: "name")!
label2.text = name1
label1.text = String(name1[name1.startIndex])
label2.textColor = UIColor.white
label1.textColor = UIColor.white
label1.textAlignment = NSTextAlignment.center
label1.font = UIFont.systemFont(ofSize: 17)
label1.bounds = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: size, height: size))
label1.layer.cornerRadius = size / 2
label1.layer.borderWidth = 3.0
label1.layer.backgroundColor = newSwiftColor.cgColor
label1.layer.borderColor = UIColor.black.cgColor
}
override func viewDidAppear(_ animated: Bool) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}}
What I want, is detect the click on a tableview. But I am not able to detect anything .I supposedly should get cell selected printed, but i am getting nothing.Why the click function not getting called?
I think you need to selection of tableview as below :
You can check your code you have to implemented two time methods(cellForRowAtIndexPath and cellForRowAt)
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
}
First make sure that you have connected tableview and connected delegate and datasource also.
Please remove these lines of code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MenuDrawerTableViewCell", for: indexPath as IndexPath) as UITableViewCell
return cell
}
As already you implemented this method.
Hope this will fix your problem.
I had this problem and noticed that somehow "isEditing" was set to "true" for the tableView in my viewDidLoad code... removing that line of code fixed it.

Resources