Swift UITableViewAutomaticDimension is not working - swift

I have got this table and cell
tableView = UITableView()
tableView.dataSource = self
tableView.delegate = self
tableView.allowsSelection = false;
tableView.estimatedRowHeight = 200
tableView.rowHeight = UITableViewAutomaticDimension
var postTexts = ["Nirvana"]
var posters = ["Champagnepapi"]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return postTexts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let ProfilePicture = UIImageView(frame: CGRect(x:19, y: 14, width: 50, height: 50))
ProfilePicture.image = #imageLiteral(resourceName: "cole")
ProfilePicture.layer.cornerRadius = 25
ProfilePicture.layer.masksToBounds = true
let username = UILabel(frame: CGRect(x: 80, y: 14, width: 300, height: 30))
username.text = posters[indexPath.row]
let postText = UITextView(frame: CGRect(x:75,y:40,width:200,height:200))
postText.text = "I rather be dead than cool - Kurt Cobain"
let border = UIView(frame: CGRect(x:0,y:150,width:350,height:1))
border.backgroundColor = UIColor(r: 217, g: 221, b: 219)
cell.addSubview(ProfilePicture)
cell.addSubview(username)
cell.addSubview(postText)
cell.addSubview(border )
return cell
}
I wanted my cell to have automatic height but this it the result i get
How can i get an automatic height for my cells?

You needs to set the constraints on your cell objects like this so the cell can calculate the size needed.

Related

Tableview cell randomly place string in empty textview

In my swift code below there is a add button pressed to add another tableview cell. The problem is that in the sixth tableview cell that is added the number 1 is just randomly there and this problem would repeat every time another cell is added. You can see what I am talking about in the gif below. What I am looking for in a answer is just getting the cell added with nothining in the text view.
import UIKit
/*
*/
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
var addBTN = UIButton()
var arr = [nil] as [Any?]
var currentIndex = 0
var arrayValues = [Int]()
var index = Int()
var pic = UIImageView()
var cellCount = 0
var tableview = UITableView()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arr.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 118
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTv
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
[addBTN].forEach{
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
}
tableview.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height * 0.8)
addBTN.frame = CGRect(x: view.frame.width / 2, y: view.frame.height * 0.8, width: view.frame.width / 2 , height: view.frame.height / 5)
addBTN.backgroundColor = .systemTeal
view.addSubview(tableview)
tableview.register(CustomTv.self, forCellReuseIdentifier: "cell")
tableview.delegate = self
tableview.dataSource = self
pic.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height )
addBTN.addTarget(self, action: #selector(newCell), for: .touchDown)
addBTN.setTitle("New cell", for: .normal)
}
#objc func newCell(){
cellCount += 1 // update the value
arr.append(1)
tableview.beginUpdates()
tableview.insertRows(at: [IndexPath(row: arr.count - 1, section: 0)], with: .automatic) /// animate the insertion
tableview.endUpdates()
}
}
class CustomTv: UITableViewCell {
lazy var backView : UIView = {
let view = UIView(frame: CGRect(x: 10, y: 6, width: self.frame.width , height: 110))
view.backgroundColor = .green
return view
}()
lazy var txt : UITextField = {
let btn = UITextField(frame: CGRect(x: 150-25, y: 60, width: 100 , height: 50))
btn.backgroundColor = .orange
return btn
}()
override func layoutSubviews() {
backView.clipsToBounds = true
backView.frame = CGRect(x: 0, y: 6, width: bounds.maxX , height: 110)
addSubview(backView)
backView.addSubview(txt)
}
}
implement the prepareForReuse method inside the cell class
and within the method clear the text field
override func prepareForReuse() {
// clear the text field
}

delete tableviewcell from button within inside the cell

I want my swift code to delete the tableview cell within the cell using the blue button. You can see a example of what I am trying to below in the gif. I also have a indexPath selection var. You should probably have to delete it in the deleteCell func also I assume you would use a tag. All the code is in below no need for storyboard.
import UIKit
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource, UIImagePickerControllerDelegate & UINavigationControllerDelegate {
var arr = [1,1,3,3]
var tableview = UITableView()
var selectedIndexPath = IndexPath(row: 0, section: 0)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 118
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! customtv
return cell
}
#objc func deleteCell(){
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableview.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height * 0.8)
view.addSubview(tableview)
tableview.register(customtv.self, forCellReuseIdentifier: "cell")
tableview.delegate = self
tableview.dataSource = self
}
}
class customtv: UITableViewCell {
lazy var backView : UIView = {
let view = UIView(frame: CGRect(x: 10, y: 6, width: self.frame.width , height: 110))
view.backgroundColor = .green
return view
}()
lazy var btn : UIButton = {
let btn = UIButton(frame: CGRect(x: 50, y: 6, width: 100 , height: 110))
btn.backgroundColor = .blue
return btn
}()
override func layoutSubviews() {
backView.clipsToBounds = true
backView.frame = CGRect(x: 0, y: 6, width: bounds.maxX , height: 110)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(animated, animated: true)
addSubview(backView)
backView.addSubview(btn)
}
}
Here's my solution:
import UIKit
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource, CustomTvCellDelegate, UIImagePickerControllerDelegate & UINavigationControllerDelegate {
var cellCount = 2
var tableview = UITableView()
var selectedIndexPath = IndexPath(row: 0, section: 0)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellCount // use a variable here so you can change it as you delete cells, else it will crash
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 118
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTv
cell.delegate = self
return cell
}
func deleteCell(_ customTV: CustomTv, location: CGPoint) {
// put the button location in an index path array
var deleteArray = [IndexPath]()
if let indexPath = tableview.indexPathForRow(at: location) {
deleteArray.append(indexPath)
}
cellCount -= 1 // update the row count as you delete a cell
// delete the row using the delete array with a fade animation
tableview.deleteRows(at: deleteArray, with: .fade)
}
override func viewDidLoad() {
super.viewDidLoad()
tableview.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height * 0.8)
view.addSubview(tableview)
tableview.register(CustomTv.self, forCellReuseIdentifier: "cell")
tableview.delegate = self
tableview.dataSource = self
}
}
// you need to declare a cell delegate that will let your tableview know when the button was tapped on the cell
protocol CustomTvCellDelegate: AnyObject {
func deleteCell(_ customTV: CustomTv, location: CGPoint)
}
class CustomTv: UITableViewCell { // it's good form to Pascal case your class name ;)
weak var delegate: CustomTvCellDelegate? // make that delegate a property of your cell
lazy var backView : UIView = {
let view = UIView(frame: CGRect(x: 10, y: 6, width: self.frame.width , height: 110))
view.backgroundColor = .green
return view
}()
lazy var btn : UIButton = {
let btn = UIButton(frame: CGRect(x: 50, y: 6, width: 100 , height: 110))
btn.backgroundColor = .blue
return btn
}()
override func layoutSubviews() {
backView.clipsToBounds = true
backView.frame = CGRect(x: 0, y: 6, width: bounds.maxX , height: 110)
// moved these calls here instead of on setSelected(_selected:animated:)
addSubview(backView)
backView.addSubview(btn)
btn.addTarget(self, action:#selector(self.deleteCell), for: .touchUpInside)
}
#objc func deleteCell(sender: UIButton){
// let your cell delegate (tableview) know what the button got tapped, you need to pass the button here)
let buttonLocation = sender.superview?.convert(sender.frame.origin, to: nil) ?? CGPoint.zero
delegate?.deleteCell(self, location: buttonLocation)
}
}

add together all of the labels in tableview cells

i want my swift code to add together all of the tableview cells together and convert it from a string to a int. The code should be called from the view did load func. The total should be 3 when all of the labels are added together. Look at my comment in view did load for more info. The code does not use storyboards.
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var numberOfRows = 3
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { numberOfRows }
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 118 }
var tableView = UITableView()
var selectedIndexPath = IndexPath(row: 0, section: 0)
override func viewDidLoad() {
super.viewDidLoad()
setTableVIew()
//print the sum of the labels added togther on the tableview cells
}
func setTableVIew(){
let VCframe = view.frame
let height = VCframe.height * 0.8
let widthx = VCframe.width
tableView.frame = CGRect(x: 10, y: 0, width: widthx - 20, height: height)
tableView.delegate = self
tableView.dataSource = self
view.addSubview(tableView)
tableView.register(customtv.self, forCellReuseIdentifier: "cell")
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! customtv
cell.lbl.text = "\(indexPath.row)"
return cell
}
}
class customtv: UITableViewCell {
lazy var backView : UIView = {
let view = UIView(frame: CGRect(x: 10, y: 6, width: self.frame.width , height: 110))
view.backgroundColor = .green
print(self.frame.width)
return view
}()
override func layoutSubviews() {
backView.clipsToBounds = true
backView.frame = CGRect(x: 0, y: 6, width: bounds.maxX , height: 110)
}
lazy var lbl : UILabel = {
let press = UILabel(frame: CGRect(x: 0, y: 3, width: 120 , height: 50))
press.backgroundColor = .yellow
press.text = String("1")
return press
}()
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(animated, animated: true)
addSubview(backView)
backView.addSubview(lbl)
}
}
Here's how you do this:
override func viewDidLoad() {
super.viewDidLoad()
setTableView()
calculateSum()
}
func calculateSum() {
var sum = 0
for row in 0..<numberOfRows {
let indexPath = IndexPath(row: row, section: 0)
let cell = tableView.cellForRow(at: indexPath) as! CustomTableViewCell
sum += Int(cell.label.text ?? "") ?? 0
}
print(sum)
}
Note: Make the class and object names consistent eg: lbl -> label and customtv -> CustomTableViewCell as I've given above. Even the method names setTableVIew -> setTableView. It would be best if you use SwiftLint.

Print indexpath row on label in each table view cell

In my swift code below each table view cell features a label. In that label I want to print the index path row. So in cell one the label should be 1 in cell to it should read 2. I tried to code cell.textLabel?.text = "(indexPath.row)" but that code is not complying. The code uses no storyboards all code.
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var numberOfRows = 3
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { numberOfRows }
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 118 }
var tableView = UITableView()
var selectedIndexPath = IndexPath(row: 0, section: 0)
override func viewDidLoad() {
super.viewDidLoad()
setTableVIew()
}
func setTableVIew(){
let VCframe = view.frame
let height = VCframe.height * 0.8
let widthx = VCframe.width
tableView.frame = CGRect(x: 10, y: 0, width: widthx - 20, height: height)
tableView.delegate = self
tableView.dataSource = self
view.addSubview(tableView)
tableView.register(customtv.self, forCellReuseIdentifier: "cell")
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! customtv
cell.textLabel?.text = "\(indexPath.row)"
return cell
}
}
class customtv: UITableViewCell {
lazy var backView : UIView = {
let view = UIView(frame: CGRect(x: 10, y: 6, width: self.frame.width , height: 110))
view.backgroundColor = .green
print(self.frame.width)
return view
}()
override func layoutSubviews() {
backView.clipsToBounds = true
backView.frame = CGRect(x: 0, y: 6, width: bounds.maxX , height: 110)
}
lazy var lbl : UILabel = {
let press = UILabel(frame: CGRect(x: 0, y: 3, width: 120 , height: 50))
press.backgroundColor = .yellow
press.text = String("1")
return press
}()
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(animated, animated: true)
addSubview(backView)
backView.addSubview(lbl)
}
}
You are almost there :)
You have a custom cell, so you need to set the value of custom label you created, instead of cell.textLabel?.text.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! customtv
//cell.textLabel?.text = "\(indexPath.row)" // Don't Do this.
cell.lbl.text = "\(indexPath.row)" // Do this.
return cell
}
To clarify, your cells will start with values 0, 1, 2. IndexPath.row is 0 based.

Anchoring UILabel in UITableView section header

I'm getting a misalignment of a UILabel (variable name: wrongCountLabel) in a UITableView section header, as it's programatically set to a fixed x and y coordinate. These coordinates work fine for smaller screens but fall short of the right hand side on larger screens (see screen dumps below).
Since I created the section header in code I've tried to programatically anchor the trailing edge of the "Wrong (times)" label to the trailing edge of UITableView. When I run the widget, it says it's unable to load the data.
//
// TodayViewController.swift
// Widget
//
// Created by on 10/02/2019.
// Copyright © 2019. All rights reserved.
//
import UIKit
import NotificationCenter
class TodayViewController: UIViewController, NCWidgetProviding, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var words = [String]()
var sortedPracticeWords = [String]()
var chosenLanguage = String()
let wordsString = "Words"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view from its nib.
if let defaults = UserDefaults(suiteName: "group.co.uk.tirnaelectronics.polyglot") {
if let savedLanguage = defaults.object(forKey: "languageChosen") as? String {
print("savedLanguage is: \(savedLanguage)")
chosenLanguage = savedLanguage
if let savedWords = defaults.object(forKey: "\(chosenLanguage)\(wordsString)") as? [String] {
words = savedWords
}
}
}
extensionContext?.widgetLargestAvailableDisplayMode = .expanded
sortPracticeWords()
}
func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) {
if activeDisplayMode == .compact {
preferredContentSize = CGSize(width: 0, height: 110)
} else {
preferredContentSize = CGSize(width: 0, height: 440)
}
}
func widgetPerformUpdate(completionHandler: (#escaping (NCUpdateResult) -> Void)) {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResult.Failed
// If there's no update required, use NCUpdateResult.NoData
// If there's an update, use NCUpdateResult.NewData
completionHandler(NCUpdateResult.newData)
}
func sortPracticeWords() {
var practiceWords = [String]()
for i in 1..<words.count {
if Int(words[i - 1].components(separatedBy: "::")[2]) ?? 0 > 0 {
practiceWords.append(words[i - 1])
}
}
var sortedAboveIndex = practiceWords.count
var swaps = 0
var tempPracticeWord = String()
repeat {
var lastSwapIndex = 0
for i in 1..<sortedAboveIndex {
if Int(practiceWords[i - 1].components(separatedBy: "::")[2])! < Int(practiceWords[i].components(separatedBy: "::")[2])! {
tempPracticeWord = practiceWords[i]
practiceWords[i] = practiceWords[i - 1]
practiceWords[i - 1] = tempPracticeWord
lastSwapIndex = i
swaps += 1
}
}
sortedAboveIndex = lastSwapIndex
} while (sortedAboveIndex != 0)
sortedPracticeWords = practiceWords
print("sortedPracticeWords are: \(sortedPracticeWords)")
print("practiceWords is sorted in \(swaps) swaps.")
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sortedPracticeWords.count
}
internal func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView()
view.backgroundColor = UIColor(white: 1, alpha: 0.2)
let languageLabel = UILabel()
languageLabel.text = "\(chosenLanguage.capitalized)"
languageLabel.frame = CGRect(x: 10, y: 5, width: 170, height: 20)
//languageLabel.leadingAnchor.constraint(equalTo: tableView.layoutMarginsGuide.leadingAnchor).isActive = true
view.addSubview(languageLabel)
let wrongCountLabel = UILabel()
wrongCountLabel.text = "Wrong (times)"
wrongCountLabel.textAlignment = .left
wrongCountLabel.frame = CGRect(x: 180, y: 5, width: 120, height: 20)
//wrongCountLabel.trailingAnchor.constraint(equalTo: tableView.layoutMarginsGuide.trailingAnchor).isActive = true
view.addSubview(wrongCountLabel)
return view
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 30
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:TodayCustomCell = self.tableView.dequeueReusableCell(withIdentifier: "PracticeWord") as! TodayCustomCell
let sortedPracticeWord = sortedPracticeWords[indexPath.row]
print("practiceWord is: \(sortedPracticeWord)")
let split = sortedPracticeWord.components(separatedBy: "::")
cell.practiceWord.textColor = UIColor(white: 1, alpha: 0.75)
cell.selectedBackgroundView = UIView()
cell.selectedBackgroundView!.backgroundColor = UIColor(white: 1, alpha: 0.20)
cell.practiceWord.frame = CGRect(origin: CGPoint(x: 10, y: 10), size: CGSize(width: 200, height: 40))
cell.wrongCount.frame = CGRect(origin: CGPoint(x: 210, y: 10), size: CGSize(width: 100, height: 40))
cell.practiceWord?.text = split[1]
cell.wrongCount?.text = split[2]
print("cell is: \(cell)")
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
/*if let cell = tableView.cellForRow(at: indexPath) {
let practiceWord = sortedPracticeWords[indexPath.row]
let split = practiceWord.components(separatedBy: "::")
cell.detailTextLabel?.text = split[2]
print("Detail cell is: \(cell)")
}*/
}
}
The right hand screen should display like the left hand screen, only on a bigger screen.
I need some code that anchors UILabels so they work regardless of user's screen size.
Would changing:
wrongCountLabel.textAlignment = .left
To
wrongCountLabel.textAlignment = .right
Help?