Layout and sizing issues with TableViewCells that change height based on content inside - Swift 3 - ios

I'm building a simple messenger app that uses a tableview to display the messages. Each cell contains text and a stretchable background image. When messages are added to the tableview, they do change height to accommodate the text. However, whenever a single-line message is entered, the table view cell appears to be way too long for just a single line of text.
I think it has to do with the initial height and width of the tableviewcell, but I am not sure. How can I fix this to ensure the text bubble image encompasses the text but does not expand too much over it?
Screenshot of single and multi-lined texts:
Screenshot of long single-lined text for comparison:
I am using auto layout, if it helps.
ViewController code:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
var texts: [String] = ["Hey, how are you?", "Good, you?", "Great!"]
var user: [Int] = [1, 0, 1]
let screenSize = UIScreen.main.bounds
#IBOutlet weak var tableView: UITableView!
#IBAction func sendMessage(_ sender: Any) {
if textBox.text != ""
{
let str:String = textBox.text
let retstr:String = insert(seperator: "\n", afterEveryXChars: 27, intoString: str)
let rand:UInt32 = arc4random_uniform(2)
addText(text: String(retstr), user: Int(rand))
}
}
#IBOutlet weak var textBox: UITextView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.texts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (self.user[indexPath.row]==1)
{
let cell:CustomCell = self.tableView.dequeueReusableCell(withIdentifier: "cell") as! CustomCell
cell.myCellLabel.text = self.texts[indexPath.row]
cell.myCellLabel.textAlignment = NSTextAlignment.left
cell.myCellLabel.sizeToFit()
cell.myBackgroundImage.image = UIImage(named: "bubbleReversed")?.resizableImage(withCapInsets: UIEdgeInsetsMake(60, 50, 60, 50))
return cell
}
else
{
let cell:CustomCellOther = self.tableView.dequeueReusableCell(withIdentifier: "cell2") as! CustomCellOther
cell.myCellLabel.text = self.texts[indexPath.row]
cell.myCellLabel.textAlignment = NSTextAlignment.right
cell.myCellLabel.sizeToFit()
cell.myBackgroundImage.image = UIImage(named: "bubble")?.resizableImage(withCapInsets: UIEdgeInsetsMake(60, 50, 60, 50))
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped cell number \(indexPath.row).")
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = 10.0
tableView.rowHeight = UITableViewAutomaticDimension
tableView.reloadData()
}
func addText(text:String, user:Int)
{
if (self.texts.count > 20)
{
self.texts.remove(at: 0)
self.user.remove(at: 0)
}
self.texts.append(text)
self.user.append(user)
tableView.reloadData()
let indexPath = NSIndexPath(row: self.texts.count-1, section: 0)
tableView.scrollToRow(at: indexPath as IndexPath, at: .top, animated: true)
}
func insert(seperator: String, afterEveryXChars: Int, intoString: String) -> String {
var output = ""
intoString.characters.enumerated().forEach { index, c in
if index % afterEveryXChars == 0 && index > 0 {
output += seperator
}
output.append(c)
}
return output
}
}
My tableviewcell classes just contain a UIImageView and a label.

Related

TableView Collapse, why it's sticking up like this?

I'm setting up a collapsable tableView, but something strange happens on the collapsable item. When you look at the video keep an eye on the "Where are you located" line.. (I'm using a .plist for the question and answer items)
Where do I go wrong, is it somewhere in my code? I don't want to let that line stick on the top :(
Here is the code I'm using but I can't find anything strange...
class FAQViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var questionsArray = [String]()
var answersDict = Dictionary<String, [String]>() // multiple answers for a question
var collapsedArray = [Bool]()
#IBOutlet weak var tableView: UITableView!
override func viewWillAppear(_ animated: Bool) {
// Hide the navigation bar on the this view controller
self.navigationController?.setNavigationBarHidden(true, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
addTableStyles()
readQAFile()
tableView.delegate = self
tableView.dataSource = self
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
func addTableStyles(){
navigationController?.isNavigationBarHidden = false
self.tableView?.backgroundView = {
let view = UIView(frame: self.tableView.bounds)
return view
}()
tableView.estimatedRowHeight = 43.0;
tableView.rowHeight = UITableView.automaticDimension
tableView.separatorStyle = UITableViewCell.SeparatorStyle.singleLine
}
func readQAFile(){
guard let url = Bundle.main.url(forResource: "QA", withExtension: "plist")
else { print("no QAFile found")
return
}
let QAFileData = try! Data(contentsOf: url)
let dict = try! PropertyListSerialization.propertyList(from: QAFileData, format: nil) as! Dictionary<String, Any>
// Read the questions and answers from the plist
questionsArray = dict["Questions"] as! [String]
answersDict = dict["Answers"] as! Dictionary<String, [String]>
// Initially collapse every question
for _ in 0..<questionsArray.count {
collapsedArray.append(false)
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return questionsArray.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if collapsedArray[section] {
let ansCount = answersDict[String(section)]!
return ansCount.count
}
return 0
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
// Set it to any number
return 70
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if collapsedArray[indexPath.section] {
return UITableView.automaticDimension
}
return 2
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView(frame: CGRect(x:0, y:0, width:tableView.frame.size.width, height:40))
headerView.tag = section
let headerString = UILabel(frame: CGRect(x: 10, y: 10, width: tableView.frame.size.width, height: 50)) as UILabel
headerString.text = "\(questionsArray[section])"
headerView .addSubview(headerString)
let headerTapped = UITapGestureRecognizer (target: self, action:#selector(sectionHeaderTapped(_:)))
headerView.addGestureRecognizer(headerTapped)
return headerView
}
#objc func sectionHeaderTapped(_ recognizer: UITapGestureRecognizer) {
let indexPath : IndexPath = IndexPath(row: 0, section:recognizer.view!.tag)
if (indexPath.row == 0) {
let collapsed = collapsedArray[indexPath.section]
collapsedArray[indexPath.section] = !collapsed
//reload specific section animated
let range = Range(NSRange(location: indexPath.section, length: 1))!
let sectionToReload = IndexSet(integersIn: range)
self.tableView.reloadSections(sectionToReload as IndexSet, with:UITableView.RowAnimation.fade)
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cellIdentifier = "Cell"
let cell: UITableViewCell! = self.tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
cell.textLabel?.adjustsFontSizeToFitWidth = true
cell.textLabel?.numberOfLines = 0
let manyCells : Bool = collapsedArray[indexPath.section]
if (manyCells) {
let content = answersDict[String(indexPath.section)]
cell.textLabel?.text = content![indexPath.row]
}
return cell
}
override var prefersStatusBarHidden: Bool {
return true
}
}
You need to change the style of the tableView to grouped, when you initialize it:
let tableView = UITableView(frame: someFrame, style: .grouped)
or from Storyboard:
After that you will have this issue, which I solved by setting a tableHeaderView to the tableView that has CGFloat.leastNormalMagnitude as its height:
override func viewDidLoad() {
super.viewDidLoad()
var frame = CGRect.zero
frame.size.height = .leastNormalMagnitude
tableView.tableHeaderView = UIView(frame: frame)
}
Just remove your headerView from view hierarchy here
#objc func sectionHeaderTapped(_ recognizer: UITapGestureRecognizer) {
headerView.removeFromSuperview()
...
}
By the way, yes creating a openable tableview menu with using plist is one of the methods but it could be more simple. In my opinion you should refactor your code.

Deleting a custom view from UITableView

I am swiping left to delete a cell that is a customview in Swift 3.
The cell is:
class CustomTableCell: SwipeTableViewCell
{
public var atest = UILabel();
public var btest = UILabel();
var animator: Any?
override init(style: UITableViewCellStyle, reuseIdentifier: String!)
{
super.init(style: style, reuseIdentifier: reuseIdentifier);
let height = 140;
atest = UILabel(frame: CGRect(x: 20 + (activityWidth / 2), y: 72, width: (activityWidth / 2) - 10, height: 30));
btest = UILabel(frame: CGRect(x: 20 + (activityWidth / 2), y: 102, width: (activityWidth / 2) - 10, height: 30));
self.contentView.addSubview(atest);
self.contentView.addSubview(btest);
}
Then in my table view controller I have:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier") as! CustomTableCell;
cell.atest.text = "text from an array at indexpath.row";
cell.btest.text = "another different text from an array";
return cell;
}
The deletion in the table view controller happens here:
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]?
{
let delete = SwipeAction(style: .destructive, title: "Delete")
{
action, indexPath in
print("delete button tapped");
// self.table.deleteRows(at: [indexPath], with: .none);
// database is a string index array
self.database.remove(at: indexPath.row);
if (self.database.count == 0)
{
self.noText.isHidden = false;
self.footer.isHidden = false;
self.table.tableFooterView = self.footer;
}
// self.table.setNeedsDisplay();
}
delete.backgroundColor = UIColor.red;
return [delete];
I do a delete by swiping left and it deletes everything correctly. The issue I have is that the deleting makes all the table view cells under the fold the same as the last visible cell.
How do I fix this? I am using a custom cell view too.
An example is that I have 6 rows and the top 4 are visible. Deleting the first row makes the 4th and 5th rows the same. As in the last visible row also becomes the first none visible row. The prepareForReuse is probably not working right.
The delete works and goes from 6 rows to 5 but an example is below.
UITableView
First row label A
Second row label B
Third row label C
Fourth row label D (last visible row)
Fifth row label E (first non visible row)
Sixth row label F
Deleting the first row by swiping creates this new UITableView:
UITableView
Second row label B
Third row label C
Fourth row label D
Fourth row label D (last visible row)
Fifth row label E (first non visible row)
The reusable cells are not working correctly.
I do not use awakeFromNib and just upgraded to swift 4.1 as well.
In TableView custom cells if you are adding views from storyboard or XIB then it get removed on scrolling but if you are adding views programmatically then you have to remove the view from the tableViewCell:
Either using below code in cellForRow :
for label in cell.subviews {
if let mylabel = label as? UILabel {
mylabel.removeFromSuperview()
}
}
or you can use this code customCellClass in prepareForReuse method:
class myCustomCell: UITableViewCell {
override func prepareForReuse() {
super.prepareForReuse()
for view in self.subviews {
view.removeFromSuperview()
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
Above code will remove all subviews from the cell.
Check below links for more answers:
remove the subviews from the contentView of UITableViewCell (reset the UITableView)
EDIT
I tried below code with string Array and swipeDelete functionality in tableView:
class ViewController: UIViewController {
#IBOutlet weak var sampleTableView: UITableView!
var nameArray = ["Snoop", "Sarah", "Fido", "Mark", "Jill", "Parague", "London", "Barcelona", "Italy", "France", "Eiffiel", "Tower", "Paris", "Europe", "Amsterdam", "Zurich", "Germany", "Munich", "Milan", "Venice", "Switzerland", "Brussels"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return nameArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier", for: indexPath)
cell.textLabel?.text = nameArray[indexPath.row] + " - " + String(describing: indexPath.row)
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
// handle delete (by removing the data from your array and updating the tableview)
tableView.beginUpdates()
nameArray.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
tableView.endUpdates()
}
}
}
When I start swipe deleting the tableView cells the string data is correct but the indexPath value doesn't change. It changes when I scrolled the tableView. Which is correct logically because after deletion the string data indexes have changed and when cells are reused the new index will be visible.
EDIT Using SwipwCellKit:
Used the same code of yours with the SwipeCellKit:
My ViewController :
class ViewController: UIViewController {
#IBOutlet weak var sampleTableView: UITableView!
var nameArray = ["Snoop", "Sarah", "Fido", "Mark", "Jill", "Parague", "London", "Barcelona", "Italy", "France", "Eiffiel", "Tower", "Paris", "Europe", "Amsterdam", "Zurich", "Germany", "Munich", "Milan", "Venice", "Switzerland", "Brussels"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
}
extension ViewController : UITableViewDataSource, UITableViewDelegate, SwipeTableViewCellDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 72
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return nameArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier") as! CustomTableCell
cell.delegate = self
cell.atest.text = nameArray[indexPath.row] + " - " + String(describing: indexPath.row)
cell.btest.text = "another different text from an array";
return cell;
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]?
{
let delete = SwipeAction(style: .destructive, title: "Delete")
{
action, indexPath in
print("delete button tapped")
self.nameArray.remove(at: indexPath.row)
self.sampleTableView.deleteRows(at: [indexPath], with: .none)
}
delete.backgroundColor = UIColor.red;
return [delete];
}
}
My CustomTableViewCell:
class CustomTableCell: SwipeTableViewCell {
public var atest = UILabel()
public var btest = UILabel()
var animator: Any?
override func awakeFromNib() {
atest = UILabel(frame: CGRect(x: 20 , y: 2, width: 200, height: 30));
btest = UILabel(frame: CGRect(x: 20 , y: 32, width: 200, height: 30));
self.contentView.addSubview(atest);
self.contentView.addSubview(btest);
}
}
It is working same as the first editing.
EDIT
CustomTableViewCell using init :
class CustomTableViewCell: SwipeTableViewCell {
public var atest = UILabel();
public var btest = UILabel();
var animator: Any?
override init(style: UITableViewCellStyle, reuseIdentifier: String!)
{
super.init(style: style, reuseIdentifier: reuseIdentifier)
atest = UILabel(frame: CGRect(x: 20 , y: 2, width: 100, height: 30))
btest = UILabel(frame: CGRect(x: 20 , y: 32, width: 100, height: 30))
self.contentView.addSubview(atest)
self.contentView.addSubview(btest)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("init(coder:) has not been implemented")
}
}
change in cellForRow:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = CustomTableViewCell.init(style: .default, reuseIdentifier: "reuseIdentifier")
cell.delegate = self
cell.atest.text = nameArray[indexPath.row] + " - " + String(describing: indexPath.row)
cell.btest.text = "another different text from an array";
return cell;
}
still working same as above.
The problem is in how your cells are being reused. Add this line of code to your table cell class:
override func prepareForReuse() {
super.prepareForReuse()
atest.text = nil
btest.text = nil
}

Collapse other UITableViewCell when current cell is expanded

I am trying to expand my UITableViewCell and I can expand cells. But I want to collapse the UITableViewCell which are not selected.
What I am trying in code:
var expandedCells = [Int]()
#IBOutlet weak var tableVieww:UITableView!
#IBAction func buttonPressed(_ sender: AnyObject) {
// If the array contains the button that was pressed, then remove that button from the array
if expandedCells.contains(sender.tag) {
expandedCells = expandedCells.filter({ $0 != sender.tag})
}
// Otherwise, add the button to the array
else {
expandedCells.append(sender.tag)
}
// Reload the tableView data anytime a button is pressed
tableVieww.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! exampleCell
cell.myButton.tag = indexPath.row
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// Set the row height based on whether or not the Int associated with that row is contained in the expandedCells array
if expandedCells.contains(indexPath.row) {
return 212
} else {
return 57
}
}
You can maintain a variable for maintaining the selected index as below,
var expandedIndexPath: IndexPath?
Then update your tableView delegate as follows,
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
expandedIndexPath = indexPath // Update the expandedIndexPath with the selected index
tableView.reloadData() // Reload tableview to reflect the change
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// Check wether expanded indexpath is the current index path and updated return the respective height
if expandedIndexPath == indexPath {
return 212
} else {
return 57
}
}
This should work fine.
1) First Create Bool Array variable
var isExpandArr = Array<Bool>()
2) Insert true at 0 index in ViewLoad()
isExpandArr.insert(true, at: 0)
3) Put Into cellForRowAt
cell.ToggleBT.addTarget(self, action: #selector(handleToggleBtn), for:.touchUpInside)
cell.ToggleBT.tag = indexPath.row
if isExpandArr[indexPath.row] == true{
cell.bottomrView.isHidden = false
}else{
cell.bottomrView.isHidden = true
}
4) set heightForRowAt
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return isExpandArr[indexPath.row] ? 204 : 60
}
5) put function
func handleToggleBtn(sender: UIButton){
print(sender.tag)
if isExpandArr.contains(true){
print("yes")
if let unwrappedIndex = isExpandArr.index(of: true){
isExpandArr[unwrappedIndex] = false
isExpandArr[sender.tag] = true
}
}
table.reloadData()
}
in #swift 2.3
Hope this will also helpful
For collapse and expand the header
define the variable above the viewDidLoad or within the class block
var headerIcon: UIImageView = UIImageView()
var sectionTitleArray : NSMutableArray = NSMutableArray()
var sectionContentEventTitleDict : NSMutableDictionary = NSMutableDictionary()
var sectionEventImagesDict : NSMutableDictionary = NSMutableDictionary()
var arrayForBool : NSMutableArray = NSMutableArray()
var arrFirstSectionItemTitle = ["Section 1 item Name 1","Section 1 item Name 2","Section 1 item Name 3","Section 1 item Name 4","Section 1 item Name 5","Section 1 item Name 6","Section 1 item Name 7"]
var arrFirstSectionItemImages = ["Section_1_icon_1","Section_1_icon_2","Section_1_icon_3","Section_1_icon_4","Section_1_icon_5","Section_1_icon_6","Section_1_icon_7"]
var arrSecondSectionItemTitle = ["Section 2 item Name 1","Section 2 item Name 2","Section 2 item Name 3","Section 2 item Name 4"]
var arrSecondSectionItemImages = ["Section_1_icon_1","Section_2_item_icon_2","Section_2_item_icon_3","Section_1_item_icon_4","Section_1_item_icon_5","Section_1_item_icon_6","Section_1_item_icon_7"]
var arrData: Array<Dictionary<String,AnyObject>> = Array<Dictionary<String,AnyObject>>()
#IBOutlet weak var tableList: UITableView!
in viewDidLoad() method write these lines of code
tableList.delegate = self
tableList.dataSource = self
tableList.backgroundColor = UIColor.getRGBColor(240, g: 240, b: 240)
let customView = UIView(frame: CGRectMake(0, 0, 200, 80))
customView.backgroundColor = UIColor.getRGBColor(240, g: 240, b: 240)
let customLine = UIView(frame: CGRectMake(0, 0, tblAllQuestion.frame.size.width, 1))
customLine.backgroundColor = UIColor.getRGBColor(240, g: 240, b: 240)
customView.addSubview(customLine)
tableList.tableFooterView = customView
now write the delegate and datasource methods as following:
//MARK: UITableViewDelegate AND UITableViewDataSource METHOD WITH SECTION
for number of sections to be collapsed and expand
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
print_debug(sectionTitleArray.count)
return sectionTitleArray.count
}
cell method:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CELL_IDENTIFIER", forIndexPath: indexPath)as! CellAllQuestionFiqhVC
//let cellIdentifier = "CellAllQuestionFiqhVC"
// cell = self.tableList.dequeueReusableCellWithIdentifier(cellIdentifier)
let manyCells : Bool = arrayForBool .objectAtIndex(indexPath.section).boolValue
if (!manyCells) {
// cell.textLabel.text = #"click to enlarge";
}
else{
let group = self.arrData[indexPath.section]
let data: Array<Dictionary<String,AnyObject>> = group["data"]as! Array<Dictionary<String,AnyObject>>
cell.configCell(data[indexPath.row], instance: self)
}
return cell
}
number cell in a section:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(arrayForBool.objectAtIndex(section).boolValue == true){
let count1 = arrData[section]["data"]!
if count1.count != nil {
return count1.count
}
}
return 0;
}
the height of the section
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 40
}
The height of the cell:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if(arrayForBool.objectAtIndex(indexPath.section).boolValue == true){
return 100
}
return 2;
}
to set header tappable:
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 40))
headerView.backgroundColor = UIColor.whiteColor()//Constant.kAPP_COLOR
headerView.tag = section
let headerString = UILabel(frame: CGRectMake(40/*tableView.frame.size.width/2-30*/, 10, tableView.frame.size.width, 18))
let headerLine = UIView(frame: CGRectMake(0, 39, tableView.frame.size.width, 1))
//UILabel(frame: CGRect(x: 20, y: 10, width: tableView.frame.size.width-10, height: 30)) as UILabel
//headerLine.text = sectionTitleArray.objectAtIndex(section) as? String
headerLine.backgroundColor = UIColor.getRGBColor(240, g: 240, b: 240)
headerView .addSubview(headerLine)
headerIcon = UIImageView(frame: CGRect(x: tblAllQuestion.bounds.maxX-30, y: 10, width: 8, height: 12)) as UIImageView
headerIcon.image = UIImage(named: "right_arrow")
headerView.addSubview(headerIcon)
let headerTapped = UITapGestureRecognizer(target: self, action: #selector(sectionHeaderTapped))
headerView.addGestureRecognizer(headerTapped)
return headerView
}
for rotate section icon on tapped on:
func rotateSectionIconOnTapped(indexTapped: Int) {
isActiveMap = false
if arrayForBool.objectAtIndex(indexTapped) as! NSObject == true {
print("toggle the icon at degree 90")
headerIcon.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2))
}
else if arrayForBool.objectAtIndex(indexTapped) as! NSObject == false {
print("toggle the ico at degree 0")
headerIcon.transform = CGAffineTransformMakeRotation(0)
}
}
action for section header tapped:
func sectionHeaderTapped(recognizer: UITapGestureRecognizer) {
//filterByCategoryIsOn = false
print("Tapping working")
print(recognizer.view?.tag)
//if recognizer.view!.tag != 2 {
let indexPath : NSIndexPath = NSIndexPath(forRow: 0, inSection:(recognizer.view?.tag as Int!)!)
if (indexPath.row == 0) {
for (indexx, bl) in arrayForBool.enumerate() {
if recognizer.view!.tag != indexx {
let index : NSIndexPath = NSIndexPath(forRow: 0, inSection:(indexx))
arrayForBool.replaceObjectAtIndex(index.section, withObject: false)
let range = NSMakeRange(index.section, 1)
let sectionToReload = NSIndexSet(indexesInRange: range)
self.tableList.reloadSections(sectionToReload, withRowAnimation:UITableViewRowAnimation.Fade)
}
}
var collapsed = arrayForBool.objectAtIndex(indexPath.section).boolValue
print_debug(collapsed)
collapsed = !collapsed;
print_debug(collapsed)
arrayForBool.replaceObjectAtIndex(indexPath.section, withObject: collapsed)
print(arrayForBool)
//reload specific section animated
let range = NSMakeRange(indexPath.section, 1)
let sectionToReload = NSIndexSet(indexesInRange: range)
self.tableList.reloadSections(sectionToReload, withRowAnimation:UITableViewRowAnimation.Fade)
rotateSectionIconOnTapped(recognizer.view!.tag)
}
}
get action for didselect row at index path :
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print_debug(indexPath)
print_debug(indexPath.row)
print_debug(indexPath.section)
if indexPath.section == 0 {
}
else if indexPath.section == 1 {
}
}

When I swipe a UITableViewCell, the header view moves as well

So, I have a few Swipe actions like delete, block, etc in my UITableView. I wanted to add headers to separate my two sections. So, I added a prototype cell, named it HeaderCell and then went to the view. I added one label, named headerLabe. My problem is that when I swipe for the actions, the header cells were moving as well, which looked bad. I researched, and found a solution to just return the contentView of the cell. However, when I do this, the label has not shown up. I have tried a dozen different solutions, and nothing has worked, so I have turned to SO. Can anyone help me?
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell : CustomHeaderTableViewCell = tableView.dequeueReusableCellWithIdentifier("HeaderCell") as! CustomHeaderTableViewCell
if section == 0 {
headerCell.headerLabel.text = "Thank You's"
} else if section == 1 {
headerCell.headerLabel.text = "Conversations"
}
return headerCell.contentView
}
Thanks so much.
You can use a section Header as #ozgur suggest.If you still want to use a cell.
Refer to this datasource method
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
if indexPath = YourHeaderCellIndexPath{
return false
}
return true
}
check the following methods
In your UIViewController use the following
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 60
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableCellWithIdentifier("HeaderCell") as! WishListHeaderCell
headerCell.lblTitle.text = cartsData.stores_Brand_Name
let imgVw = UIImageView()
imgVw.frame = CGRectMake(8, 18, 25, 25)
imgVw.image = UIImage(named: "location.png")
let title = UILabel()
title.frame = CGRectMake(41, 10, headerCell.viwContent.frame.width - 49, 41)
title.text = cartsData.stores_Brand_Name
title.textColor = UIColor.whiteColor()
headerCell.viwContent.addSubview(imgVw)
headerCell.viwContent.addSubview(title)
return headerCell.viwContent
}
In your UITableViewCell use the following
import UIKit
class HeaderCell: UITableViewCell {
#IBOutlet weak var viwContent: UIView!
#IBOutlet weak var imgIcn: UIImageView!
#IBOutlet weak var lblTitle: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
self.viwContent.backgroundColor = UIColor.grayColor()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
//UITableViewCell
let headerCell = tableView.dequeueReusableCellWithIdentifier("headerCell") as! SecJobCCHeaderTableViewCell
// Cell Rect
var cellRect : CGRect = headerCell.frame
cellRect.size.width = screenBounds.width
// Header Footer View
let headerFooterView = UITableViewHeaderFooterView(frame : cellRect)
//Adding Gesture
let swipeGestRight = UISwipeGestureRecognizer(target: self, action:#selector(AddSecJobCostCentreViewController.draggedViewRight(_:)))
swipeGestRight.enabled = true
swipeGestRight.direction = UISwipeGestureRecognizerDirection.Right
headerFooterView.addGestureRecognizer(swipeGestRight)
// Update Cell Rect
headerCell.frame = cellRect
// Add Cell As Subview
headerCell.tag = 1000
headerFooterView.addSubview(headerCell)
// Return Header Footer View
return headerFooterView
}
func draggedViewRight(sender:UISwipeGestureRecognizer) {
// Swipe Gesture Action
let currentHeaderView = sender.view?.viewWithTag(1000) as! SecJobCCHeaderTableViewCell
}

UITableView only updating on scroll up, not down

I have a UITableView that updates when I scroll up, but it does not update when I scroll down. Furthermore, when it does update it occasionally seems to "skip" a cell and update the next one.
There are 6 total cells that should populate
I've created the UITableView in the storyboard, set my constraints for both the hashLabel and the creditLabel in storyboard
Here is the image of the initial TableView:
And upon scrolling up, when updated properly:
...and when scrolling up "misses" a cell:
and of course, the class:
class HashtagController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var model:ModelData!
var currentCell: UITableViewCell!
#IBOutlet var hashtagTableView: UITableView!
let basicCellIdentifier = "CustomCells"
override func viewDidLoad() {
super.viewDidLoad()
model = (self.tabBarController as CaptionTabBarController).model
hashtagTableView.delegate = self
hashtagTableView.dataSource = self
self.navigationController?.navigationBar.titleTextAttributes = [ NSFontAttributeName: UIFont(name: "CherrySwash-Regular", size: 25)!, NSForegroundColorAttributeName: UIColor(red:27.0/255, green: 145.0/255, blue: 114.0/255, alpha: 1.0)]
configureTableView()
hashtagTableView.reloadData()
}
func configureTableView() {
hashtagTableView.rowHeight = UITableViewAutomaticDimension
hashtagTableView.estimatedRowHeight = 160.0
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//deselectAllRows()
hashtagTableView.reloadData()
}
override func viewDidAppear(animated: Bool) {
hashtagTableView.reloadData()
}
func deselectAllRows() {
if let selectedRows = hashtagTableView.indexPathsForSelectedRows() as? [NSIndexPath] {
for indexPath in selectedRows {
hashtagTableView.deselectRowAtIndexPath(indexPath, animated: false)
}
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return model.quoteItems.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return customCellAtIndexPath(indexPath)
}
func customCellAtIndexPath(indexPath:NSIndexPath) -> CustomCells {
var cell = hashtagTableView.dequeueReusableCellWithIdentifier(basicCellIdentifier) as CustomCells
setTitleForCell(cell, indexPath: indexPath)
setSubtitleForCell(cell, indexPath: indexPath)
return cell
}
func setTitleForCell(cell:CustomCells, indexPath:NSIndexPath) {
let item = Array(Array(model.quoteItems.values)[indexPath.row])[0] as? String
cell.hashLabel.text = item
}
func setSubtitleForCell(cell:CustomCells, indexPath:NSIndexPath) {
let item = Array(model.quoteItems.keys)[indexPath.row]
cell.creditLabel.text = item
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
/*currentCell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell!
var currentLabel = currentCell.textLabel?.text
var currentAuthor = currentCell.detailTextLabel?.text
model.quote = currentLabel!
model.author = currentAuthor!*/
}
}
class CustomCells: UITableViewCell {
#IBOutlet var hashLabel: UILabel!
#IBOutlet var creditLabel: UILabel!
}
As it turns out, the issue had to do with my estimatedRowHeight. In this case the row height was too large and it was effecting the way the table cells were being constructed.
So in the end I changed hashtagTableView.estimatedRowHeight = 160.0 to hashtagTableView.estimatedRowHeight = 80.0 and everything worked just fine.

Resources