Is there a way to stop scrolling certain cells in tableview? I want the top 2 cells in UITableView to be static and have scroll enabled on other cells. Scroll enabled property is on tableview, so it scrolls all the cells.
The top 2 cells are of height 44.0 each. I tried the below code, but it still scrolls the top 2 cells.
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
isScrolling = true
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let startScrollingOffset = 88.0
if (scrollView.contentOffset.y < CGFloat(startScrollingOffset)) {
// moved to top
isScrollDown = false
} else if (scrollView.contentOffset.y > CGFloat(startScrollingOffset)) {
// moved to bottom
isScrollDown = true
} else {
// didn't move
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if isScrollDown == false {
return
}
}
If you're trying to achieve an effect like static cells that remain on top when the list scrolls, use header view property of UITableView, here is an example with minimum code required to make this work. Replace the headerView with whatever cells you're trying to not scroll.
class VC: UITableViewController {
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = .purple
return headerView
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = String(describing: indexPath)
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
}
The solution could be take out first two row data from your data source( may be an array) and and create a custom view with that two row data. then set the custom view in the tableviewHeader using viewForHeaderInSection
As you have already removed first two row your table will show the data from third entry and the first two will be in top of them as header.
Related
I am using corner radius to set rounded border of UITableView, Following is my code
self.tableView.layer.cornerRadius = 8.0
self.tableView.layer.masksToBounds = true
But its showing like this without rounded corner in bottom :(
If your table height is proper then, your second cell inner view's height is greater that its height.
Try removing your inner view from cell or if there is no inner view then check your tableview height.
Thanks.
I set up a quick demo project with a sample viewcontroller containing the following code:
#IBOutlet var tableView: UITableView! {
didSet {
tableView.rowHeight = 44
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 2 }
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return tableView.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath)
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.layer.cornerRadius = 10
}
In storyboard the viewcontroller looks like this:
As you can see the tableview has a fixed height of 128pt. Although the tableview contains only two rows with a height of 44pt each (88pt in total) the tableview's corners look fine. So there has to be some other issue in your code.
I have a UITableviewController which I created programmatically in Swift4.
The marked rectangle in the picture is my footerview with a UITableView inside
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let tableFooterView = UITableView()
tableFooterView.rowHeight = 100
return tableFooterView
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 365
}
I want to load custom cells in the tableview cells in footerview. I tried using this following code
tableFooterView.dequeueReusableCell(withIdentifier: emptyCellIdentifier) as! EmptyTableViewCells
But I get error -
fatal error: unexpectedly found nil while unwrapping an Optional value
Please help
You have to register the cell for the footer tableView when you are creating in viewForFooterInSection method and you should distinguish both the tables via tag or something if you are making the delegate and datasource as self for the tableFooterView UITableView
tableFooterView.register(EmptyTableViewCells.self, forCellReuseIdentifier: emptyCellIdentifier)
So your method will be :
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
if tableView.tag == 1 {
tableFooterView = UITableView()
tableFooterView.register(EmptyTableViewCells.self, forCellReuseIdentifier: emptyCellIdentifier)
tableFooterView.rowHeight = 100
tableFooterView.tag = 2
tableFooterView.dataSource = self
tableFooterView.delegate = self
return tableFooterView
} else {
//footer view is nil for the table view which is in the main table footer
return nil
}
}
This will stop the crash on the tableFooterView.dequeueReusableCell line but you need to add the other views like buttons and labels on the tableFooter Empty Cell by code and not IBOutlet. It will again crash if you access any IBOutlet connected component.
I am new to iOS Development and I just implemented a simple expandable sections UITableView. I am not able to understand why some rows disappear and sometimes change position when the row heights are recalculated on tapping the section header. I went through all the already answered questions on this topic and have not been able to find the right solution.
Following is a scenario:
Launch the app:
Tap on the section header:
Section expands
All other headers disappear
Tap again
Section collapses
The headers continue to be blank
Scrolled to the bottom and back to the top
The positions of headers changed
Scrolled to the bottom and back to the top again
The positions of headers changed again with some cells still blank
Things I have already tried:
Wrapping reloadRowsAtIndexPaths in updates block (beginUpdates() and endUpdates())
Using reloadRowsAtIndexPaths with animation set to .none
Removing reloadRowsAtIndexPaths at all while keeping the updates block
Using reloadData() instead which actually works but I lose animation
Code:
Here is the link to the project repository.
You're using cells for the header. You shouldn't do that, you need a regular UIView there, or at least a cell that's not being dequeued like that. There's a few warnings when you run it that give that away. Usually just make a standalone xib with the view and then have a static method like this in your header class. Make sure you tie your outlets to the view itself, and NOT the owner:
static func view() -> HeaderView {
return Bundle.main.loadNibNamed("HeaderView", owner: nil, options: nil)![0] as! HeaderView
}
You're reloading the cells in the section that grows, but when you change the section that's grown you'd need to at least reload the former section for it to take the changes to it's cell's height. You can reload the section by index instead of individual rows in both cases
Ok as you ask, I am changing my answer according to you.
import UIKit
class MyTableViewController: UITableViewController {
let rows = 2
var categories = [Int](repeating: 0, count: 10)
struct Constants {
static let noSelectedSection = -1
}
var selectedSection: Int = Constants.noSelectedSection
func selectedChanged(to selected: Int?) {
let oldIndex = selectedSection;
if let s = selected {
if selectedSection != s {
selectedSection = s
} else {
selectedSection = Constants.noSelectedSection
}
tableView.beginUpdates()
if(oldIndex != -1){
tableView.reloadSections([oldIndex,s], with: .automatic)
}else{
tableView.reloadSections([s], with: .automatic)
}
tableView.endUpdates()
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return categories.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("reloading section \(section)")
return (selectedSection == section) ? rows : 0;//rows
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return tableView.rowHeight
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return tableView.rowHeight
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "Header")
if let categoryCell = cell as? MyTableViewCell {
categoryCell.category = section + 1
let recognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture))
recognizer.numberOfTapsRequired = 1
recognizer.numberOfTouchesRequired = 1
categoryCell.contentView.tag = section;
categoryCell.contentView.addGestureRecognizer(recognizer)
}
return cell?.contentView
}
func handleTapGesture(recognizer: UITapGestureRecognizer) {
if let sindex = recognizer.view?.tag {
selectedChanged(to: sindex)
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Body", for: indexPath)
if let label = cell.viewWithTag(1) as? UILabel {
label.text = "Body \(indexPath.section + 1) - \(indexPath.row + 1)"
}
return cell
}
}
As you can see now I am just reloading a particular section instead of reloading the whole table.
also, I have removed gesture recognizer from the cell & put this into the main controller.
I will be using a dynamic number of cells and resizing the height of the table on that basis. I could work out how to do this myself by I can't seem to resize the table. Whether I use 100 cells or 10 the view is set at the same height.
class generalTableViewController: UITableViewController {
override func viewWillAppear(_ animated: Bool) {
tableView.rowHeight = UITableViewAutomaticDimension
// self.tableView.contentSize.height = 2000
// self.tableView.frame.size.height = 2000
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1000
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
return cell
}
}
UITableViewControllers generally take over the entire view. To occupy only a portion of the view with a UITableView use a UIViewController.
You should use a UIViewController subclass rather than a subclass of UITableViewController to manage a table view if the view to be managed is composed of multiple subviews, only one of which is a table view. The default behavior of the UITableViewController class is to make the table view fill the screen between the navigation bar and the tab bar (if either are present).
See here for Apple guidance. You basically just need to take care of some of the things the controller would normally handle for you.
I have created UIViewController and added UITableView on it (pinned to all four edges with autolayout).
Then I set estimatedRowHeight (44) and rowHeight (UITableViewAutomaticDimension) and returned 5 custom cells. And it worked.
Now, I want to add custom UITableViewHeaderFooterView that would have dynamic height.
I'm doing next:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 88.0
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return R.nib.orderStatusHeaderView.firstView(owner: self)!
}
My OrderStatusHeaderView is a xib view that has UITableView on it pinned to all 4 edges with autolayout.
OrderStatusHeaderView:
final class OrderStatusHeaderView: UITableViewHeaderFooterView {
#IBOutlet weak var tableView: UITableView! {
didSet {
tableView.dataSource = self
tableView.delegate = self
tableView.rowHeight = 44.0
}
}
}
extension OrderStatusHeaderView: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "\(indexPath.row)")
cell.textLabel?.text = "\(indexPath.row)"
cell.backgroundColor = .red
return cell
}
}
This displays like:
And when I tap or scroll, all red cells disappears. What could it be? And how to make UITableView dynamically load content and UITableViewHeaderFooterView will size itself so it fit UITableView.contentSize. Is it possible?
Check out:
tableView(_:estimatedHeightForFooterInSection:)
and
tableView(_:heightForFooterInSection:)
You also have the equilavant for headerInSection.
Since you are asking for a table's header and footer view, you can skip the delegate methods you describe. Those (as the name implies) are for SECTION headers and footers.
When you set a view that is using AutoLayout as the table's header or footer, the its frame still has a zero height (That's why buttons in such view for example won't work as they are not receiving the touches).
To correctly size a table's header or footer views using AutoLayout you have to apply a trick to actually calculate the height yourself, and set the headerView again. It is described in detail in many posts like these:
https://stackoverflow.com/a/28102157/756039
https://gist.github.com/marcoarment/1105553afba6b4900c10
http://collindonnell.com/2015/09/29/dynamically-sized-table-view-header-or-footer-using-auto-layout/
http://roadfiresoftware.com/2015/05/how-to-size-a-table-header-view-using-auto-layout-in-interface-builder/
Hope this helps.