Swift - adding cells in UITableView - ios

I have a UITableView and I am trying to add some content (Whish) in my case. I have set up everything programmatically but somehow adding cells is not working for me.
This is my UITableView and custom Wish Class for my data:
import UIKit
class WhishlistTableViewController: UITableViewController {
public var wishList : [Wish]?
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(WhishCell.self, forCellReuseIdentifier: WhishCell.reuseID)
self.wishList?.append(Wish(withWishName: "Test"))
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return wishList?.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: WhishCell.reuseID, for: indexPath)
let currentWish = self.wishList![indexPath.row]
cell.textLabel?.text = currentWish.wishName
return cell
}
}
class Wish: NSObject {
public var wishName : String?
init(withWishName name: String) {
super.init()
wishName = name
}
}
And this is how I am creating the WishlistTableView inside my other UIViewController:
let theTableView: WhishlistTableViewController = {
let v = WhishlistTableViewController()
v.view.layer.masksToBounds = true
v.view.layer.borderColor = UIColor.white.cgColor
v.view.layer.borderWidth = 2.0
v.view.translatesAutoresizingMaskIntoConstraints = false
return v
}()
and its constraints:
theTableView.view.topAnchor.constraint(equalTo: wishlistView.topAnchor, constant: 180.0),
theTableView.view.bottomAnchor.constraint(equalTo: wishlistView.bottomAnchor, constant: 0),
theTableView.view.leadingAnchor.constraint(equalTo: wishlistView.safeAreaLayoutGuide.leadingAnchor, constant: 30.0),
theTableView.view.trailingAnchor.constraint(equalTo: wishlistView.safeAreaLayoutGuide.trailingAnchor, constant: -30.0),
The TableView appears just fine with its constraints but adding cells is not working and I have no idea why.. Grateful for every help :)

Replace
public var wishList : [Wish]?
with
public var wishList = [Wish]()
As you never init wishList so adding item here wishList?.append won't do anything
self.wishList.append(Wish(withWishName: "Test"))
Also remove this as it's the default
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

Related

Filtering query for Realm

I have a function which prints all the objects in my realm table to a table view. I would like to be able to filter these objects by their "muscle" property.
Here's my DB helper functions:
func getMusclesCount()-> Int {
let storedExercise = realm.objects(StoredExercise.self)
return storedExercise.count
}
//MARK:- getAllMuscelsNames
func getAllMusclesNames()-> [String] {
var musclesName = [String]()
let storedExercise = realm.objects(StoredExercise.self)
for exercise in storedExercise {
print("Muscle = \(exercise.muscle)")
musclesName.append(exercise.name)
}
return musclesName
}
Here's my Table View Controller class :
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return DBHelper.shared.getAllMusclesNames().count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
}
let muscle = DBHelper.shared.getAllMusclesNames()[indexPath.row]
cell.textLabel?.text = muscle
return cell
}
I've tried adding .Filter to 'let storedExercise' but I'm not sure how to set it up correctly. Any assitance would be greatly appreciated, thanks.
If your StoredExercise model looks like this
class StoredExercise: Object {
#objc dynamic var muscle = ""
}
then to get all of the exercises that are for the biceps, it's this
let bicepResults = realm.objects(StoredExercise.self).filter("muscle == 'biceps'")

How to connect swift codes and storyboard?

I am trying to figure out a way to use the Pod ColorMatchTabs, however its example is doing it programmatically and I can't find a way to use them in my project. I will summarize this:
To use the pod, I have to follow this protocols to install the tabs in my viewController:
extension ExampleViewController: ColorMatchTabsViewControllerDataSource {
func tabsViewController(_ controller: ColorMatchTabsViewController, iconAt index: Int) -> UIImage {
return TabItemsProvider.items[index].normalImage
}
func tabsViewController(_ controller: ColorMatchTabsViewController, hightlightedIconAt index: Int) -> UIImage {
return TabItemsProvider.items[index].highlightedImage
}
func numberOfItems(inController controller: ColorMatchTabsViewController) -> Int {
return TabItemsProvider.items.count
}
func tabsViewController(_ controller: ColorMatchTabsViewController, viewControllerAt index: Int) -> UIViewController {
return StubContentViewControllersProvider.viewControllers[index]
}
func tabsViewController(_ controller: ColorMatchTabsViewController, titleAt index: Int) -> String {
return TabItemsProvider.items[index].title
}
func tabsViewController(_ controller: ColorMatchTabsViewController, tintColorAt index: Int) -> UIColor {
return TabItemsProvider.items[index].tintColor
}
These are the protocols to handle how the tabs look like, how many items and my difficulties are in the part calling UIViewController. I assume it return which controllers are used based on which tab, and here goes the code in StubContentViewControllersProvider:
import UIKit
import ColorMatchTabs
class StubContentViewControllersProvider {
static let viewControllers: [UIViewController] = {
let productsViewController = StubContentViewController()
productsViewController.type = .products
let venuesViewController = StubContentViewController()
venuesViewController.type = .venues
let reviewsViewController = StubContentViewController()
reviewsViewController.type = .reviews
let usersViewController = StubContentViewController()
usersViewController.type = .users
return [productsViewController, venuesViewController, reviewsViewController, usersViewController]
}()
}
I assume we are calling all the controllers from StubContentViewController and here is how it looks like:
import UIKit
class StubContentViewController: UITableViewController {
enum `Type` {
case products, venues, reviews, users
}
var type: Type!
fileprivate var objects: [UIImage] = []
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
setupDataSource()
}
fileprivate func setupTableView() {
tableView.backgroundColor = UIColor.clear
tableView.allowsSelection = true
tableView.separatorColor = UIColor.clear
tableView.register(UINib(nibName: "ExampleTableViewCell", bundle: nil), forCellReuseIdentifier: "cell")
}
fileprivate func setupDataSource() {
if type == .products || type == .reviews {
self.objects = [UIImage(named: "product_card1")!, UIImage(named: "product_card2")!]
} else if type == .venues || type == .users {
self.objects = [UIImage(named: "venue_card1")!, UIImage(named: "venue_card2")!]
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objects.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ExampleTableViewCell
let image = objects[(indexPath as NSIndexPath).row]
cell.apply(image)
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return tableView.bounds.width / 1.4
}
}
So my question here is, I am setting up a TableViewController and reference it to StubContentViewController in storyboard, but why when I make changes in the storyboard, it doesn't make any effect? And for example I want to perform a segue for the table cell, I couldn't find any way to make it via the storyboard?

How to Update UITableView With Swift?

I'm trying to populate a table view from a SQlite database. Tickets get printed in the console, but nothings shows up on the table view. What's the proper way to update and refresh? Here is my code. Thanks!
import UIKit
import SQLite
class TicketTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var tickets = [String]()
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let dm = DatabaseManager.shared
let db = dm.db!
do {
for row in try db.prepare(dm.tickets) {
let ticket = row[dm.pick]
tickets.append(ticket)
debugPrint(ticket)
}
table.reloadData()
} catch {}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tickets.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ticket")!
cell.detailTextLabel!.text = tickets[indexPath.row]
return cell
}
}
Dynamic table views needs to know their delegate and datasource. If you didn't set the delegate and datasource, you can add them programmatically in your viewDidLoad function. Like this:
override func viewDidLoad() {
super.viewDidLoad()
//Set delegate and datasource
table.delegate = self
table.dataSource = self
let dm = DatabaseManager.shared
let db = dm.db!
do {
for row in try db.prepare(dm.tickets) {
let ticket = row[dm.pick]
tickets.append(ticket)
debugPrint(ticket)
}
table.reloadData()
} catch {}
}

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()
}
}

Setting a UITableView data source and delegate in separate file - swift

If I would like to have the same basic UITableView appearing in two different scenes, is it a good idea to use one datasource and delegate location for both tables?
I wanted to try this, but when I select the table view in IB and try to drag the line to a custom class of UITableView file, or even to another custom view controller, it will not connect. It only seems possible to make the current View Controller into the table's datasource and delegate(?).
I'm wondering if this is at least similar to this question, but even if it is, how is this done in swift (and perhaps there is a new way to do this).
Swift 4.1. You can create separate class and inherit it from UITableViewDataSource and UITableViewDelegate class. Here, I am implementing UITableViewDataSource() methods in DataSource class. You also need to confirm NSObject so that we don’t have to fiddle with the #objc and #class keywords because UITableViewDataSource is an Objective-C protocol.
import Foundation
import UIKit
class DataSource: NSObject, UITableViewDataSource {
var formData: [FormData]? = nil
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.formData?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let label = cell?.contentView.viewWithTag(100) as? UILabel
let type = self.formData![indexPath.row]
label?.text = type.placeHolder
return cell!
}
}
Now, We will set DataSource to UITableView. If we crate separate class then we have to pass data to DataSource class.
class ViewController: UIViewController {
#IBOutlet weak var tblView: UITableView!
var formData: [FormData]? = nil
var dataSource = DataSource()
override func viewDidLoad() {
super.viewDidLoad()
formData = FormData.array
dataSource.formData = formData // Pass data to DataSource class
tblView.dataSource = dataSource // Setting DataSource
}
}
In similar way you can implement UITableViewDelegate in separate class. The other way to separate DataSource and Delegate is by creating extension of your viewController. Even you can crate separate class where you can only define extensions for your view controller. In you define extension then you don't need to pass data.
class ViewController: UIViewController {
#IBOutlet weak var tblView: UITableView!
var formData: [FormData]? = nil
override func viewDidLoad() {
super.viewDidLoad()
formData = FormData.array
tblView.dataSource = self
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.formData?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let label = cell?.contentView.viewWithTag(100) as? UILabel
let type = self.formData![indexPath.row]
label?.text = type.placeHolder
label?.backgroundColor = UIColor.gray
return cell!
}
}
Here is a code example showing different Datasource and delegates for UITableView.
Code in Swift
import UIKit
// MARK: Cell
class ItemCell: UITableViewCell{
var label: UILabel!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 20))
label.textColor = .black
label.backgroundColor = .yellow
contentView.addSubview(label)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: Main View Controller
class BlueViewController: UIViewController{
var tableView: UITableView!
var myDataSourse: MyTVDataSource!
var myDelegate: MyTVDelegate!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .blue
tableView = UITableView()
myDataSourse = MyTVDataSource(tableView: tableView)
myDelegate = MyTVDelegate()
myDelegate.presentingController = self
tableView.dataSource = myDataSourse
tableView.delegate = myDelegate
tableView.register(ItemCell.self, forCellReuseIdentifier: "Cell")
self.view.addSubview(tableView)
self.tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0)
])
}
}
extension BlueViewController: BluePresenting{
func currentSelected(_ indexPath: IndexPath) {
print(indexPath)
}
}
// MARK: TableViewDelegate
protocol BluePresenting: class {
func currentSelected(_ indexPath: IndexPath)
}
class MyTVDelegate: NSObject,UITableViewDelegate{
var presentingController: BluePresenting?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
presentingController?.currentSelected(indexPath)
}
}
// MARK: TableView DataSource
class MyTVDataSource: NSObject, UITableViewDataSource{
private var tableView: UITableView
private var items = ["Item 1","item 2","item 3","Item 4"]
init(tableView: UITableView) {
self.tableView = tableView
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = ItemCell(style: .default, reuseIdentifier: "Cell")
cell.label.text = items[indexPath.row]
return cell
}
}
You can implement a custom class object, and implement the UITableViewDataSource methods for this class.
#interface MyDataSource : NSObject <UITableViewDataSource>
//...
#end
And then, the UITableView has properties, delegate and dataSource.
Assign right objects to those properties.
MyDataSource ds = ... ///< Initialize the dataSource object.
self.tableView.dataSource = ds; ///< Let ds be the dataSource of `self.tableView`
self.tableView.delegate = .... ///< Assign the delegate, generally it is `self`.
Each Tableview should have its own Tableview controller. This is in accordance with the Model View Controller Design Pattern.
If the data in the the two tables are the same, you could have a common class serve as the dataSource.

Resources