TableView containing two array of informations - ios

I have Employee.swift which contains the following code:
import Foundation
struct Employee {
var name: String
var favoriteLinks: [String]
var links: [String]
init(name: String, favoriteLinks: [String], links: [String]) {
self.name = name
self.favoriteLinks = favoriteLinks
self.links = links
}
}
And I have ViewController.swift that uses TableView with the following code:
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var lists: [Employee] = [People(name: "Employee 1",
favoriteLinks: ["Facebook","Twitter"],
links: ["www.facebook.com","www.twitter.com"])
]
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}
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 = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = lists[indexPath.row].name
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showLinks" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let destination = segue.destination as? SecondTableViewController
destination?.talks = lists[indexPath.row].talk
destination?.links = lists[indexPath.row].link
}
}
}
}
And another TableViewController that contains the following code:
import UIKit
class SecondTableViewController: UITableViewController {
var favoriteLinks: [String] = []
var links: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return favoriteLinks.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = favoriteLinks[indexPath.row]
return cell
}
}
I created a property that contain name of an employee with a lists of his favoriteLinks and links. ViewController contain a tableview that should only contain the name of the employee, if clicked on the employee, you will be redirected to another tableview with a lists of his favoriteLists.
This is the problem. Because the tableview is only showing text and not the link. I would like the text to contain the link as well, in which if clicked upon would direct you to the link connected. For example, if clicked on Facebook, it will show me to www.facebook.com. What would be the best way to go around to achieve this?
I tried creating two separate arrays to contain the information but I have no idea how to call the array that contains the link. Any help would be appreciated. Thank you!

You have to add didSelectRowAt in SecondTableViewController like below:
if your lists is like below :
var lists: [Employee] = [Employee(name: "Employee 1",
favoriteLinks: ["Facebook","Twitter"],
links: ["http://www.facebook.com","http://www.twitter.com"])
]
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
UIApplication.shared.open(URL(string: links[indexPath.row])!, options: [:])
}
if your lists is like below :
var lists: [Employee] = [Employee(name: "Employee 1",
favoriteLinks: ["Facebook","Twitter"],
links: ["www.facebook.com","www.twitter.com"])
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var urlStr: String = links[indexPath.row]
if !urlStr.hasPrefix("http") && !urlStr.hasPrefix("https") {
urlStr = "http://\(urlStr)"
}
UIApplication.shared.open(URL(string: urlStr)!, options: [:])
}

Better yet, pass the whole Employee record to your second view controller:
class ViewController: UITableViewController {
// Add the https protocol for the link
var lists: [Employee] = [
Employee(name: "Employee 1",
favoriteLinks: ["Facebook","Twitter"],
links: ["https://www.facebook.com","https://www.twitter.com"])
]
...
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showLinks" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let destination = segue.destination as! SecondTableViewController
// Pass the whole employee record to the second view controller
destination.employee = lists[indexPath.row]
}
}
}
}
And the second view controller:
class SecondTableViewController: UITableViewController {
var employee: Employee!
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return employee.favoriteLinks.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Select the prototype cell in IB and change its style to Subtitle if you want a 2-line label
// Alternatively you can hide the link altogether, it doesn't affect the outcome
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
// For the first line, we display the name of the link
cell.textLabel?.text = employee.favoriteLinks[indexPath.row]
// For the second line, we display the address of the link
cell.detailTextLabel?.text = employee.links[indexPath.row]
cell.detailTextLabel?.textColor = .blue
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let href = employee.links[indexPath.row]
let url = URL(string: href)!
// Open the URL in whatever app that registered to open it. That could be
// the Facebook app or the Twitter app instead of Safari. After it has been
// opened, de-highlight the cell
UIApplication.shared.open(url, options: [:], completionHandler: { _ in tableView.selectRow(at: nil, animated: false, scrollPosition: .none) })
}
}

Related

How to retrieve the array from UITableViewController to ViewTable

I'v tried to get array from UITableViewController to another ViewController. I have navigationController and main screen is MyTableViewController. I'm trying to pass data with Segue. Here is the part of MyTableViewController. All other code in this file are correct.
MyTableViewController
struct Faculty {
var name = String()
var descriptions = [String]()
}
let faculties = [
Faculty(name: "Faculty of Architecture", descriptions: ["Architecture and Urban Planning", "Spatial Planning"]),
Faculty(name: "Faculty of Civil Engineering", descriptions: ["Civil Engineering"]),
Faculty(name: "Faculty of Chemistry", descriptions: ["Biotechnology", "Chemistry","Chemical and Process Engineering", "Materials Science and Engineering", "Chemical Technology"]),
Faculty(name: "Faculty of Electronics", descriptions: ["Control Engineering and Robotics", "Electronics", "Telecommunications", "Teleinformatics", "Technical Computer Science"])]
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
tableView.deselectRow(at: indexPath, animated: true)
performSegue(withIdentifier: "showDetail", sender: cell)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail",
let indexPath = self.tableView.indexPathForSelectedRow {
let sendDescriptions = faculties[indexPath.row].descriptions
let destination = segue.destination as? ViewController
destination?.getDescriptions = sendDescriptions
}
}
ViewController
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView: UITableView!
var getDescriptions = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print(getDescriptions)
tableView.delegate = self
tableView.dataSource = self
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return getDescriptions.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "detailIdentifier", for: indexPath)
let row = indexPath.row
cell.textLabel?.text = getDescriptions[row]
return cell
}

How to change tableView numberOfRows and Cells based on which button is clicked in Swift iOS

I have two buttons in my user's profile page, one for the saved shop items and one for his reviews.
I want when the user clicks the saved button it would load his saved shop's items in the table view and when he clicks the reviews button it would load his reviews.
I'm struggling on how to figure out how to do this
Any help, please?
here is my code:
#IBOutlet weak var reviewsBtn: UIButton!
#IBOutlet weak var saveBtntab: UIButton!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(reviewsBtn.isSelected == true){
print("review selected")
return reviews.count
}
if(saveBtntab.isSelected == true){
print("saved selected")
return shops.count
}
return shops.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellFave", for: indexPath) as! FaveTableViewCell
let shops = self.shops[indexPath.row]
let reviews = self.reviews[indexPath.row]
// i want to do the same idea for the number of rows here.
}
#IBAction func reviewsTapped(_ sender: Any) {
reviewsBtn.isSelected = true
reviewsBtn.isEnabled = true
faveBtntab.isEnabled = false
faveBtntab.isSelected = false
}
#IBAction func savedTapped(_ sender: Any) {
faveBtntab.isSelected = true
faveBtntab.isEnabled = true
reviewsBtn.isEnabled = false
reviewsBtn.isSelected = false
}
First of all if there are only two states you can simplify numberOfRows
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return reviewsBtn.isSelected ? reviews.count : shops.count
}
In cellForRow do the same thing, display the items depending on reviewsBtn.isSelected
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellFave", for: indexPath) as! FaveTableViewCell
if reviewsBtn.isSelected {
let reviews = self.reviews[indexPath.row]
// assign review values to the UI
} else {
let shops = self.shops[indexPath.row]
// assign shop values to the UI
}
}
And don't forget to call reloadData when the state has changed.
You can create two different dataSource instances for clarity and separation like following -
class ShopsDataSource: NSObject, UITableViewDataSource, UITableViewDelegate {
var shops: [Shop] = []
var onShopSelected: ((_ shop: Shop) -> Void)?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return shops.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ShopTableViewCell", for: indexPath) as! ShopTableViewCell
let shop = self.shops[indexPath.row]
cell.populateDetails(shop: shop)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.onShopSelected?(shops[indexPath.row])
}
}
class ReviewsDataSource: NSObject, UITableViewDataSource, UITableViewDelegate {
var reviews: [Review] = []
var onReviewSelected: ((_ review: Review) -> Void)?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return reviews.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ReviewTableViewCell", for: indexPath) as! ReviewTableViewCell
let review = self.reviews[indexPath.row]
cell.populateDetails(review: review)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.onReviewSelected?(reviews[indexPath.row])
}
}
class ViewController: UIViewController {
let shopsDataSource = ShopsDataSource()
let reviewsDataSource = ReviewsDataSource()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(ShopTableViewCell.self, forCellReuseIdentifier: "ShopTableViewCell")
tableView.register(ReviewTableViewCell.self, forCellReuseIdentifier: "ReviewTableViewCell")
shopsDataSource.onShopSelected = { [weak self] (shop) in
self?.showDetailsScreen(shop: shop)
}
reviewsDataSource.onReviewSelected = { [weak self] (review) in
self?.showDetailsScreen(review: review)
}
}
#IBAction func shopsTapped(_ sender: Any) {
tableView.dataSource = shopsDataSource
tableView.delegate = shopsDataSource
tableView.reloadData()
}
#IBAction func addNewShop(_ sender: Any) {
/// ask user about shop details and add them here
shopsDataSource.shops.append(Shop())
tableView.reloadData()
}
func showDetailsScreen(shop: Shop) {
/// Go to shop details screen
}
#IBAction func reviewsTapped(_ sender: Any) {
tableView.dataSource = reviewsDataSource
tableView.delegate = reviewsDataSource
tableView.reloadData()
}
#IBAction func addNewReview(_ sender: Any) {
/// ask user about review details and add them here
reviewsDataSource.reviews.append(Review())
tableView.reloadData()
}
func showDetailsScreen(review: Review) {
/// Go to review details screen
}
}

Issues displaying a table view cell from one view controller to another

Right now I am trying to move information from my goal cell into a new table view cell, and am having difficulty getting the cell to display.
Here is the code for my goal cell.
import UIKit
class GoalsViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var Goals: [String] = ["goal 1", "goal 2", "goal 3"]
let theEmptyModel: [String] = ["No data in this section."]
var valueToPass = ""
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func showGoalSelected() {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) {
let popUp = GoalSelectedPopUp()
self.view.addSubview(popUp)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "GoalConversationsCell_1") {
let viewController = segue.destination as! ActiveGoalsViewController
viewController.Goals.append([valueToPass])
}
}
}
extension GoalsViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Goals.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "GoalCell_1", for: indexPath)
cell.textLabel?.text = Goals[indexPath.row]
cell.textLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping
cell.textLabel?.numberOfLines = 3
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
valueToPass = Goals[indexPath.row]
performSegue(withIdentifier: "activeGoalsSegue", sender: self)
Goals.remove(at: indexPath.row)
if Goals.count != 0 {
showGoalSelected()
} else {
Goals.append(contentsOf: theEmptyModel)
}
tableView.reloadData()
}
}
Here is the goal cells storyboard with the push segue connecting it to the other table view.
That other table view is shown below.
Here is the code for this new tableview.
import UIKit
class ActiveGoalsViewController: UIViewController {
#IBOutlet weak var goalTableView: UITableView!
let sections: [String] = ["Mark as Complete:", "History:"]
var goals: [[String]] = [[], []]
let theEmptyModel: [String] = ["No data in this section."]
extension ActiveGoalsViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Goals[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TodayGoalViewCell_1", for: indexPath) as? GoalTableViewCell
cell?.goalLabel.text = Goals[indexPath.section][indexPath.row]
cell?.cellDelegate = self
cell?.index = indexPath
return cell!
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
return Goals.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
if Goals[0] != theEmptyModel {
Goals[1].append(Goals[0][indexPath.row])
if Goals[1].first!.contains("No data in this section.") {
Goals[1].removeFirst()
}
Goals[0].remove(at: indexPath.row)
if Goals[0].count == 0 {
Goals[0].append(contentsOf: theEmptyModel)
}
tableView.reloadData()
}
}
}
Once the goal is selected, it sends me to the new storyboard, but this new view does not display the goal that was just added. Can someone help me figure out why this isn't working? Thanks.
I think in the second view controller you need to access the "goals" variable with a lower case g rather then the "Goals" variable with an upper case G.

Multiple selected rows to show multiple images in other view controller

Choosing a string in a UITableViewController, shows pic of the same name in another view controller, which works as it should.
But the problem is when I want to choose multiple strings to show multiple different pics.
I've been trying to add another IBOutlet, doubling stuff, but it was just showing me the same pic twice.
Any idea?
multiple selection = true
First VC segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "tablesegue" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let selectedRow = indexPath.row
let passingVal = segue.destination as! Tabulka_data
passingVal.selectedImageName = self.tableItems[selectedRow]
}
}
}
secondVC:
#IBOutlet weak var pic: UIImageView!
var selectedImageName:String = ""
override func viewWillAppear(_ animated: Bool) {
self.pic.image = UIImage(named: selectedImageName)
}
two choices
You can try this
let yourDataArray = ["name 1","name 2","name 3","name 4","name 5"]
var imageSelected = [String]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return yourDataArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = yourTable.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = yourDataArray[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
imageSelected.append(yourDataArray[indexPath.row])
print(imageSelected)
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
for (index, element) in imageSelected.enumerated() {
if element == yourDataArray[indexPath.row] {
imageSelected.remove(at: index)
}
}
print(imageSelected)
}
You can use didSelectRowAt and didDeoRowAt to see which rows are selected, then just pass imageSelected to the second viewController

Getting correct index row from sectioned tableview after JSON download Swift 4

JSON data is already alphabetized before being downloaded. The app correctly divides the data into sections, creates the section title based off of the first letter, and lists all names starting with that letter in the correct section. The problem is each section repeats the same data once it transitions to the detail view controller. Sections B, C, etc. show all of correct names but repeat the "A" names when going to the detail view controller. How can I get the selected cell and the details view controller to match again?
func numberOfSections(in tableView: UITableView) -> Int {
return figuresByLetter.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return figuresByLetter[section].key
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return figuresByLetter[section].value.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "figureCell", for: indexPath)
cell.textLabel?.text = figuresByLetter[indexPath.section].value[indexPath.row].name
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetails", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? FigureViewController {
destination.figure = figures[(tableView.indexPathForSelectedRow?.row)!]
}
}
Let me know if there is any other code needed to answer the question!
struct FigureStats: Decodable {
let name: String
let number: String
let weapon: String?
let desc: String?
let year: String?
}
In the detail view controller:
class FigureViewController: UIViewController {
var figure:FigureStats?
override func viewDidLoad() {
super.viewDidLoad()
nameLabel.text = figure?.name
numberLabel.text = figure?.number
weaponLabel.text = figure?.weapon
descLabel.text = figure?.desc
yearLabel.text = figure?.year
}
}
Try using this property. I hope it helps you.
class YourClass: UIViewController {
var currentFigure: FigureStats!
func numberOfSections(in tableView: UITableView) -> Int {
return figuresByLetter.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return figuresByLetter[section].key
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return figuresByLetter[section].value.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "figureCell", for: indexPath)
cell.textLabel?.text = figuresByLetter[indexPath.section].value[indexPath.row].name
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
currentFigure = figuresByLetter[indexPath.section].value[indexPath.row]
print(currentFigure)
performSegue(withIdentifier: "showDetails", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? FigureViewController {
print(currentFigure)
destination.figure = currentFigure
}
}
class FigureViewController: UIViewController {
var figure:FigureStats?
override func viewDidLoad() {
super.viewDidLoad()
print(figure)
nameLabel.text = figure?.name
numberLabel.text = figure?.number
weaponLabel.text = figure?.weapon
descLabel.text = figure?.desc
yearLabel.text = figure?.year
}
}
I think your problem is that you don't send data to your showDetails (I don't see how you do it) so you see data which did add to storyboard but not data from your model, check it.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetails", sender: indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetails" {
let controller = (segue.destination as! UINavigationController).topViewController as! YourViewController
let row = (sender as! IndexPath).row
controller.figuresByLetter = figuresByLetter[row]
}
}

Resources