A bit of context:
I'm filling a tableView with movie titles
my table
When one of those rows gets selected I want to go to the movie detail
but when I tap any of the rows nothing happens.
class SearchTableViewController: NavigationController{
#IBOutlet weak var tableView: UITableView!
var filteredMovies = [Movie]()
let request = Requests()
override func viewDidLoad() {
super.viewDidLoad()
}
/*
searches movies from the db which title corresponds to the given text
*/
func searchMovie(_ keywords: String) {
request.searchMovie(keywords: keywords){ response in
for data in response{
self.filteredMovies.append(data)
}
DispatchQueue.main.sync {
self.tableView?.reloadData()
}
}
}
}
extension SearchTableViewController : UITableViewDelegate { }
extension SearchTableViewController: UISearchBarDelegate {
/*
every time a key gets pressed, the table view gets updateted
with new movie titles
*/
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
self.filteredMovies = [Movie]()
searchText.isEmpty ? self.tableView?.reloadData() : searchMovie(searchText)
}
}
extension SearchTableViewController : UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredMovies.count
}
func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) {
print("hello from highlight")
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("hello from selection")
}
func tableView(_ tableView: UITableView, didFocusRowAt indexPath: IndexPath) {
print("hello from focus")
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SearchTableCell
cell.onDidSelectMovie = {
super.goToMovieDetail(movie: self.filteredMovies[indexPath.row])
}
let label = cell.contentView.subviews.first as! UILabel
label.text = filteredMovies[indexPath.row].title
return cell
}
}
As you can see i tested every function that may show that a row got tapped but none of them works.
I also tried to give to the cells a custom class where I override the behavior of the cell property "isSelected".
class SearchTableCell: UITableViewCell {
var onDidSelectMovie:(()->Void)?
override var isSelected: Bool{
didSet {
if isSelected{
if let cb = onDidSelectMovie{
cb()
}
}
}
}
}
Another thing that I have want to point out, which may help, is that "selection" on a cell is enabled like "user interactions" but if I try to change selection from "default" to "blue" the color doesn't change.
I literally ran out of ideas and tried many possible solutions but none of them worked. Any suggestion?
Edit:
I'm going to add everything that can be useful
the tableView delegate is SearchTableViewController
It looks like I had just to remove and add again the tableView delegate... don't ask me why, I've spent 2 days on this. Thanks anyway to everyone who tried to help.
Related
sorry to bother programmers again,
I'm writing a simple app, and one of the view controllers has a table.
I'm new to all of this so I found some videos and programmed the table. This is the code above classes (honestly I still don't understand what it does):
extension ViewController3: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("you tapped me!")
}
}
extension ViewController3: UITableViewDataSource
{
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = names[indexPath.row]
return cell
}
}
And this is the class:
class ViewController3: UIViewController {
#IBOutlet var tView: UITableView!
let names = [
"Alex",
"Andrew",
"Mary",
"Henry"]
override func viewDidLoad() {
super.viewDidLoad()
tView.delegate = self
tView.dataSource = self
}
}
It works fine but the task is when the orientation of the phone/simulator is horizontal, to SHOW the table not in one row, but 2x2 (like 4 cells - 2 rows, 2 lines). I don't know where to search it, I found some tips about placing elements in this way but it's not for a table. And I don't know whether its possible or not. Thank you all in advance
I'm new to Swift and i've been working on an app for a couple of day now. Now I'm trying to fill up my tableview, but for some reason it stays empty. I've checked if my data comes through (by doing a print of my drinks in the updateUi function), and it seems so be there but my view just doesn't get filled. Anyone has a clue what could be wrong?
Thanks a lot!
import UIKit
class SearchResultsTableViewController: UITableViewController {
let cocktailController = CocktailController()
var drinks = [Drink]()
var drink: String!
override func viewDidLoad() {
super.viewDidLoad()
title = drink.capitalized
cocktailController.fetchDrinks(forDrink: drink)
{ (drinks) in
if let drinks = drinks {
self.updateUI(with: drinks)
}
}
}
func updateUI(with drinks: [Drink]) {
DispatchQueue.main.async {
self.drinks = drinks
self.tableView.reloadData()
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return drinks.count
}
override func tableView(_ tableView: UITableView, cellForRowAt
indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:
"DrinkCellIdentifier", for: indexPath)
configure(cell, forItemAt: indexPath)
return cell
}
func configure(_ cell: UITableViewCell, forItemAt indexPath:
IndexPath) {
let drinkItem = drinks[indexPath.row]
cell.textLabel?.text = drinkItem.strDrink
}
}
Instead of number of sections, you should use number of rows in section to return the tableview row count.
func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return drinks.count
}
The problem is fairly weird, I try to fetch some data and then I reload data after getting them. I type in searchbar the first letter and it doesn't reload the data in UITableview but when the searchText gets edited the next time the UITableView gets reloaded from itself immediately.
I looked at many solutions with no gain.
Both delegates are set in Storyboard.
Here is my code:
extension SearchVC: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
reload(searchText) { (users) in
self.queriedUsers = users
DispatchQueue.main.async {
print("Table view reloading \(self.queriedUsers.count)")
self.tableview.reloadData()
}
}
}
}
extension SearchVC: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableview.dequeueReusableCell(withIdentifier: queriedCellID, for: indexPath) as! QueriedUserViewCell
cell.nicknameLabel.text = queriedUsers[indexPath.row].getNickname()
cell.usernameLabel.text = queriedUsers[indexPath.row].getName()
cell.userProfilePicture.image = UIImage(named: "defaultpfp")
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return queriedUsers.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 70.0
}
}
i have a tableview in a viewcontroller and because i need to reuse most of the code for another table i created an extra class:
class StatisticsViewDelegate: NSObject, UITableViewDelegate, UITableViewDataSource {
var defaultList:[String]
var infolist:[String] = []
var tableView:UITableView
var controller:UIViewController?
init(defaultList:[String], view:UITableView, controller:UIViewController?) {
self.defaultList = defaultList
self.controller = controller
tableView = view
super.init()
tableView.delegate = self
tableView.dataSource = self
loadTable()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return infolist.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "infocell", for: indexPath) as! TableViewCell
// [fill cell]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// [...]
}
func loadTable() {
DispatchQueue.global(qos: .userInitiated).async {
//[...]
// in this case:
self.infolist = self.defaultList
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
and in my UITViewController in the viewDidLoad():
delegate = StatisticsViewDelegate(defaultList: defaultList, view: tableView, controller:self)
delegate is a member of the ViewController
now when i run it, the function func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) never gets called. The func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) gets called however(before and after the reload) and returns the correct number(in my case 4). The TableView isn't visible at all. Where is my error?
Maybe you can use the subclassing strategy to resolve your problem. There are many reference passed to your class and if you forgot to clean that up you will be have memory leaks in your hand. So I'll suggest the simple example as below. You can modify as you like and let me know if that was what you are after. If not please pardon me.
//This will be parent class that will handle all table methods, so you need to write only once the delegates and stuffs
class MyCommonTableController: UITableViewController {
var infoList = [String]()
// MARK: - TableView Delegate and Datsource Impl
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return infoList.count
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 55.0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = infoList[indexPath.row]
return cell
}
}
The first class that is directly subclassing the from above MyCommonTableController
//In here we just have to know the data and set the infoList from parent
class TheTableViewController: MyCommonTableController {
let defaultList = ["Data1","Data2","Data3"] //....etc
override func viewDidLoad() {
super.viewDidLoad()
//this is were I will set those
infoList = defaultList
//reload the table
tableView.reloadData()
}
}
The second class that is directly subclassing the from above MyCommonTableController. Same process goes here
class TheSecondTableViewController: MyCommonTableController {
let defaultList = ["List1","List2","List3"] //....etc
override func viewDidLoad() {
super.viewDidLoad()
//this is were I will set those
infoList = defaultList
//reload the table
tableView.reloadData()
}
}
And now you are not repeating and table methods. You can also get the reference of table and use in your norma table view
#IBOutlet weak var theTable: UITableView!
let defaultList = ["List1","List2","List3"] //....etc
let commonTable = MyCommonTableController()
// MARK: - LifeCycle
override func viewDidLoad() {
super.viewDidLoad()
commonTable.infoList = defaultList
commonTable.tableView = theTable
}
What I want to do is make a List with items from a Results. What I have is a UITableView which was populated from Results. From this table I want to create a List when the user selects rows, but for some reason nothing gets appended to the List when I append items from the Results to the List when the rows are selected.
Here is the code I have:
Realm Object:
import Foundation
import RealmSwift
class Fruit:Object{
dynamic var fruitName:String = ""
dynamic var createdAt = NSDate()
}
ViewController:
var fruits: Results<Fruit>!
var fruitSelectionList: List<Fruit>!
override func viewDidLoad() {
super.viewDidLoad()
updateFruits()
tableFruits.setEditing(true, animated: true)
tableFruits.allowsMultipleSelectionDuringEditing = true
}
func updateFruits(){
fruits = realm.objects(Fruit.self).sorted(byKeyPath: "fruitName")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fruits.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let index = fruitSelectionList?.index(of: fruits[indexPath.row]) {
try! realm.write {
fruitSelectionList?.remove(at: index)
}
} else {
try! realm.write {
// nothing gets appended here, why?
fruitSelectionList?.append(fruits[indexPath.row])
}
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let index = fruitSelectionList?.index(of: fruits[indexPath.row]) {
fruitSelectionList?.remove(at: index)
}
}
The funny thing is that similar code without using Realm works just fine...
Plain Swift code that works when not using Realm:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var fruits = ["Apples", "Oranges", "Grapes", "Watermelon", "Peaches"]
var newFruitList:[String] = []
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.allowsSelection = false
tableView.setEditing(true, animated: true)
tableView.allowsMultipleSelectionDuringEditing = true
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fruits.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = fruits[indexPath.row]
return cell
}
#IBAction func makeSelection(_ sender: Any) {
tableView.allowsMultipleSelectionDuringEditing = true
tableView.setEditing(true, animated: true)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = fruits[indexPath.row]
if let index = newFruitList.index(of: item) {
newFruitList.remove(at: index)
} else {
newFruitList.append(fruits[indexPath.row])
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let index = newFruitList.index(of: fruits[indexPath.row]) {
newFruitList.remove(at: index)
}
}
}
How can I make a List with some items from a Results?
FYI - The only reason I need to create the List is to be able to add and remove items on a table row selection otherwise I would stick with just Results.
Your code is not the same at all when not using Realm. The issue has nothing to do with Realm, it is about wrongly declaring the Lists.
You declare them as implicitly unwrapped optionals (which you shouldn't do in the first place), then try to append a nil list. You cannot append nil, you need an empty List in the first place. If you didn't use safe unwrapping of the optionals, you would get a runtime exception instead of a nil result.
Declare fruitSelectionList as an empty List and you won't have this issue.
var fruits: Results<Fruit>?
var fruitSelectionList = List<Fruit>()
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let index = fruitSelectionList?.index(of: fruits[indexPath.row]) {
fruitSelectionList.remove(at: index)
} else {
fruitSelectionList.append(fruits[indexPath.row])
}
}
Moreover, fruitSelectionList is not managed by Realm, since only Results are automatically updating, so you don't need to put your actions on fruitSelectionList inside write transactions. You also don't actually need fruitSelectionList to be of type List, it could be an Array as well.
Just a general piece of advice: if you need your selections to be persisted, I would advise you to update your Fruit model class to have a selected property of type Bool and update that property when a row of the tableView is selected.