Swift: How to access value of selected table view cells - ios

How can I access the value of the cell of the table view that the user selects? I know how to access the value if the user hasn't searched because I can just access the IndexPath's value of my array, but I can't when the user has searched something because the townArray won't line up with the cells that are shown.
To give you a better understanding- just say I have an array of fruits which has [apples, bananas, oranges]. If the user searches for bananas, then the only cell with text showing (results) will say bananas. If I then try to access the IndexPath element of fruits, I will get apples since it is the first element of fruits and bananas is the first element showing. What I want is to get the access the value bananas when the user selects bananas and searches "bananas" instead of apples. I know this may be confusing but please let me know if you have any thoughts on how I can solve this issue.

var searchedResult: [Fruit] = yourSearchingFunction()
tableview.reloadData()
IndexPaths would refresh correctly after TableView reloaded with a new collection of fruits
Explanation is showed with the following code:
class ViewController: UIViewController {
#IBOutlet private weak var tableView: UITableView!
private var originalFruits: [Fruit] = [] // This data is used to cache
private var fruits: [Fruit] = [] // This data is used to display on TableView
// MARK: - View Cycle
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
setupData()
}
private func setupData() {
// Loading all fruits at the first time [Apple, Banana, Orange ]
originalFruits = yourSetupDataFunctionToFetchAllFruitsAtTheFirstTime()
fruits = originalFruits
tableView.reloadData()
}
#IBAction private func actionTapToSearchButton(_ sender: Any) {
let searchingKey = searchTextField.text
if searchingKey.isEmpty {
fruits = originalFruits
} else {
fruits = yourSeacrchingFruitFunction(searchingKey)
}
tableView.reloadData()
}
}
// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {
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 {
if let cell = tableView.dequeueReusableCell(withIdentifier: FruitCell.identifier) as? FruitCell {
return cell
}
return UITableViewCell()
}
}
// MARK: - UITableViewDelegate
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}

Related

coreData only printing last entity (swift4)

I am trying to call all of my entries of coreData .color. The problem is only 1 entry of coreData is being called to the label. I would like all of the entities of coreData to be printed on the label not just the latest one, which the code is currently doing.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
#IBOutlet var label: UILabel!
var users = [User]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.dataSource = self
if CDHandler.fetchObject() != nil {
users = CDHandler.fetchObject()!
tableView.reloadData()
}
}
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil)
cell.textLabel?.text = users[indexPath.row].username
for c in users {
label.text = c.color
}
return cell
}
}
Actually your current loop sets the last item of the array to the label text , so Try this to append values together
for c in users {
label.text = "\((label.text)!)+\((c.color)!)"
}

ReloadData() Not Refreshing TableView

I have a text field that when I type a word in, and then press a button is supposed to add the word the the tableview. I know the array is being updated because after the button is pressed, the array, with its new value print fine in the console. I've tried reloadData() in several places but it's not doing anything. Here is my code:
import UIKit
class Arraytest: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var textField: UITextField?
var names = ["Jack", "Andy", "Guy", "Bruce", "Nick", "James", "Dominick"]
#IBAction func addButton(_ sender: Any) {
let name : String = textField!.text!
names.append(name)
textField?.text! = ""
for guy in names{
**print(guy)**
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "Cell")
cell.textLabel?.text = names[indexPath.row]
***tableView.reloadData()***
return cell
}
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "Cell")
cell.textLabel?.text = names[indexPath.row]
return cell
}
}
Let's say I type John in the text field, here is what the console prints:
Jack
Andy
Guy
Bruce
Nick
James
Dominick
John
I know the array works fine, but not why the tableView won't reload when everyone claims reloadData() works(I'm sure it does, and I'm just making an easy mistake!)..Any ideas?
EDIT: Ok it turns out that you do have to drag the IBOutlet from the tableview. The reason I didn't do this earlier was because I watched a video and his worked without making the connection.
You should learn more about table view & how it works. Please see Apple Documentation Creating and Configuring a Table View
Here the method cellForRowAt is a data source of table view and it's get fired from tableview automatically when it's need to populate cell data. You couldn't manually call this method. Implementing this method inside #IBAction func addButton() does nothing. Function always needs to call from another method. So you should remove cellForRowAt inside #IBAction func addButton().
Solution: Get an table view outlet from storyboard. If need help see here Connect the UI to Code
#IBOutlet weak var tableView: UITableView?
Then set tableview datasource and delegate in viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
tableView?.dataSource = self;
tableView?.delegate = self;
}
And finally update #IBAction func addButton() as below
#IBAction func addButton(_ sender: Any) {
let name : String = textField!.text!
names.append(name)
textField?.text! = ""
for guy in names{
print(guy)
}
tableView?.reloadData()
}
Full source may look like this:
import UIKit
class Arraytest: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var textField: UITextField?
#IBOutlet weak var tableView: UITableView?
var names = ["Jack", "Andy", "Guy", "Bruce", "Nick", "James", "Dominick"]
override func viewDidLoad() {
super.viewDidLoad()
tableView?.dataSource = self;
tableView?.delegate = self;
}
#IBAction func addButton(_ sender: Any) {
let name : String = textField!.text!
names.append(name)
textField?.text! = ""
for guy in names{
**print(guy)**
}
tableView?.reloadData()
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "Cell")
cell.textLabel?.text = names[indexPath.row]
return cell
}
}
Your code has two very big mistakes (assuming that your tableview delegates are connected to your viewcontroller correctly).
First your a missing an IBOulet reference to your tableview. Create one and you can call the reloadData() method in that outlet.
Second, you should not call the public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell delegate method inside your IBAction. It is a delegate method, it is already listening for an event or command that will fire its logic, in this case reloadData().
So your IBaction should only take the value from the textfield and add it to the array, to finally call reloadData(), which will call public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
this code works... hope it helps:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableview: UITableView!
#IBOutlet weak var textField: UITextField?
var names = ["Jack", "Andy", "Guy", "Bruce", "Nick", "James", "Dominick"]
// MARK: tableview delegate methods
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "Cell")
cell.textLabel?.text = names[indexPath.row]
return cell
}
// MARK: ibactions
#IBAction func addButton(_ sender: Any) {
let name : String = textField!.text!
names.append(name)
textField?.text! = ""
tableview.reloadData()
}
}
You have to remove TableView delegate method from
#IBAction func addButton(_ sender: Any) {
}
method & put it outside of it. Create a property of this tableView form storyboard like that
#IBOutlet weak var tableView: UITableView!
In viewDidload method set datasource & delegate of it like that
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.datasource = self
self.tableView.delegate = self
}
Hope it helps.
You can achieve this by adding a property observer to the array of names. The didset method will run if there is any changes to the value.
class Arraytest: UIViewController, UITableViewDelegate UITableViewDatasource {
var names = ["Jack", "Andy", "Guy", "Bruce", "Nick", "James", "Dominick"] {
didSet {
self.tableView.reloadData();
}
}
}
The tableview func inside the addButton func doesnt run.. remove it and keep it like this:
#IBAction func addButton(_ sender: Any) {
let name : String = textField!.text!
names.append(name)
textField?.text! = ""
for guy in names{
**print(guy)**
}
}
You have to take your tableview func outside the #IBAction func addButton, as it's part of the uitableviewdatasource and is required to display your cell. After that, all you need is to put tableView.reloadData() after the for guy in names{ ... } loop.
Drag a connection from a split view controller to your view controller and use reloadData().

UITableView not being populated

I have a method which grabs JSON and appends individual values from the JSON into the array like so:
internal let methods = Methods()
var countryData = [""]
#IBOutlet weak var countryTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
displayCountries()
}
func displayCountries() {
// Wait for task to complete before grabbing JSON
methods.getCountriesData {() -> () in
for (_, value) in self.methods.getJSON() {
self.countryData.append(String(describing: value))
}
DispatchQueue.main.async {
self.countryTableView.reloadData()
print(self.countryData)
}
}
}
I have UITableView delegates declared like so also:
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return countryData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")! as UITableViewCell
let row = indexPath.row
cell.textLabel?.text = self.countryData[row]
return cell
}
The print(self.countryData) prints all of the countries within the log (100's of countries) however for some reason the countries aren't being displayed in the UITableView, does anyone understand why?
You need to specify the table view's data source. This can be done in code or in the storyboard.
In code:
countryTableView.dataSource = self
You will also need to have your data source object conform to the UITableViewDataSource protocol:
MyViewController: UIViewController, UITableViewDataSource {
Since the data is an array of strings and a property on the view controller, this should work fine.
In the storyboard:
Simply drag the dataSource outlet to the object which is providing the data (in this example it is the view controller):

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 {}
}

Swift 3 - Custom cells not showing?

After a few hours of trying to figure out why my customs cells aren't showing, I have hit a bit of a bump. Using the MVC pattern:
Model.swift
struct studentProperties {
var _title : String!
var _forename : String!
var _surname : String!
}
class MainModel {
// Singleton instances
static let modelInstance = MainModel()
static var structInstance = studentProperties()
// Array of structs
var studentArray: [studentProperties] = []
// MARK: - Initialization
private init() {}
func studentInput(title: String, forename: String, surname: String) {
MainModel.structInstance._title = title
MainModel.structInstance._forename = forename
MainModel.structInstance._surname = surname
}
}
CreateStudentController.swift
I won't show the whole file, but this code is inside of a save button - user inputs data, and that data gets append to the array in model
#IBAction private func saveNewStudentButton(_ sender: UIBarButtonItem) {
// Put student data into the struct properties in model
MainModel.modelInstance.studentInput(title: studentTitle.text!, forename: studentForename.text!, surname: studentSurname.text!)
// Append the user input
MainModel.modelInstance.studentArray.append(MainModel.structInstance)
dismiss(animated: true, completion: nil)
performSegue(withIdentifier: "returnToStudentsList", sender: sender)
}
MainStudentController.swift
class MainStudentController: UIViewController, UITableViewDelegate, UITableViewDataSource {
//MARK: - #IBOutlets
#IBOutlet weak var myTableView: UITableView!
//MARK: - #IBActions
#IBAction func addStudentButton(_ sender: UIBarButtonItem) {
dismiss(animated: true, completion: nil)
performSegue(withIdentifier: "createStudentSegue", sender: sender)
}
override func viewDidLoad() {
super.viewDidLoad()
myTableView.delegate = self
myTableView.dataSource = self
myTableView.reloadData()
}
// MARK: - Table view data source
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(MainModel.modelInstance.studentArray.count)
return MainModel.modelInstance.studentArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: MainCell = self.myTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MainCell
cell.studentTitle?.text = MainModel.modelInstance.studentArray[0]._title
cell.studentForename?.text = MainModel.modelInstance.studentArray[0]._forename
cell.studentSurname?.text = MainModel.modelInstance.studentArray[0]._surname
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
myTableView.deselectRow(at: indexPath, animated: true)
}
}
There is another file - which is of UITableViewCell, but it literally only has the outlets for my custom cell.
Issue
The issue I am currently experiencing is (I think) around this method?:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(MainModel.modelInstance.studentArray.count)
return MainModel.modelInstance.studentArray.count
}
User inputs data -> it gets append to that array -> thus now I am using those elements from the array to represent the amount of rows there will be.
Sadly, it currently isn't showing any rows? I could add multiple elements and it still wouldn't show any rows. Yet, when I print that same line out, it shows that there are elements inside(dependant on how many times I add new data). I'm a little confused as to why this isn't adding rows?
I'm not sure if it's something I am missing or isn't correct in code. I would appreciate if anyone could see where I have gone wrong?
Hopefully that explains my situation.
Thanks
According to this code, your problem is just that the table view itself is not getting reloaded. Your only reload call is in viewDidLoad, so it only gets loaded when your view loads the first time (that method only gets called once). You can fix this easily by moving myTableView.reloadData() from viewDidLoad into viewWillAppear or viewDidAppear. That should be all you need to do.
Something like this:
override func viewWillAppear() {
super.viewWillAppear()
myTableView.reloadData()
}

Resources