UITableView isn't appearing in view? - swift

My tableview isn't loading once I press build, can anyone see what's wrong with the code as to why it's not showing up? In the console, it's printing the data from Firestore, but I can't see anything on the simulator but a white screen.
struct Posts {
var caption:String
}
class CoursesVC: UIViewController, UITableViewDelegate {
let tableView = UITableView()
var posts = [Posts]()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
self.tableView.dataSource = self
self.tableView.delegate = self
setupViews()
loadPosts()
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
func loadPosts() {
let dbUsers = Firestore.firestore().collection("Payouts")
dbUsers.addSnapshotListener { (querySnapshot, error) in
if let error = error {
print("\(error.localizedDescription)")
} else {
for document in (querySnapshot?.documents)! {
if let Caption = document.data()["amount"] as? String {
print(Caption)
var post = Posts(caption: "")
post.caption = Caption
self.posts.append(post)
}
}
DispatchQueue.main.async
{
self.tableView.reloadData()
}
print(self.posts)
}
}
}
private func setupViews() {
let stackView: UIStackView = {
let sv = UIStackView()
sv.translatesAutoresizingMaskIntoConstraints = false
sv.spacing = 28
sv.axis = .vertical
sv.distribution = .fill
sv.alignment = .fill
return sv
}()
view.addSubview(stackView)
view.addSubview(tableView)
}
}
extension CoursesVC: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let post = posts[indexPath.row]
cell.textLabel?.text = post.caption
return cell
}
}

You forgot to assign frame to your tableView. As you are adding your tableView programatically so, you have to set frame for your tableView. Write below code in your setupViews() method.
E.g. tableView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 450.0)
Change tableView frame as per your requirement.

Related

What is this extra row in the UITabBar (Swift)

I programmatically add a TableView in a ViewController, then appeared an extra row. I would like to be able to manage it, but first I would have to know what it is. This line seems to come from the TabBar rather than the TableView. Do you know which object it is?
The code:
import UIKit
final class SearchViewController2: UIViewController, UITableViewDataSource, UITableViewDelegate {
private let ingredients = [
"flour",
"sugar",
"eggs",
"butter",
"baking powder",
"vanilla extract",
"milk",
"salt",
"cocoa powder",
"yeast",
"olive oil",
"cheese",
"breadcrumbs",
"onions",
"garlic",
"tomatoes",
"basil",
"oregano",
"thyme",
"parsley",
"pepper"
]
private let tableView: UITableView = {
let tableView = UITableView()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
return tableView
}()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let ingredient = ingredients[indexPath.row]
var cellConfig = cell.defaultContentConfiguration()
cellConfig.text = ingredient
cell.contentConfiguration = cellConfig
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
ingredients.count
}
override func viewDidLoad() {
super.viewDidLoad()
let header = StretchyTableHeaderView(
frame: CGRect(
x: 0,
y: 0,
width: view.frame.size.width,
height: view.frame.size.width / 2
)
)
view.addSubview(tableView)
header.imageView.image = UIImage(named: "header")
tableView.dataSource = self
tableView.delegate = self
tableView.frame = view.bounds
tableView.tableHeaderView = header
}
}
extension SearchViewController2: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard let header = tableView.tableHeaderView as? StretchyTableHeaderView else { return }
header.scrollViewDidScroll(scrollView: tableView)
}
}
I tried to add/remove a footer in the TableView... No changes.

Unable to simultaneously satisfy constraints. Adaptive cell height using UICollectionViewFlowLayout

I'm trying to make a news feed like app using UICollectionView with adaptive cell height. I found this tutorial and used the option "2. Solution for iOS 11+" for my code, which works perfectly fine. However, whenever I try to add more subviews to the cell and layout them as required, I get this error: "Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want."
Below is my code which gives me an error.
NewsFeedViewController:
import UIKit
class NewsFeedViewController: UIViewController {
var friends: [Friend] = []
var allPosts: [Post?] = []
var shuffledPosts: [Post?] = []
var collectionView: UICollectionView = {
let layout = NewsFeedFlowLayout()
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize // new
layout.scrollDirection = .vertical
let collection = UICollectionView(frame: .zero, collectionViewLayout: layout)
collection.backgroundColor = UIColor.systemRed
collection.isScrollEnabled = true
collection.contentInsetAdjustmentBehavior = .always
collection.translatesAutoresizingMaskIntoConstraints = false
return collection
}()
let cellID = "NewsFeedCollectionViewCell"
override func viewDidLoad() {
super.viewDidLoad()
getPosts()
view.addSubview(collectionView)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(NewsFeedCollectionViewCell.self, forCellWithReuseIdentifier: cellID)
collectionView.pin(to: view)
}
func getPosts() {
friends = FriendFactory().friends
allPosts = friends.flatMap{$0.posts}
var tempPosts = allPosts
for _ in allPosts {
if let index = tempPosts.indices.randomElement() {
let post = tempPosts[index]
shuffledPosts.append(post)
tempPosts.remove(at: index)
}
}
}
}
extension NewsFeedViewController: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return shuffledPosts.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! NewsFeedCollectionViewCell
let post = shuffledPosts[indexPath.row]
let friend = friends.first(where: {$0.identifier == post?.authorID})
let text = post?.postText
cell.configurePostText(postText: text!)
return cell
}
}
NewsFeedCollectionViewCell:
import UIKit
class NewsFeedCollectionViewCell: UICollectionViewCell {
var selectedPost = Post()
var likeBarView: LikeBarView = {
let view = LikeBarView()
view.backgroundColor = .systemIndigo
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
var postTextLabel: UILabel = {
let label = UILabel()
label.font = label.font.withSize(20)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.backgroundColor = .systemYellow
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addViews()
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func addViews() {
addSubview(likeBarView)
addSubview(postTextLabel)
}
func configurePostText(postText: String) {
postTextLabel.text = postText
}
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
let layoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes)
layoutIfNeeded()
layoutAttributes.frame.size = systemLayoutSizeFitting(UIView.layoutFittingCompressedSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
return layoutAttributes
}
func setupConstraints() {
NSLayoutConstraint.activate([
likeBarView.topAnchor.constraint(equalTo: topAnchor, constant: 20),
likeBarView.leadingAnchor.constraint(equalTo: leadingAnchor),
likeBarView.trailingAnchor.constraint(equalTo: trailingAnchor),
likeBarView.heightAnchor.constraint(equalToConstant: 100),
postTextLabel.topAnchor.constraint(equalTo: likeBarView.bottomAnchor, constant: 20),
postTextLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
postTextLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
postTextLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20),
])
}
}
NewsFeedFlowLayout:
import UIKit
final class NewsFeedFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let layoutAttributesObjects = super.layoutAttributesForElements(in: rect)?.map{ $0.copy() } as? [UICollectionViewLayoutAttributes]
layoutAttributesObjects?.forEach({ layoutAttributes in
if layoutAttributes.representedElementCategory == .cell {
if let newFrame = layoutAttributesForItem(at: layoutAttributes.indexPath)?.frame {
layoutAttributes.frame = newFrame
}
}
})
return layoutAttributesObjects
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
guard let collectionView = collectionView else {
fatalError()
}
guard let layoutAttributes = super.layoutAttributesForItem(at: indexPath)?.copy() as? UICollectionViewLayoutAttributes else {
return nil
}
layoutAttributes.frame.origin.x = sectionInset.left
layoutAttributes.frame.size.width = collectionView.safeAreaLayoutGuide.layoutFrame.width - sectionInset.left - sectionInset.right
return layoutAttributes
}
}
I enclose the screenshot of the collection view I'm getting and the screenshot of the analysis of the error done here, which is saying that the height of the cell is not dynamic if I'm getting it right.
What am I doing wrong?
Adding this line of code to NewsFeedViewController silenced the error:
layout.estimatedItemSize = CGSize(width: 375, height: 200)

Custom UISearchBar using TextField

I need to make custom UISearchBar using UITextField.
I try to make it myself, but nothing works. Please, write a code for UITextField, which will work like UISearchBar
It's code where I use UISearchBar, but I need to change to UITextField
class FoodViewController: UIViewController {
override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent }
let tableView = UITableView()
var foods = [FoodModel]()
var searchedFoods = [FoodModel]()
var searching = false
override func viewDidLoad() {
super.viewDidLoad()
fetchFoods()
self.modalPresentationCapturesStatusBarAppearance = true
self.view.backgroundColor = UIColor.white
let controller = UIViewController()
self.tableView.delegate = self
self.tableView.dataSource = self
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
self.view.addSubview(self.tableView)
tableView.delegate = self
tableView.dataSource = self
let searchBar = UISearchBar(frame: CGRect(x: 0, y: 5, width: 350, height: 40))
searchBar.searchBarStyle = .minimal
searchBar.barStyle = .black
searchBar.delegate = self
self.view.addSubview(searchBar)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.updateLayout(with: self.view.frame.size)
}
func updateLayout(with size: CGSize) {
self.tableView.frame = CGRect.init(x: 0, y: 45, width: size.width, height: 400)
}
func fetchFoods() {
Database.database().reference().child("food").observe(.childAdded) { (snapshot) in
if let dict = snapshot.value as? [String: AnyObject] {
let newTitle = dict["title"] as! String
let exCell = FoodModel(title: newTitle)
self.foods.append(exCell)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
}
extension FoodViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
if searching {
let food = searchedFoods[indexPath.item]
cell.textLabel?.text = food.title
} else {
let food = foods[indexPath.item]
cell.textLabel?.text = food.title
}
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return searchedFoods.count
} else {
return foods.count
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var titleOfFood = String()
if searching == true {
titleOfFood = searchedFoods[indexPath.row].title
print(titleOfFood)
} else {
titleOfFood = foods[indexPath.row].title
print(titleOfFood)
}
let alertController = UIAlertController(title: "Hello", message: "Message", preferredStyle: .alert)
let cancel = UIAlertAction(title: "Cancel", style: .default)
let save = UIAlertAction(title: "Save", style: .cancel) { (action) in
self.dismiss(animated: true, completion: nil)
}
alertController.addTextField { (textField) in
textField.keyboardType = .numberPad
textField.borderStyle = .roundedRect
textField.layer.borderColor = UIColor.clear.cgColor
textField.addConstraint(textField.heightAnchor.constraint(equalToConstant: 50))
textField.font = UIFont(name: "Roboto-Medium", size: 30)
// textField.cornerRadius = 8
}
alertController.addAction(save)
alertController.addAction(cancel)
self.present(alertController, animated: true)
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
}
}
extension FoodViewController: UITableViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView == self.tableView {
SPStorkController.scrollViewDidScroll(scrollView)
}
}
}
extension FoodViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchedFoods = foods.filter({ $0.title.lowercased().prefix(searchText.count) == searchText.lowercased() })
searching = true
tableView.isHidden = false
tableView.reloadData()
let transitionDelegate = SPStorkTransitioningDelegate()
transitionDelegate.customHeight = 620
transitionDelegate.showIndicator = false
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searching = false
searchBar.text = ""
tableView.reloadData()
}
}
Basically (if i'm understanding you correctly) after you add your UITextField to the view, all you need to have is some method to trigger whenever the value of the UITextField changes.
Something like this:
textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
And then:
func textFieldDidChange(_ textField: UITextField) {
let searchText = textField.text!
searchedFoods = foods.filter({ $0.title.lowercased().prefix(searchText.count) == searchText.lowercased() })
searching = true
tableView.isHidden = false
tableView.reloadData()
let transitionDelegate = SPStorkTransitioningDelegate()
transitionDelegate.customHeight = 620
transitionDelegate.showIndicator = false
}

Filter data from TableView

How can I filter a tableView by search bar? I fetch data to the tableView from a Firebase database and my search bar is inside navigationBar.
This is my code:
class TableViewCustomer: UIViewController, UITableViewDelegate, UITableViewDataSource {
var ref:DatabaseReference!
var customerList = [CustomerModel]()
lazy var searchBar: UISearchBar = UISearchBar(frame: CGRect(x: 0, y: 0, width: 300, height: 20))
#IBOutlet weak var tableViewCustomer: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
searchBar.placeholder = "Search"
navigationItem.titleView = searchBar
if let textfield = searchBar.value(forKey: "searchField") as? UITextField {
textfield.textColor = UIColor.gray
if let backgroundview = textfield.subviews.first {
backgroundview.backgroundColor = UIColor.white
backgroundview.layer.cornerRadius = 18
backgroundview.clipsToBounds = true
}
}
tableViewCustomer.delegate = self
tableViewCustomer.dataSource = self
ref = Database.database().reference().child("Customer");
ref.observe(DataEventType.value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
self.customerList.removeAll()
for results in snapshot.children.allObjects as! [DataSnapshot] {
let results = results.value as? [String: AnyObject]
let name = results?["Name and surname"]
let phone = results?["Phone"]
let company = results?["Company name"]
let myCustomer = CustomerModel(name: name as? String, phone: phone as? String, company: company as? String)
self.customerList.append(myCustomer)
}
self.tableViewCustomer.reloadData()
}
})
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
#objc func buttonAction(sender: UIButton!) {
dismiss(animated: true, completion: nil)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return customerList.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewCustomer.dequeueReusableCell(withIdentifier: "cellCustomer") as! TableViewCellCustomer
cell.layer.backgroundColor = UIColor.init(white: 1, alpha: 0.7).cgColor
let customer: CustomerModel
customer = customerList[indexPath.row]
cell.nameLabel.text = customer.name
cell.phoneLabel.text = customer.phone
cell.companyLabel.text = customer.company
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "description", sender: self)
}
}
Add your search bar, and an empty array. As you search add the suitable items to the empty array, then remove them from the array. Use the new array to index into your UI.

TableView Swift with dynamic and fixed cells

I'm making an app that displays data from Firebase , I have a TV that has to show one fixed cell (to choose the type of data to display) and the other cell to display data from server this is the code:
import UIKit
import Firebase
import SwiftDate
class EventiTVC: UITableViewController {
let userID = FIRAuth.auth()?.currentUser?.uid
let ref = FIRDatabase.database().reference()
let utente = UserDefaults.standard
#IBOutlet weak var Menu_button: UIBarButtonItem!
var Eventi:[String] = []
override func viewDidLoad() {
super.viewDidLoad()
if self.revealViewController() != nil {
Menu_button.target = self.revealViewController()
Menu_button.action = #selector(SWRevealViewController.revealToggle(_:))
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
let imageView = UIImageView(image: #imageLiteral(resourceName: "Hangover_Background"))
imageView.contentMode = .scaleAspectFill
self.tableView.backgroundView = imageView
self.tableView.tableFooterView = UIView()
self.navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white,NSFontAttributeName: UIFont(name: "HelveticaNeue-Bold", size: 19)!]
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if self.revealViewController() != nil {
Menu_button.target = self.revealViewController()
Menu_button.action = #selector(SWRevealViewController.revealToggle(_:))
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
self.navigationController?.navigationBar.barTintColor = UIColor.orange
self.navigationController?.navigationBar.tintColor = UIColor.white
self.navigationController?.navigationBar.titleTextAttributes = [ NSFontAttributeName: UIFont(name: "HelveticaNeue-Bold", size: 19)!,NSForegroundColorAttributeName : UIColor.white]
DeterminaInfoProprietario()
DeterminoLocali()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 2
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if section == 0{
return 1
}
else {
return Eventi.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0{
let cell = Bundle.main.loadNibNamed("Event_Type", owner: self, options: nil)?.first as! Event_Type
return cell
}
else {
let cell = Bundle.main.loadNibNamed("Evento", owner: self, options: nil)?.first as! Evento
let EventoCorrente = Eventi[indexPath.row]
// scarico info Evento corrente ref.child("Eventi").child(EventoCorrente).observeSingleEvent(of: .value, with: { (snap) in
if snap.childrenCount != 0 {
let DatiEvento = snap.value as? NSDictionary
cell.Nome.text = DatiEvento?.value(forKey: "Nome") as! String!
cell.Locale.text = DatiEvento?.value(forKey: "Locale") as! String!
let urlcopertina = DatiEvento?.value(forKey: "Immagine Copertina") as! String!
cell.Copertina.aail_load(url: NSURL(string: urlcopertina!)!)
// per scaricare il colore , devo scaricare il locale
let locale = DatiEvento?.value(forKey: "Locale") as! String!
var Colore:UIColor? = nil
var Ombra:UIColor? = nil
self.ref.child("Locali").child(locale!).observeSingleEvent(of: .value, with: { (snap2) in
if snap2.childrenCount != 0 {
let DatiLocale = snap2.value as? NSDictionary
Colore = ColoriDaStringa(Colore: (DatiLocale?.value(forKey: "Colore Pagina"))! as! String)[0]
Ombra = ColoriDaStringa(Colore: (DatiLocale?.value(forKey: "Colore Pagina"))! as! String)[1]
cell.ViewC.backgroundColor = Colore!
cell.View_ombra.backgroundColor = Ombra!
}
})
}
else {
// Evento Cancellato? // gestire situazione
}
})
return cell
}
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat{
if indexPath.row == 0{
return 44
}else {
return 190
}
}
#IBAction func ritornaHome (Segue:UIStoryboardSegue){
}
}
I got 2 static cell and 1 dynamic cell, it should be the other way around, any idea why?
In your cellForRowAtIndexPath() method you should change your if statement from:
if (indexPath.row == 0)
to
if (indexPath.section == 0)
Modify these two functions to:
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Eventi.count+1
}
As we should go with latest features. You should go with Auto layout. Auto layout has in built feature for showing dynamic height of UITableView Cell. Refer below link if you want to learn.
Dynamic cell height UITableview
Otherwise here we have answer of issue in your code.
In numberOfSections() you have set wrong condition. Just replace
return 2
to
return 1
And
in numberOfRowsInSection() replace
if section == 0{
return 1
}
else {
return Eventi.count
}
to
return Eventi.count+1