How to reorder section in Tableview - ios

I want to reorder section and row. So by default, native section reordering is not possible. So I implemented the section as a row. And reorder section and row. But the problem is when I reorder the section then it takes a row. It will crash the application.
Can you please help me?
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). Table view: <UITableView: 0x7f874f026000; frame = (0 59; 393 759); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x6000023bde60>; backgroundColor = <UIDynamicSystemColor: 0x60000388b640; name = tableBackgroundColor>; layer = <CALayer: 0x600002df4c60>; contentOffset: {0, 0}; contentSize: {393, 206.66667175292969}; adjustedContentInset: {0, 0, 0, 0}; dataSource: <ReorderTableView.ViewController: 0x7f874c709840>>
class ViewController: UIViewController {
#IBOutlet weak var tblView: UITableView!
var arrSection = [Section]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tblView.isEditing = true
tblView.dataSource = self
tblView.delegate = self
// register tableviewcell
tblView.register(UINib(nibName:"SectionTableViewCell", bundle: nil), forCellReuseIdentifier: "SectionTableViewCell")
setupView()
}
func setupView(){
let obj = Group(groupName: "Group1")
let sec = Section(sectionName: "Section1", group: [obj])
let sec1 = Section(sectionName: "Section2", group: [obj])
arrSection.append(sec)
arrSection.append(sec1)
tblView.reloadData()
}
}
extension ViewController : UITableViewDelegate,UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return arrSection.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if arrSection[section].group?.count ?? 0 > 0 {
let temp = arrSection[section].group?.count ?? 0
return temp + 1
}
else{
return 1
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let obj = arrSection[indexPath.section]
let cell = tableView.dequeueReusableCell(withIdentifier: "SectionTableViewCell") as! SectionTableViewCell
cell.lblTitle.text = obj.sectionName
return cell
}
else{
let obj = arrSection[indexPath.section].group?[indexPath.row - 1]
let cell = tableView.dequeueReusableCell(withIdentifier: "SectionTableViewCell") as! SectionTableViewCell
cell.lblTitle.text = obj?.groupName ?? ""
return cell
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 0
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return nil
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0 {
return UITableView.automaticDimension
}else{
return UITableView.automaticDimension
}
}
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
tblView.beginUpdates()
let movingItem = self.arrSection[sourceIndexPath.section]
self.arrSection.remove(at: sourceIndexPath.section)
self.arrSection.insert(movingItem, at: destinationIndexPath.section)
tblView.moveSection(sourceIndexPath.section, toSection: destinationIndexPath.section)
tblView.endUpdates()
}
}

Related

How to add header to each row in a table view

I'm building an iOS app with a collection view inside the table view. I'm having three rows each row having a collection view inside it. I am planning to have three sections each section for each row. For example row, one should be in a separate section with a header and similarly for rows 2 and 3.
Whenever I create three sections I'm getting all the three rows in all three sections. I want to have a separate section with a header for each row.
import UIKit
class StoreVC: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var CourseTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
CourseTableView.tableFooterView = UIView()
CourseTableView.delegate = self
CourseTableView.dataSource = self
}
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0
{
return "Courses"
}
else if section == 1
{
return "Tests"
}
return "Bundles"
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CourseRow
return cell
}
else if indexPath.row == 1
{
let cell = tableView.dequeueReusableCell(withIdentifier: "testcell", for: indexPath) as! TestRow
return cell
}
else if indexPath.row == 2
{
let cell = tableView.dequeueReusableCell(withIdentifier: "bundlecell", for: indexPath) as! BundleRow
return cell
}
return UITableViewCell()
}
}
Try this code on your Xcode-playground and customize as you need.
import UIKit
import PlaygroundSupport
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in tableView: UITableView) -> Int {
3
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
1
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UILabel()
headerView.text = "Header: \(section)"
return headerView
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = "Cell: \(indexPath)"
return cell
}
}
PlaygroundPage.current.liveView = ViewController()

UIScrollView: infinite scroll to display fibonacci numbers

I am trying to let the user infinitely scroll the screen for more fibonacci numbers. I can only display up to the number of fib numbers that is the length of my posts array. Here is what it looks like now. I am having trouble implementing the scrollViewDidScroll function to achieve the infinite scroll. I found some code on stackoverflow that makes sense to me, but I don't know what to do to connect it to the tableview (the part where you call for more data). Any input is appreciated!
import UIKit
class FeedTableViewController: UITableViewController {
let posts : [String] = ["","","","","","","",""]
var fibArray: [Int] = [0,1]
let cellIdentifier = "userPostFeedCell"
var indexOfPageToRequest = 1
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offsetY = scrollView.contentOffset.y
let contentHeight = scrollView.contentSize.height
if offsetY > contentHeight - scrollView.frame.size.height {
// increments the number of the page to request
indexOfPageToRequest += 1
// call your API for more data
// tell the table view to reload with the new data
self.tableView.reloadData()
}
}
func fibonacci(n: Int) -> Int {
if (fibArray.count > n) {
return fibArray[n];
}
let fibonacciNumber = fibonacci(n: n - 1) + fibonacci(n: n - 2)
fibArray.append(fibonacciNumber)
return fibonacciNumber
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(
UINib(nibName: "FeedTableViewCell", bundle: nil), forCellReuseIdentifier: cellIdentifier)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> FeedTableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
cell.textLabel?.text = "\(fibonacci(n: indexPath.row+1))"
print("cellForRowAt \(indexPath.row)")
return cell as! FeedTableViewCell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
print("heightForRowAt \(indexPath.row)")
return 40
}
}
You'll overflow an Int pretty quick in a fibonacci series so you don't actually need infinite scroll. You just set the number of rows in your section to be high. The application only ever creates the cells that need to be displayed so you won't be using large amount of memory. Here is my code
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20000
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
cell.textLabel?.text = "\(fibonacci(n: indexPath.row+1))"
return cell
}

ios 11 UITableViewHeaderFooterView not properly scroll in animation

I have collapse and expand animation in UITableView. Tableview has two section in which first section data is collapse and expand. This thing perfectly working with ios 10 but in ios 11 Section view repeated or overlapped with cell data which is expanded.
Below is my code
//MARK: -Table View delegate Method
func numberOfSections(in tableView: UITableView) -> Int {
return read_Localizable("titleHelpSection").components(separatedBy: ",").count
}
//MARK: -Table View Datasource Method
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat{
return 44.0
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
var headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "headerView")
let arrSection = read_Localizable("titleHelpSection").components(separatedBy: ",")
if headerView == nil
{
headerView = UITableViewHeaderFooterView(reuseIdentifier: "headerView")
headerView?.contentView.backgroundColor = UIColor.white
let lblResult = UILabel()
lblResult.tag = 123456
lblResult.font = AppCommonSNMediumFont()
lblResult.textColor = UIColor.black
lblResult.translatesAutoresizingMaskIntoConstraints = false
headerView?.contentView.addSubview(lblResult)
let seperator = UIView()
seperator.translatesAutoresizingMaskIntoConstraints = false
seperator.backgroundColor = UIColor.black
headerView?.contentView.addSubview(seperator)
headerView?.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[seperator]|", options: [], metrics: nil, views: ["seperator":seperator]))
headerView?.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[lable]-(>=8)-|", options: [], metrics: nil, views: ["lable":lblResult]))
headerView?.contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[lable]-[seperator(1)]|", options: [], metrics: nil, views: ["lable":lblResult,"seperator":seperator]))
}
if let lblResult = headerView?.contentView.viewWithTag(123456) as? UILabel
{
lblResult.text = arrSection[section]
}
return headerView
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 20.0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0
{
return (arrHelpData.count)
}
else
{
return 1
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0
{
var cell = tableView.dequeueReusableCell(withIdentifier: "HelpCell") as? CellHelp;
if cell == nil {
cell = CellHelp(style: .default, reuseIdentifier: "HelpCell")
cell?.selectionStyle = .none
cell?.txtContain.delegate = self
}
if let objModel = arrHelpData.object(at: indexPath.row) as? HelpModel
{
cell?.lblTitle.text = objModel.helpTitle
if objModel.isExpanded == true
{
cell?.txtContain.text = objModel.helpDesc
}
else
{
cell?.txtContain.text = ""
}
cell?.imgArrow.isHighlighted = !objModel.isExpanded
}
return cell!
}
else
{
var cell = tableView.dequeueReusableCell(withIdentifier: "DefultCell")
if cell == nil
{
cell = UITableViewCell(style: .default, reuseIdentifier: "DefultCell")
cell?.textLabel?.textColor = color1F87A3()
cell?.textLabel?.font = AppCommonSNRegularFont()
cell?.selectionStyle = .none
cell?.textLabel?.numberOfLines = 0
}
cell?.textLabel?.text = read_Localizable("titleSettings")
return cell!
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 && indexPath.row < (arrHelpData.count)
{
if let objModel = arrHelpData.object(at: indexPath.row) as? HelpModel
{
if objModel.isExpanded == true
{
objModel.isExpanded = false
}
else
{
objModel.isExpanded = true
}
tableView.reloadData()
}
}
}
Actual view
Section overlapped on cell data
This is very frustrating iOS11 issue, something to do around estimatedHeight issue, If you really want to keep the self sized row and header then u need to go with the below approach.
Declare variable which holds the height of the cell/header and store height into that and used it as below:
var cellHeightDictionary: NSMutableDictionary // To overcome the issue of iOS11.2
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 125
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cellHeightDictionary.setObject(cell.frame.size.height, forKey: indexPath as NSCopying)
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if cellHeightDictionary.object(forKey: indexPath) != nil {
let height = cellHeightDictionary.object(forKey: indexPath) as! CGFloat
return height
}
return UITableViewAutomaticDimension
}
This is the only solution which worked for me for iOS11 issues with auto sizing cells. Otherwise people suggest to keep estimatedHeight 0 to get rid off such issues.
In your case first try doing this for cell and that doesn't solve the issue completely then do same for header height also. Hope this helps!
Don't forget to test in both iOS11.1 and iOS11.2.

all of the cells in section view are not being returned

I have my tableview hooked up property to my viewcontroller class however I am unable to get all of my cells to return within the sections. I want 10pix margins between each cell and was able to do this successfully in another VC, however now the method that I am using is only returning one cell (there are only 2 cells total) so I would like help in figuring out a way to display all cells within the section, code is included below:
//UITableViewDataSource
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.layer.cornerRadius = 8.0
headerView.layer.masksToBounds = true
headerView.backgroundColor = UIColor.colorWithHex("A171FF")
let headerLabel = UILabel(frame: CGRect(x: 30, y: 0, width:
tableView.bounds.size.width, height: tableView.bounds.size.height))
headerLabel.font = UIFont(name: "Gill Sans", size: 15)
headerLabel.textColor = UIColor.white
headerLabel.text = self.tableView(self.myWldTbl, titleForHeaderInSection: section)
headerLabel.sizeToFit()
headerView.addSubview(headerLabel)
return headerView
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch (section) {
case 0 :
return userMoves.count
case 1:
return rsvps.count
default :
print("unable to set up sections")
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row % 2 != 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: MyWorldTableViewCell.self)) as! MyWorldTableViewCell
//cell appearance
cell.layer.cornerRadius = 8.0
cell.clipsToBounds = true
//cell data
let evt = userMoves[indexPath.row]
cell.rsvpCount.text = "\(rsvps.count)"
//evt img
if let evtImg = evt.event_photo_url {
cell.img.kf.setImage(with: URL(string: Constants.Server.PHOTO_URL + evtImg))
} else {
cell.img.image = UIImage(named: "user_icon")
}
cell.ttl.text = evt.event_name!
return cell } else {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: InvisibleCell.self)) as! InvisibleCell
cell.backgroundColor = UIColor.clear
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row % 2 == 0 {
return 10.0
}
return 102.0
}
//UITableViewDelegate
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 30
}
EDITED:
The first thing that is confusing about your code is that you appear to have 2 sections showing the contents of 2 different arrays (per the logic in numberOfRowsInSection), but you don't check the section number in cellForRowAt.
Before anything else, you should be checking indexPath.section in cellForRowAt to make sure you are using the correct array. As written, your code is using the userMoves array to populate both sections.
The cause of your missing cells is that you must account for the invisible separator cells in your numberOfRowsInSection method. You had the right idea to multiply by 2:
switch (section) {
case 0 :
return userMoves.count * 2
//etc
}
When accessing the array, in cellForRowAt, you need to divide by 2 to avoid the index out of bounds exception:
if indexPath.row % 2 != 0 {
//dequeue, etc.
let evt = userMoves[indexPath.row / 2]
//etc.
}

Custom HeaderView only displayed in last Section

I created a custom header view for a UITableView. The type of the header view is UITableViewCell. I implemented it in the storyboard and added it to the view hierarchy like its shown in the screenshot below
Here is my Code for UITableViewController
import UIKit
class SecondCustomTableViewController: UITableViewController {
#IBOutlet weak var customHeaderView: UITableViewCell!
let array = ["Row 1", "Row 2", "Row 3"]
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(SecondCustomTableViewController.self, forHeaderFooterViewReuseIdentifier: "Header")
self.tableView.dequeueReusableHeaderFooterView(withIdentifier: "Header")
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "\(array[indexPath.row])"
return cell
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
print("\(customHeaderView) -> Section: \(section)")
return customHeaderView
}
}
When i run it in the simulator it looks like this
My question is: Why is my custom header view shown for the last section only?
The print statement in the viewForHeaderInSection method looks like this:
Optional(<UITableViewCell: 0x7ff287802200; frame = (0 0; 375 44); clipsToBounds = YES; layer = <CALayer: 0x600000038d00>>) -> Section: 0
Optional(<UITableViewCell: 0x7ff287802200; frame = (0 0; 375 44); clipsToBounds = YES; autoresize = W; layer = <CALayer: 0x600000038d00>>) -> Section: 1
Optional(<UITableViewCell: 0x7ff287802200; frame = (0 176; 375 44); clipsToBounds = YES; autoresize = W; layer = <CALayer: 0x600000038d00>>) -> Section: 2
I also unwrapped the optional to make it a non optional but its the same behavior. As the print statement shows the viewForHeaderInSectionmethod is called three times, so why the header shows up for the last section only?
I hope someone can help me with this.
Every hint is highly appreciated.
I think its because ui elements cannot be copied, what you are doing is make an instance of UITableViewCell as IBOutlet. Better you can create your view when it needs.
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return tableView.dequeueReusableCell(withIdentifier: "Header") as! UITableViewHeaderFooterView
}
Hope it helps. Sorry for my bad english.

Resources