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

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
}

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.

UITableView Table Header pulled down when collapsing collapsible section headers

I have a collapsible header for uitableview sections based on another stack overflow post (no idea where now, as that was months ago). As it happens, the testers found a weird bug where collapsing all of the sections pulls the table header view down.
*edit the table view header is just a UI view I dropped into the storyboard, inside the tableview, above the prototype cell. No significant constraints. Just height for the cells and the header. The tableview is pinned to the safe area.
Everything looks fine until you expand one of the sections off screen, then scroll it up so the rows start to slide under the floating section header at the top. Then you tap to collapse it. It collapses, but the header view is pulled down. It looks like it happens when the sections fit on one screen, and the rows were scrolled slightly before the collapse.
Any help would be appreciated.
In my demo project (happy to share), when the four sections are collapsed, it looks like this:
When the user expands some of the sections, scrolls so a section header is sticky at the top and the contents are scrolled under it, then collapses the sticky section header, it can look like this:
I have a protocol for the delegate:
protocol CollapsibleHeaderViewDelegate: class {
func toggleSection(header: CollapsibleSectionHeader, section: Int)
}
protocol SectionHeaderCollapsible {
var isCollapsed: Bool { get }
var rowCount: Int { get }
}
And the subclass of UITableVieHeaderFooterView:
class CollapsibleHeader: UITableViewHeaderFooterView {
#IBOutlet var sectionHeaderLabel: UILabel!
var collapsed = false
weak var delegate: CollapsibleHeaderViewDelegate?
var sectionItem: SectionHeaderCollapsible?
static let reuseIdentifer = "CollapsibleHeader"
func configure(headerText: String) {
textLabel?.text = headerText
}
override func awakeFromNib() {
super.awakeFromNib()
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapHeader)))
}
#objc private func didTapHeader(gestureRecognizer: UITapGestureRecognizer) {
guard let header = gestureRecognizer.view as? CollapsibleHeader else { return }
delegate?.toggleSection(header: self, section: header.tag)
}
}
Then the delegate does something like. this:
struct CollapsibleSection: SectionHeaderCollapsible {
var isCollapsed: Bool = false
var rowCount: Int {
get {
return isCollapsed ? 0 : dataContents.count
}
}
var dataContents: [String]
}
class ViewController: UIViewController {
#IBOutlet var tableView: UITableView!
#IBOutlet var headerView: UITableView!
var sections = [CollapsibleSection(isCollapsed: false, dataContents: ["first", "second"]),
CollapsibleSection(isCollapsed: false, dataContents: ["red", "blue"]),
CollapsibleSection(isCollapsed: false, dataContents: ["seven", "five"]),
CollapsibleSection(isCollapsed: false, dataContents: ["Josephine", "Edward"])]
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
let nib = UINib(nibName: "CollapsibleHeader", bundle: nil)
tableView.register(nib, forHeaderFooterViewReuseIdentifier: "CollapsibleHeader")
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].rowCount
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") else { fatalError() }
cell.textLabel?.text = sections[indexPath.section].dataContents[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard let header = self.tableView.dequeueReusableHeaderFooterView(withIdentifier: "CollapsibleHeader") as? CollapsibleHeader else { fatalError() }
header.sectionHeaderLabel.text = "Section \(section + 1)"
header.delegate = self
header.tag = section
return header
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 100
}
}
extension ViewController: CollapsibleHeaderViewDelegate {
func toggleSection(header: CollapsibleHeader, section: Int) {
sections[section].isCollapsed = !sections[section].isCollapsed
tableView.reloadSections([section], with: .fade)
}
}
EDIT:
Looks like my coworkers created a work around based on (or at least similar to) your answer:
if tableView.contentOffset.y < 0 {
var offset = tableView.contentOffset
offset.y = tableView.contentSize.height - tableView.bounds.height
tableView.setContentOffset(offset, animated: true)
} else {
tableView.setContentOffset(tableView.contentOffset, animated: true)
}
Faced same problem, apparently right after "reloadSections", tableView's contentOffset.y has some strange value (you can see it when print "tableView.contentOffset.y" before and after "reloadSections"). So I just set contentOffset after it uncollapse to 0 offset value:
let offset = tableView.contentOffset.y
// Reload section
tableView.reloadSections(IndexSet(integer: section), with: .automatic)
if !sections[section].isCollapsed {
tableView.contentOffset.y = offset - offset
}

UiView fixed on top of UiTableViewController

I need to put an UIView fixed on top of UITableViewController (like a header). I've tried this:
override func scrollViewDidScroll (scrollView: UIScrollView) {
var fixedFrame: CGRect = self.uiTopView.frame;
fixedFrame.origin.y = scrollView.contentOffset.y;
self.uiTopView.frame = fixedFrame;
}
But it does not work and I don't know why. Someone have any idea?
This can not be done, one way to accomplish this is to add the UITableViewController insideUIContainerView
So the structure will be as follows:
ViewController1 contains aUIContainerView this container view has embedded segue
to your tableViewController.
Then you can add the view to the ViewController1.
Why do you actually use UITableViewController instead of UIViewController with a tableView inside?
Maybe you should add your header view first then add you tableview depending on the header's frame.
for example: `import UIKit
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
var fixedLabel : UILabel!
var tableView : UITableView!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.tableView.frame = CGRectMake(0, self.fixedLabel.frame.maxY, self.view.frame.width, self.view.frame.height-70)
self.fixedLabel.frame = CGRectMake(0,0,self.view.bounds.width,70)
}
override func viewDidLoad() {
super.viewDidLoad()
self.fixedLabel = UILabel()
self.fixedLabel.backgroundColor = UIColor.blueColor()
self.fixedLabel.text = "This is a fixedLabel"
self.fixedLabel.textAlignment = .Center
self.tableView = UITableView()
self.tableView.delegate = self
self.tableView.dataSource = self
self.view.addSubview(fixedLabel)
self.view.addSubview(tableView)
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell : UITableViewCell? = tableView.dequeueReusableCellWithIdentifier("cell")
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
}
cell?.textLabel?.text = "Your text"
return cell!
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
}
`

Programmatically added UITableView won't populate data

I wish to create UITableView and a custom cell, and add them to my UIViewController.
If I layout UITableView from the storyboard onto my UIViewController, everything works fine with custom cell.
But I realized that animation regarding its height is not very smooth - sometimes the table view disappears
So I decided to create/destroy a UITableView everytime I wish to do some animation, and add it as a subview to my UIViewController.
But the programmatically-added UITableView won't populate data.
What am I doing wrong here?
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var dataArr = [String]() // Holds data for UITableView
var tv: UITableView! // Notice it's not #Outlet
override func viewDidLoad() {
super.viewDidLoad()
dataArr = ["one", "two", "three"]
// Create and add UITableView as drop down
tv = UITableView()
tv.delegate = self
tv.dataSource = self
createTableView()
}
func createTableView() {
let w = UIScreen.mainScreen().bounds.size.width
tv.frame = CGRectMake(0, 50, w, 0)
tv.rowHeight = 25.0
// Register custom cell
var nib = UINib(nibName: "searchCell", bundle: nil)
tv.registerNib(nib, forCellReuseIdentifier: "searchCell")
self.view.addSubview(tv)
UIView.animateWithDuration(0.2, delay: 0.0, options: UIViewAnimationOptions.BeginFromCurrentState, animations: {
self.tv.frame.size.height = 100
}, completion: { (didFinish) in
self.tv.reloadData()
})
}
func destroyTableView() {
UIView.animateWithDuration(0.2, animations:
{
// Hide
self.tv.frame.size.height = 0
}, completion: { (didFinish) in
self.tv.removeFromSuperview()
})
}
// MARK: - UITableView
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArr.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: SearchCell = self.tv.dequeueReusableCellWithIdentifier("searchCell") as! SearchCell
cell.cellLabel.text = dataArr[indexPath.row]
return cell;
}
}
I think the problem is with this line.
self.tv.frame.size.height = 100
Try setting the whole frame instead of just the height. Pretty sure a UIView's Frame's properties are read only.
See here
If that doesn't work. Maybe try implementing
func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat

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