How to Show/Hide UITableView Header when scrolling up & down? - uitableview

I have added a HeaderView(from .xib) to UITableView,it is showing properly when i load the ViewController.But how will i hide the tableView Header as i scroll up to any position of tableView and show HeaderView when i scroll down.If anyone could help me it will be appreciated. Below is my code:
class ViewController: UIViewController,UITableViewDataSource,UIScrollViewDelegate {
#IBOutlet var myTable : UITableView!
var array = ["See All Local Deals","Food & Drink","Kids Activities","Taxi" , "Shopping" , "Local Tradesmen" , "Tattoo shop" , "Car Hire" , "Health & Fitness" , "Beauty & Spas" , "Home & Garden" , "Local Services" , "Nightlife" , "Wedding Planning" , "Holiday rentals"]
lazy var headerView : HeaderView? = {
return Bundle.main.loadNibNamed("HeaderView", owner: self, options: nil)?[0] as? HeaderView
}()
override func viewDidLoad() {
super.viewDidLoad()
self.myTable.dataSource = self
self.myTable.tableHeaderView = headerView
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyCell
cell.lblText.text = array[indexPath.row]
return cell
}
}

In storyboard-> Select your tableview-> go with property inspector-> And change type to Grouped from Plain

In Swift 5.0
The programatic way of doing this is to use UITableView.Style.grouped:
self.tableView = UITableView(frame: .zero, style: .grouped)

Related

UIImages as icons in side push menu

I would like to preface this by saying I am new to iOS.
I am currently trying to create a side push menu that when clicked on has icons beside each menu item. I currently have the side push menu working with several menu items listed, however, I am really struggling trying to get the images beside each item.
I have written the side push menu and list items programmatically with the code below.
import UIKit
class SideMenuTable: UITableViewController {
let sideMenuImages = [UIImage(named: "Main"), UIImage(named: "Departments"), UIImage(named: "Deliveries"), UIImage(named: "Warehouse"), UIImage(named: "Help")]
var menu = [Menu]()
#IBOutlet weak var test: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Change Menu View"
menu = [
Menu(title: "Main"),
Menu(title: "Departments"),
Menu(title: "Deliveries"),
Menu(title: "Warehouse"),
Menu(title: "Help")
]
}
override func viewWillAppear(_ animated: Bool) {
if tableView.isHidden {
if let selectionIndexPath = tableView.indexPathForSelectedRow {
tableView.deselectRow(at: selectionIndexPath, animated: animated)
}
} else if tableView.isFocused {
if let selectionIndexPaths = tableView.indexPathForSelectedRow {
tableView.deselectRow(at: selectionIndexPaths, animated: animated)
}
}
super.viewWillAppear(animated)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menu.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "sideCell", for: indexPath)
let menuSide: Menu
// var image: UIImage = UIImage(named: ".png")
menuSide = menu[indexPath.row]
cell.textLabel!.text = menuSide.title
cell.textLabel?.textAlignment = .center
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if(indexPath.section == 1) {
if(indexPath.row == 3) {
dismiss(animated: true, completion: nil)
performSegue(withIdentifier: "Warehouse", sender: self)
} else {
NSLog("NO SELECTION AVAILABLE")
}
}
}
}
As you can see from the picture below.
]1
I have started to attempt to solve my problem by inserting a UIImage into the prototype cells.
However, when I connect the UIImage as an outlet into the code above I receive this error. (NOTE: in the code above the outlet is named test)
It is here where I am getting stuck.
I need to connect this outlet in order to add icons for the various menu items I have.
What am I missing?
Is there a different solution to this problem of creating icons for menu items?
Do I need to create a separate subclass?
You need to create a custom class for UITableviewcell where you need to create IBOutlet to imageview and label.
Now you can access it in your cellForRowAtIndexPath method.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "sideCell", for: indexPath) as YourCustomCell
let menuSide: Menu
var image: UIImage = UIImage(named: ".png")
menuSide = menu[indexPath.row]
cell.customLabel!.text = menuSide.title
cell.customLabel?.textAlignment = .center
Cell.imgViewName.image = image
return cell
}
Here customLabel and imgViewName is the name of your IBOutlets which you need to drag in you custom cell from storyboard also provide the name of custom cell in your storyboard by selecting that particular cell.
if your not want to create custom class then please set any tag on your uiimageview and uilabel and use as below
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let lblmenu : UILabel = cell.contentView.viewWithTag(12) as! UILabel
lblmenu.text = ArrMenuoption[indexPath.row] as? String
let img : UIImageView = cell.contentView.viewWithTag(11) as! UIImageView
img.image = UIImage.init(named: ArrMenuoptionimg[indexPath.row] as! String )
return cell }

Infinite scroll with UITableView and an Array

I'm new in Swift and I want to make an infinite scroll with an Array. Here it's my class TableViewController
class TableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var legumes: [String] = ["Eggs", "Milk", "Chocolat", "Web", "Miel", "Pop", "Eco", "Moutarde", "Mayo", "Thea", "Pomelade", "Gear", "Etc" , "Nop", "Dews", "Tout", "Fun", "Xen" , "Yoga" ]
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.legumes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ImageTableViewCell
return cell
}
}
I want to show the first ten items of my array and when I am on the bottom of the TableViewController, it will load the next ten items, etc. I don't know how to do, I see a lot of code on GitHub but I don't know how to implement them.
Thank you so much.
Consider using PagingTableView
class MainViewController: UIViewController {
#IBOutlet weak var contentTable: PagingTableView!
var legumes: [String] = ["Eggs", "Milk", "Chocolat", "Web", "Miel", "Pop", "Eco", "Moutarde", "Mayo", "Thea", "Pomelade", "Gear", "Etc" , "Nop", "Dews", "Tout", "Fun", "Xen" , "Yoga" ]
override func viewDidLoad() {
super.viewDidLoad()
contentTable.dataSource = self
contentTable.pagingDelegate = self
}
}
extension MainViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return legumes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ImageTableViewCell
guard legumes.indices.contains(indexPath.row) else { return cell }
cell.content = legumes[indexPath.row]
return cell
}
}
extension MainViewController: PagingTableViewDelegate {
func paginate(_ tableView: PagingTableView, to page: Int) {
contentTable.isLoading = true
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.legumes.append(contentsOf: legumes)
self.contentTable.isLoading = false
}
}
}
Modify the paginate function to work as you wish
What you're describing is called pagination. You should do something like this:
/* Number of page you're loading contents from */
var pageIndex: Int = 1
override func scrollViewDidScroll(scrollView: UIScrollView) {
let offsetY = scrollView.contentOffset.y
let contentHeight = scrollView.contentSize.height
if offsetY > contentHeight - scrollView.frame.size.height {
/* increment page index to load new data set from */
pageIndex += 1
/* call API to load data from next page or just add dummy data to your datasource */
/* Needs to be implemented */
loadNewItemsFrom(pageIndex)
/* reload tableview with new data */
tableView.reloadData()
}
}

Designing a UITableView/Cell - iOS

I'm designing a UITableView using subviews to populate the reusable cell of it, and I wish some opinion about that.
As I had tested, it works well. But, I don't know if it is a good solution.
The scenario is: I have a tableview with different kind of cells (layouts). When I was designing, it grows fast (my controller code), as I had to register a lot of cell and handle cellForRow. Then I come with that idea, to instantiate different subviews for one unique reusable cell and use a 'Presenter' to handle delegate/datasource. You think is that a problem? And is that a good approach?
Thanks in advance!
Ps.: sorry for any english error!
EDITED:
Here is the session in project followed by de codes:
Codes at:
OrderDetailCell
class OrderDetailCell: UITableViewCell {
//MARK: Outlets
#IBOutlet weak var cellHeight: NSLayoutConstraint!
#IBOutlet weak var viewContent: UIView!
//Variables
var didUpdateLayout = false
internal func setupLayoutWith(view: UIView){
cellHeight.constant = view.frame.height
viewContent.frame = view.frame
viewContent.addSubview(view)
updateConstraints()
layoutIfNeeded()
didUpdateLayout = true
}
}
OrderDetailSubview
class OrderDetailSubview: UIView {
var type: OrderDetailsSubViewType?
var height: CGFloat = 1
class func instanceFromNib(withType type: OrderDetailsSubViewType) -> OrderDetailSubview {
let view = UINib(nibName: type.rawValue, bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! OrderDetailSubview
switch type {
case .OrderDetailSubviewStatus:
view.height = 258
case .OrderDetailSubViewItem:
view.height = 129
case .OrderDetailSubViewStoreInformation:
view.height = 317
case .OrderDetailSubViewEvaluation:
view.height = 150
}
view.updateConstraints()
view.layoutIfNeeded()
return view
}
}
OrderDetailPresenter
enum OrderDetailsSubViewType: String {
case OrderDetailSubviewStatus = "OrderDetailSubviewStatus",
OrderDetailSubViewItem = "OrderDetailSubViewItem",
OrderDetailSubViewStoreInformation = "OrderDetailSubViewStoreInformation",
OrderDetailSubViewEvaluation = "OrderDetailSubViewEvaluation"
static let types = [OrderDetailSubviewStatus, OrderDetailSubViewItem, OrderDetailSubViewStoreInformation, OrderDetailSubViewEvaluation]
}
class OrderDetailPresenter {
//Constants
let numberOfSections = 4
//Variables
// var order: Order?
func setup(reusableCell: UITableViewCell, forRowInSection section: Int) -> OrderDetailCell {
let cell = reusableCell as! OrderDetailCell
for sub in cell.viewContent.subviews {
sub.removeFromSuperview()
}
let subView = OrderDetailSubview.instanceFromNib(withType: OrderDetailsSubViewType.types[section])
cell.setupLayoutWith(view: subView)
return cell
}
func numberOfRowsForSection(_ section: Int) -> Int {
switch section {
case 1:
//TODO: count de offerList
return 4
default:
return 1
}
}
}
OrderDetailViewController
class OrderDetailViewController: BaseViewController {
//MARK: Outlets
#IBOutlet weak var tableView: UITableView!
var presenter = OrderDetailPresenter()
override func setupView() {
setupTableView()
}
}
extension OrderDetailViewController: UITableViewDataSource, UITableViewDelegate {
internal func setupTableView() {
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 600
tableView.rowHeight = UITableViewAutomaticDimension
tableView.register(UINib(nibName: "OrderDetailCell", bundle: nil), forCellReuseIdentifier: "OrderDetailCell")
}
func numberOfSections(in tableView: UITableView) -> Int {
return presenter.numberOfSections
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return presenter.numberOfRowsForSection(section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let reusableCell = tableView.dequeueReusableCell(withIdentifier: "OrderDetailCell") as! OrderDetailCell
let cell = presenter.setup(reusableCell: reusableCell, forRowInSection: indexPath.section)
return cell
}
}
*Sorry for indentation here...
Thats it! What you think?
Here you want to have multiple UITableViewCell subclasses that implement the different layouts that you want, and then select the relevant one in you table view data source.
class Cell1: UITableViewCell {
let label = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(label)
}
... whatever other setup/layout you need to do in the class ...
}
class Cell2: UITableViewCell {
let imageView = UIImageView()
override init(style: UITableViewCellStyle, reuseIdentifier: String) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(imageView)
}
... whatever other setup/layout you need to do in the class ...
}
Then in your view controller
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(Cell1.self, forCellReuseIdentifier: "cell1Identifier")
tableView.register(Cell2.self, forCellReuseIdentifier: "cell2Identifier")
}
...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row % 2 == 0 { // just alternating rows for example
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1Identifier", for: indexPath) as! Cell1
// set data on cell
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2Identifier", for: indexPath) as! Cell2
// set data on cell
return cell
}
}
So this is just an example, but is using two different cell subclasses for alternating rows in the table view.
let dynamicCellID: String = "dynamicCellID" //One Cell ID for resuse
class dynamicCell: UITableViewCell {
var sub: UIView // you just need to specify the subview
init(sub: UIView) {
self.sub = sub
super.init(style: .default, reuseIdentifier: dynamicCellID)
self.addSubview(sub)
self.sub.frame = CGRect(x: 0, y: 0, width: sub.frame.width, height: sub.frame.height)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And you need to create a views array the give that view to every cell in delegate
let views: [UIView] = []
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return views.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let v = views[indexPath.row]
return dynamicCell(sub: v)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let v = views[indexPath.row]
return v.frame.height + 10 //offset is 10 point
}

UITableView doesn't scrolling while scrolling over the cell

I created a UITableView in a UIViewController from the storyboard and create custom tableViewCell class. Now when I run my project,
It is not scrolling when I touch any cell and move up/down.
BUT, it scrolls if I start scrolling with the either end of UItableViewCell (nearly, 15px of left inset).
I tried to create another fresh tableView, still not working.
I tried to create a tableViewController, still not working.
Then I think the code is NOT the cause of the issue.
Using Xcode 8.2.1
Below is my code work :
Class File
struct Quote {
var text: String
}
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableView: UITableView?
let cellIdentifier = "cell"
// Array of strings for the tableView
var tableData = [Quote(text: "zadz ad azd azds fsd gdsfsd"), Quote(text: "zakd gqsl jdwld bslf bs ldgis uqh dm sd gsql id hsqdl sgqhmd osq bd zao mos qd"), Quote(text: "azdhsqdl sb ljd ghdlsq h ij dgsqlim dhsqihdùa dbz ai ljsm oqjdvl isq dbvksqjld"), Quote(text: "dsqb jhd gs qdgsq dgsq u hdgs qli hd gsql i dgsq li dhs qij dhlqs dqsdsd.")]
override func viewDidLoad() {
super.viewDidLoad()
self.tableView?.register(UITableViewCell.self, forCellReuseIdentifier: self.cellIdentifier)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return number of rows in table
return tableData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Create Resusable Cell, get row string from tableData
let cell = tableView.dequeueReusableCell(withIdentifier: self.cellIdentifier)! as! cellClass
let row = indexPath.row
// Set the labels in the custom cell
cell.mainText.text = tableData[row].text
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Do what you want here
let selectValue = self.tableData[indexPath.row]
print("You selected row \(indexPath.row) and the string is \(selectValue)")
}
}
And this is my cellClass: (Custom cell)
class cellClass: UITableViewCell {
#IBOutlet weak var mainText: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Storyboard hierarchy of UITableView
You might be have some x-code issues because generally it never happens and I run your project it working properly as usually it works.
Below is code work I have done.
I'm not taking sturcture of array like you, I'm just doing with taking simple array.
my array is
arrayData = ["One", "Two", "three", "four"]
below is cellForRow
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : cellClass = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! cellClass
cell.lblstates.text = arrayData[indexPath.row]
return cell
}
If you facing any issue then let me know.
Check if the user interaction Enabled check of your cell is off or not in the storyboard
Resolved: I had to uninstall and install Xcode again..

Setting rowHeight equal to UITableViewAutomaticDimension not working

I'm using XCode 6.3 to build a TableView of the different Fonts in iOS 8. First, per the book I'm reading, it said that nothing needed to be done regarding the height of the table rows, given that iOS8 takes care of that for you, so once I had everything per the book, the rows should update their heights based on their content, which wasn't the case. Then I tried to play with tableView.rowHeight and I set it equal to UITableViewAutomaticDimension in the TableViewController's viewDidLoad function, and that didn't work either. I also tried changing the height of the rows from Interface Builder, and that didn't seem to have any effect on the heights either. My code is as follows:
class RootViewController: UITableViewController {
private var familyNames: [String]!
private var cellPointSize: CGFloat!
private var favoritesList: FavoritesList!
private let familyCell = "FamilyName"
private let favoritesCell = "Favorites"
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = UITableViewAutomaticDimension
familyNames = sorted(UIFont.familyNames() as! [String])
let preferredTableViewFont = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
cellPointSize = preferredTableViewFont.pointSize
favoritesList = FavoritesList.sharedFavoritesList
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}
func fontForDisplay(atIndexPath indexPath: NSIndexPath) -> UIFont? {
if indexPath.section == 0 {
let familyName = familyNames[indexPath.row]
let fontName = UIFont.fontNamesForFamilyName(familyName).first as! String
return UIFont(name: fontName, size: cellPointSize)
} else {
return nil
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return favoritesList.favorites.isEmpty ? 1 : 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return section == 0 ? familyNames.count : 1
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return section == 0 ? "All Font Families" : "Favorite Fonts"
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier(familyCell, forIndexPath: indexPath) as! UITableViewCell
cell.textLabel!.font = fontForDisplay(atIndexPath: indexPath)
cell.textLabel!.text = familyNames[indexPath.row]
cell.detailTextLabel!.text = familyNames[indexPath.row]
return cell
} else {
return tableView.dequeueReusableCellWithIdentifier(favoritesCell, forIndexPath: indexPath) as! UITableViewCell
}
}
}
When I run this in the simulator, everything looks right until I scroll all the way to the bottom and I get this:
The attributes of the FontFamily cell are: style = subtitle, and accessory = disclosure indicator.
Any ideas on what I'm be doing wrong?
You must set self.tableView.estimatedRowHeight

Resources