CellForRowAtIndexPath not Called when reloading table data - swift

I've worked through a lot of answers like this one but I'm still stuck.
I have a viewcontroller with a collectionView and a searchController. When the user searches I want to display a UITableView on top of the collectionView. I'm trying to add the tableview programmatically b/c it's a simple text label I'm displaying
I am able to display the search results for the dummy array I have in viewdidload of my tableviewcontroller class. However when I get the actual results, I can only print them in the numberOfRows function.
All help would be greatly appreciated, here's my code:
This is my TableView Class:
import UIKit
class HashtagSearchList: UITableViewController {
var hashtagListTableview = UITableView()
var hashtagList = [String]()
override func viewDidLoad() {
super.viewDidLoad()
hashtagList = ["One", "Two", "Three"]
hashtagListTableview.delegate = self
hashtagListTableview.dataSource = self
}
// MARK: - Table view data source
//I don't need this, but added it based on some answers... didn't help
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//All these print statements show correct values
print("List Count = \(hashtagList.count)")
print("List of Strings: \(hashtagList)")
print("frame size = \(tableView.frame)")
return hashtagList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
//This print statement only fires on ViewdidLoad
print("cellForRowAt = \(hashtagList[indexPath.row])")
cell.textLabel?.text = hashtagList[indexPath.row]
cell.backgroundColor = .red
return cell
}
}
Here is my ViewController / SearchController code:
class ExploreVC: UIViewController {
var hashtagSearchController = HashtagSearchList()
override func viewDidLoad() {
super.viewDidLoad()
//Search Controller
navigationItem.searchController = searchController
searchController = UISearchController(searchResultsController: hashtagSearchController)
}
//......
// SEARCH CONTROLLER
extension ExploreVC: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let searchText = searchController.searchBar.text
if searchText == "" { return }
PostFirebase.getHashtagList(hashtag: searchText) { (hashtagList) in
self.hashtagSearchController.hashtagListTableview.frame = CGRect(x: 0.0, y: 0.0, width: self.view.bounds.width, height: self.view.bounds.height)
self.hashtagSearchController.hashtagList = hashtagList
//self.hashtagSearchController.hashtagListTableview.reloadData()
DispatchQueue.main.async {
self.hashtagSearchController.hashtagListTableview.reloadData()
}
}
}
}
}
Here is the tableview from viewDidLoad, and it never changes from here

You initialized hashtagListTableview in HashtagSearchList but you didn't add layout constraints. By default, it will have .zero frame and won't be displayed on the screen.
I guess that the table view on the screen is tableView from UITableViewController. That's why nothing happened when you call self.hashtagSearchController.hashtagListTableview.reloadData().
To fix it, try to use tableView instead of hashtagListTableview. Replace
self.hashtagSearchController.hashtagListTableview.reloadData()
with
self.hashtagSearchController.tableView.reloadData()

Related

Swift: Change height of UINavigationController view

I have a UITableView embedded in a UINavigationController. The UITableView is set up as follows:
import UIKit
class HistoryTableViewController: UITableViewController {
let cellID = "HistoryColumns"
struct CellData {
let date: String?
let number: String?
let before: String?
let after: String?
let diff: String?
let strength: String?
let location: String?
}
var data = [CellData]()
override func viewDidLoad() {
super.viewDidLoad()
data = [
// data goes here
]
tableView.register(HistoryTableViewCell.self, forCellReuseIdentifier: cellID)
self.navigationController?.navigationBar.topItem?.title = "Recent Activity"
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as! HistoryTableViewCell
if indexPath.row % 2 == 0 {
cell.backgroundColor = UIColor.orange.withAlphaComponent(0.3)
}
cell.date.valueLabel.text = data[indexPath.row].date
cell.number.valueLabel.text = data[indexPath.row].flightNumber
cell.before.valueLabel.text = data[indexPath.row].preFuel
cell.after.valueLabel.text = data[indexPath.row].postFuel
cell.diff.valueLabel.text = data[indexPath.row].uplift
cell.strength.valueLabel.text = data[indexPath.row].density
cell.location.valueLabel.text = data[indexPath.row].location
return cell
}
}
I call the controller within a UINavigationController as follows:
#objc func handleTap(_ sender: UITapGestureRecognizer? = nil) {
DispatchQueue.asyncMain {
let history = HistoryTableViewController()
let navigationController = UINavigationController(rootViewController: history)
self.present(navigationController, animated: true, completion: nil)
}
}
What I want to do is to define the height of the navigationController's view here (around 500). I've tried using navigationController.view.heightAnchor.constraint but it had no effect (obviously I set translatesAutoresizingMaskIntoConstraints to false first)...
I do want to use a UINavigationController here because I need the navbar that comes with it. Any suggestions greatly appreciated...
When you say .present, the way the presented view controller occupies the screen is up to you. Make a custom transition and supply your own UIPresentationController subclass with an implementation of frameOfPresentedViewInContainerView to say where the presented view controller's view should go.
This is a silly example with a custom animation as well, but you don't have to customize the animation if you don't want to. The important thing is that I've made the presented view controller smaller than it normally would be.

Variable not updating inside the view

For some reason, the variable Tags isnt being updated. When SelectedSauce is ran, it should run the function and then update the variable. But it's not being updated once it leaves the function. Im not sure what is wrong with this. When I change views, I pass a variable to selectedsauce from the previous view to here. im not sure if it helps or changes anything but I am using the Realm Database
class InfoUITableViewController: UITableViewController {
var SelectedSauce: Sauce? {
didSet {
print("1st")
AcquireData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
print("3rd")
print("Loaded")
tableView.register(UINib(nibName: "DetailsTableViewCell", bundle: nil), forCellReuseIdentifier: "Super")
tableView.dataSource = self
//Returns nill even though i changed the variable in acquiredata
print(Tags)
}
let realm = try! Realm()
var Tags: List<NiceTags>?
//MARK: - Data Manipulation
func AcquireData() {
print("2nd")
if let Sauce = SelectedSauce {
Tags = Sauce.tags
// print(Tags)
}
self.Tags = self.SelectedSauce?.tags
print(self.Tags)
tableView.reloadData()
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("4th")
let cell = tableView.dequeueReusableCell(withIdentifier: "Super", for: indexPath) as! DetailsTableViewCell
//This isn't running, and just uses the default text inside the label
if let TheTags = Tags?[indexPath.row] {
cell.Inflabel.text = TheTags.tags
}
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of row
print("5th")
//Returns 7 because tags is still nill
return Tags?.count ?? 7
}
}
Set SelectedSauce from tabbar like this
override func viewDidLoad() {
super.viewDidLoad()
swipeAnimatedTransitioning?.animationType = SwipeAnimationType.sideBySide
// isCyclingEnabled = true
// Do any additional setup after loading the view.
print("order")
let first_vc = self.viewControllers?.first as! DetailViewController
let last_vc = self.viewControllers?.last as! InfoUITableViewController
first_vc.SelectedSauce = SelectedSauce
last_vc.SelectedSauce = SelectedSauce
}

I want to display data from CoreData into a tableview

I want to display data from CoreData into a tableview, im working on favoris, im adding my events on favoris and i want to display it inside a tableview, there is my code :
var lists : [NSManagedObject] = [] {
didSet {
favorisEventTableView.reloadData()
}
}
#IBOutlet weak var favorisEventTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
favorisEventTableView.dataSource = self
favorisEventTableView.delegate = self
loadFavoris()
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
favorisEventTableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lists.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = favorisEventTableView.dequeueReusableCell(withIdentifier: "FavorisCell")
let contentView = cell?.viewWithTag(0)
let eventId = contentView?.viewWithTag(4) as! UILabel
let item = lists[indexPath.row]
eventId.text = String((item.value(forKey: "id_event") as! Int))
return cell!
}
func loadFavoris() {
let appDelegate = UIApplication.shared.delegate as? AppDelegate
let coreContext = appDelegate?.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Favoris")
do {
lists = try coreContext!.fetch(fetchRequest)
print(lists)
} catch let error as NSError {
print(error.userInfo)
}
}
But it's not displaying anything, maybe the problem on the add function ? im sure it works cause i get "saved" on the console, any help please?
PS: entity "Favoris" has only one attribute "id_event" which is an integer
You need to add loadFavoris inside viewDidLoad not cellForRowAt
override func viewDidLoad() {
super.viewDidLoad()
favorisEventTableView.dataSource = self
favorisEventTableView.delegate = self
loadFavoris()
}
You need to reload your table view every change on your list. you can do it like that
var lists : [NSManagedObject] = [] {
didSet {
tableView.reloadData()
}
}
Don't forget assign the delegate and dataSource protocols of tableView and fetch the data in viewDidLoad() function
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
loadFavoris()
}

UISearchController/SearchBar in Navigation Controller

I have a TableView with simple data in it. I have added SearchBar to the Navigation controller by code. (Not sure have i done proper way or not, but it is working). This is how it looks:
Code is below:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var data = ["Hello","This","Is","Table","View","With","Search","Bar","Included","In","NavBar","But","SearchBar","Is","Not","Done","Properly"]
var filtered = [String]()
override func viewDidLoad() {
super.viewDidLoad()
//My original array and filtered arrays.
filtered = data
// Here a play with preferences add/delete
let searchBar = UISearchController(searchResultsController: nil)
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.searchController = searchBar
navigationItem.searchController?.dimsBackgroundDuringPresentation = false
navigationItem.searchController?.searchResultsUpdater = self
navigationItem.hidesSearchBarWhenScrolling = false
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filtered.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = filtered[indexPath.row]
return cell
}
}
extension ViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
if searchController.searchBar.text! == "" {
filtered = data
}
else {
filtered = data.filter {$0.lowercased().contains(searchController.searchBar.text!.lowercased())}
}
self.tableView.reloadData()
}
}
As you see i have done it by creating SearchController and then adding it to a NavController.
Also implemented delegate UISearchResultsUpdating method to search and filter.
I have tried several ways but i do not know how to access to properties of SearchBar and NavController. (not by storyboard)
Some screenshots:
After like a day of search and try, i am not actually sure about it, as some methods work but some not. As example:
This works:
navigationItem.searchController?.searchBar.tintColor = .white
This does not work:
navigationItem.searchController?.searchBar.barTintColor = .red
So why can i access to one property and cannot to other?
It is a very simple functionality, but tricky to find.
Maybe you could provide me some resource i could use for my case, as indicated in Images?
Or if you have a time - some solution, maybe root example.
Thanks in advance.
Will continue to search.

Change UITableView section header when it's on top

I am using a custom header from Xib file for my table view using this code:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let result: Result = (self.responseData?.result![section])!
let headerCell = Bundle.main.loadNibNamed("HeaderViewTableViewCell", owner: self, options: nil)?.first as! HeaderViewTableViewCell
headerCell.sectionName.text = "Title"
if (section == 0) {
headerCell.sectionName.textColor = UIColor(red: 49/255.0, green: 149/255.0, blue: 213/255.0, alpha: 1)
}
return headerCell
}
Then I want to change the header sectionName when it is scrolled to top, I have tried this code
let topSection = self.mainTable .indexPathsForVisibleRows?.first?.section
let currentHeader : HeaderViewTableViewCell = self.mainTable .headerView(forSection: topSection!) as HeaderViewTableViewCell
currentHeader.sectionName.textColor = UIColor.red
But I get this error: Cannot convert value of type 'UITableViewHeaderFooterView?' to type 'HeaderViewTableViewCell' in coercion
Is there any way to cast the headerView to my custom type?
First of all I suggest you to use UITableViewHeaderFooterView for your header view. You can make a subclass and add custom code. For this example I will use an empty subclass:
class HeaderView: UITableViewHeaderFooterView {
override func prepareForReuse() {
super.prepareForReuse()
// set you default color (other properties) here.
// when scrolling fast the view gets reused and sometimes
// the view that's on top will suddenly appear on the bottom still with the previous values
textLabel?.textColor = .black
}
}
Register your header view (I am skipping all other unrelated code):
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(HeaderView.self, forHeaderFooterViewReuseIdentifier: "Header")
// If you have a nib file for your HeaderView then register nib instead
// Make sure in our nib file you set class name to HeaderView
// And the file name is also HeaderView.xib
// tableView.register(UINib.init(nibName: "HeaderView", bundle: nil) , forHeaderFooterViewReuseIdentifier: "Header")
}
Implement delegate methods:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = tableView.dequeueReusableHeaderFooterView(withIdentifier: "Header")
view?.textLabel?.text = "Hello"
return view
}
Create a method for updating headers:
// Iterates through section header views and
// checks for positions in relation to the tableview offset
func updateHeaders() {
var sectionHeaders: [Int: HeaderView?] = [:]
var i = 0
while i < numberOfSections {
sectionHeaders[i] = tableView.headerView(forSection: i) as? HeaderView
i += 1
}
let availableHeaders = sectionHeaders.flatMap { $0.value != nil ? $0 : nil }
for (index, header) in availableHeaders {
let rect = tableView.rectForHeader(inSection: index)
if rect.origin.y <= tableView.contentOffset.y + tableView.contentInset.top || index == 0 {
header!.textLabel?.textColor = .red
} else {
header!.textLabel?.textColor = .black
}
}
}
And call your updateHeaders() from UIScrollViewDelegate method scrollViewDidScroll:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
updateHeaders()
}
Also update headers before the view will be displayed (before any scroll appeared), for that use the UITableViewDelegate method willDisplayHeaderView:
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
updateHeaders()
}