I have two controllers and I'd like to pass datas between them:
This is the first controller, a tableviewcontroller.
class BooksVC: UITableViewController {
var books: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Books"
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return books.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = books[indexPath.row]
return cell
}
}
And this is the second controller, a viewcontroller
class AddController: UIViewController {
#IBOutlet weak var inputField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func done(_ sender: Any) {
let myVC = storyboard?.instantiateViewController(withIdentifier: "BooksVC") as! BooksVC
myVC.books.append(inputField.text!)
myVC.tableView.reloadData()
dismiss(animated: true, completion: nil)
}
}
Reloaddata doesn't work, can you help me I would be very glad.
You need to have a reference to the first view controller to do what you need.
You can do something like this:
class AddController: UIViewController {
....
weak var booksVC: BooksVC?
#IBAction func done(_ sender: Any) {
booksVC.books.append(inputField.text!)
booksVC.tableView.reloadData()
dismiss(animated: true, completion: nil)
}
}
And when you instatiate an AddController, you need to pass a reference of your BooksVC. Something like this:
addController.booksVC = self
Alright for second ViewController when doneButton pressed you can
dissmisstheview
trasffer data
self.parentviewController. books.apped(inputField.text!)
self.dissmissviewcontroller
than in the first VC you can do something like this
in viewWillAppear func{
tableview.reloadData()
}
Related
I have an array in a tableView that shows that contents of that array, and I am able to append values to the array. the problem is that whenever I go to another page, it automatically empties, but I want the values to stay there. Here is my code for the first view controller:
// AddFoodViewController.swift
// grosseries
//
// Created by Amish Tyagi on 6/2/20.
// Copyright © 2020 grosseries. All rights reserved.
//
import UIKit
class AddFoodViewController: UIViewController {
#IBOutlet weak var foodTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func doneTapped(_ sender: Any) {
}
func transitionToNext() {
let homeViewController = storyboard?.instantiateViewController(identifier: "TableViewController") as? TableViewController
view.window?.rootViewController = homeViewController
view.window?.makeKeyAndVisible()
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "toTableViewController") {
let homeViewController = segue.destination as? TableViewController
homeViewController?.food.append(foodTextField.text!)
view.window?.rootViewController = homeViewController
}
}
}
Here is the code for my second view controller:
// TableViewController.swift
// grosseries
//
// Created by Amish Tyagi on 5/29/20.
// Copyright © 2020 grosseries. All rights reserved.
//
import UIKit
class TableViewController: UIViewController{
#IBOutlet var tableView: UITableView!
var food : [String]! = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
// Do any additional setup after loading the view.
}
#IBAction func addItemTapped(_ sender: Any) {
transitionToNext()
}
func transitionToNext() {
let nextViewController = storyboard?.instantiateViewController(identifier: "AddFoodViewController") as? AddFoodViewController
view.window?.rootViewController = nextViewController
view.window?.makeKeyAndVisible()
}
}
extension TableViewController : UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("you tapped me :)")
}
}
extension TableViewController : UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return food.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = food[indexPath.row]
return cell
}
}
Here is a picture of my storyboard layout:
Any Help would be greatly appreciated!
In the storyboard, select your TableViewController storyboard and select Editor->Embed in->Navigation Controller:
Below your tableView (or anywhere else you'd like) add a button with a segue to show the AddFoodViewController.
Now, on your AddFoodViewController add a button to confirm the food you put on textField:
#IBAction func confirmAddedFood(_ sender: Any) {
guard let tableViewVC = navigationController?.viewControllers.first as? TableViewController else { return }
tableViewVC.food.append(foodTextField.text!)
navigationController?.popViewController(animated: true)
}
You don't need the food array on your AddFoodViewController anymore, you still need the one on TableViewController though.
Don't forget to reload the tableView when you go back to it after adding a food, in your TableViewController add:
override func viewWillAppear(_ animated: Bool) {
tableView.reloadData()
}
Your TableViewController:
class TableViewController: UIViewController{
#IBOutlet var tableView: UITableView!
var food: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
// In the case when not using prototype cell.
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
tableView.reloadData()
}
}
extension TableViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return food.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = food[indexPath.row]
return cell
}
}
And FoodViewController:
class AddFoodViewController: UIViewController {
#IBOutlet weak var foodTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func confirmAddedFood(_ sender: Any) {
guard let tableViewVC = navigationController?.viewControllers.first as? TableViewController else { return }
tableViewVC.food.append(foodTextField.text!)
navigationController?.popViewController(animated: true)
}
}
It's not very cool to just give the already done code, but I think in this case is just a silly mistake, whether from you or me.
I am trying to retrieve selected item in tableView from ViewController which has a list from sqlite like checkmark selection. My FirstViewController have tableView and on clicking selective cell it opens ViewController to select items.
Once selection has been made I have to reload FirstViewController tableview. Actually I am trying to do but not accurately doing. Anybody help me please to select item and set in tableview. I am confused and not able to do that passing selection via segue.
My github project link is below:
My Project: https://github.com/MasamMahmood/SqliteDataList/tree/master/SqliteDataList
Reference: https://blog.apoorvmote.com/how-to-pass-selection-via-segue/
Updated One FirstViewController:
class FirstViewController: UIViewControlller, ListDelegate {
var selectedIndex: Int?
var selectedSection: Int?
//Click event for navigation from FirstViewController to SecondViewController
#IBAction func BackButtonAction(_ sender: Any) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
func listFunc(listValue: String) {
AppData?.sectionList?[selectedSection!].items?[selectedIndex!].textField = listValue
tableView.reloadData()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//Add these two line in your code
selectedIndex = indexPath.row
selectedSection = indexPath.section
}
}
Updated SecondViewController:
protocol ListDelegate {
func listFunc(listValue: String)
}
class SecondViewController: UIViewControlller {
var delegate: ListDelegate?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexPath = tableView.indexPathForSelectedRow
let currentCell = tableView.cellForRow(at: indexPath!)!
//print(currentCell.textLabel?.text as Any)
currentCell.accessoryType = .checkmark
delegate?.listFunc(listValue: currentCell.textLabel?.text ?? "")
self.navigationController?.popViewController(animated: true)
}
}
What you need is a delegate to communicate back to the FirstViewController. You can do that like so...
protocol ViewControllerDelegate: AnyObject {
func viewControllerDidMakeSelection(at index: Int)
}
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
weak var delegate: ViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
delegate?.viewControllerDidMakeSelection(at: indexPath.row)
}
}
class FirstViewController: UIViewController, ViewControllerDelegate {
#IBOutlet weak var tableView: UITableView!
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
func viewControllerDidMakeSelection(at index: Int) {
// This is where the magic happens
}
}
I have one table view and inside that i placed one main view. And inside that main view i placed one button.And when ever use click on my cell button. I need to get the cell title label.This is what i need. But i tried following below code. Not sure what i am missing out. It not at all calling my cell.add target line.
Code in cell for row at index:
cell.cellBtn.tag = indexPath.row
cell.cellBtn.addTarget(self, action:#selector(self.buttonPressed(_:)), for:.touchUpInside)
#objc func buttonPressed(_ sender: AnyObject) {
print("cell tap")
let button = sender as? UIButton
let cell = button?.superview?.superview as? UITableViewCell
let indexPath = tableView.indexPath(for: cell!)
let currentCell = tableView.cellForRow(at: indexPath!)! as! KMTrainingTableViewCell
print(indexPath?.row)
print(currentCell.cellTitleLabel.text)
}
I even added a breakpoint, still it not at calling my cell.addTarget line
Tried with closure too. In cell for row at index:
cell.tapCallback = {
print(indexPath.row)
}
In my table view cell:
var tapCallback: (() -> Void)?
#IBAction func CellBtndidTap(_ sender: Any) {
print("Right button is tapped")
tapCallback?()
}
Here that print statement is getting print in console.
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var list = [String]()
#IBOutlet weak var tableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyTableViewCell
cell.saveButton.tag = indexPath.row
//cell.saveButton.accessibilityIdentifier = "some unique identifier"
cell.tapCallback = { tag in
print(tag)
}
return cell
}
}
class MyTableViewCell: UITableViewCell {
// MARK: - IBOutlets
#IBOutlet weak var saveButton: UIButton!
// MARK: - IBActions
#IBAction func saveTapped(_ sender: UIButton) {
tapCallback?(sender.tag)
}
// MARK: - Actions
var tapCallback: ((Int) -> Void)?
}
Actually this is not a good programming practice to add the button (which contains in table view cell) target action in view controller. We should follow the protocol oriented approach for it. Please try to under stand the concept.
/*This is my cell Delegate*/
protocol InfoCellDelegate {
func showItem(item:String)
}
/*This is my cell class*/
class InfoCell: UITableViewCell {
//make weak reference to avoid the Retain Cycle
fileprivate weak var delegate: InfoCellDelegate?
//Outlet for views
#IBOutlet var showButton: UIButton?
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
//This is the public binding function which will bind the data & delegate to cell
func bind(with: DataModel?, delegate: InfoCellDelegate?, indexPath: IndexPath) {
//Now the bind the cell with data here
//.....
//Assign the delegate
self.delegate = delegate
}
//Button action
#IBAction func rowSelected(sender: UIButton) {
self.delegate?.showItem(item: "This is coming from cell")
}
}
/*Now in your ViewController you need to just confirm the InfoCellDelegate & call the bind function*/
class ListViewController: UIViewController {
//Views initialisation & other initial process
}
//Table view Delegate & Data source
extension ListViewController: UITableViewDataSource, UITableViewDelegate {
/**
Configure the table views
*/
func configureTable() {
//for item table
self.listTable.register(UINib.init(nibName: "\(InfoCell.classForCoder())", bundle: nil), forCellReuseIdentifier: "\(InfoCell.classForCoder())")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "InfoCell") as! InfoCell
cell.bind(with: DataModel, delegate: self, indexPath: indexPath)
return cell
}
}
extension ListViewController: InfoCellDelegate {
func showItem(item) {
print(item)
}
}
what I did so far:
I create a tableView controller with empty rows, and a popup window on another ViewController, the purpose of the popup is to add two data(name - link) to the tableView on one ROW (passing Textfields).
what I want :
when I add a new raw (name - link) click save , I want the data to be stored , when I add other data, create another row .
my problem: when I tray to add more raws, it's always wright over the old one, so no matter how much data I input, the result always be 1 row!
popup VC
#IBOutlet weak var nameP: UITextField!
#IBOutlet weak var linkP: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let SEC: TEstVC = segue.destination as! TEstVC
SEC.add(name: nameP.text!, link: linkP.text!)
}
HomeScreen VC
class TEstVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var names = [String]()
var links = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 100.0
tableView.dataSource = self
tableView.delegate = self
}
func add(name: String, link: String) {
names.append(name)
links.append(link)
}
override func viewWillAppear(_ animated: Bool) {
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count //or links.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.cell4Name.text = names[indexPath.row]
cell.cell4Link.text = links[indexPath.row]
return cell
}
Move your array declaration to global and call reloadData in your tableView like this:
//Popup
#IBOutlet weak var nameP: UITextField!
#IBOutlet weak var linkP: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let SEC: TEstVC = segue.destination as! TEstVC
SEC.add(name: nameP.text!, link: linkP.text!)
self.tableView.reloadData()
}
Your VC
var names = [String]()
var links = [String]()
class TEstVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 100.0
tableView.dataSource = self
tableView.delegate = self
}
func add(name: String, link: String) {
names.append(name)
links.append(link)
}
override func viewWillAppear(_ animated: Bool) {
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count //or links.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.cell4Name.text = names[indexPath.row]
cell.cell4Link.text = links[indexPath.row]
return cell
}
Simple, after you add a new item to the datasource (names) you need to reload the tableview.
self.tableView.reloadData()
Well, it looks like you're going to a completely new instance of TestVC every single time. So, it's not overwriting the old line. It's putting a new line in a new instance of TestVC. I infer this because you're calling add(name:link:) in prepareForSegue, which gets called by the storyboard when a new viewController is being put on screen with a segue.
Also, your add(name:link:) method should call tableView.reloadData() if you want it to update the screen.
I'm trying to pass just the image part of a struct (containing also two textFields, and another imageView I'd like to not pass).
Here is the tableViewController
import UIKit
class SentMemesTableViewController: UITableViewController {
var _tableView: UITableView!
var memeData: [Meme] = []
//calling memes from array in Delegate
let appDelegate = UIApplication.shared.delegate as! AppDelegate
var memes: [Meme] {
return appDelegate.memes
}
override func viewWillAppear(_ animated: Bool) {
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.isScrollEnabled = true
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MemeDetailViewController" ,
let nextScene = segue.destination as? MemeDetailViewController ,
let indexPath = tableView.indexPathForSelectedRow {
let selectedMeme = memes[indexPath.row].memedImage
nextScene.sentMemeView.image = Meme.memedImage
}
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//navigationController!.pushViewController(MemeDetailViewController, animated: true)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return memes.count
}
// Here it is! -----
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let tableViewCell = tableView.dequeueReusableCell(withIdentifier: "sentMemesTableView") as! MemeTableViewCell
let meme = memes[indexPath.row]
tableViewCell.tableViewImage.image = meme.memedImage
tableViewCell.tableViewLabel.text = "\(meme.topText)...\(meme.bottomText)"
return tableViewCell
}
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return false
}
func deleteMemesInTableViewCell(_ index: Int) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.memes.remove(at: index)
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
tableView.beginUpdates()
deleteMemesInTableViewCell(indexPath.row)
tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.left)
tableView.endUpdates()
}
}
}
Here is the Meme and SentMemeView structs.
import Foundation
import UIKit
struct Meme {
let topText: String
let bottomText: String
let originalImage: UIImage
let memedImage: UIImage
}
struct SentMemeImageView {
var memedImageDetailVC: UIImageView
}
I can't find a way to call it successfully.
Here is the MemeDetailViewController. I need just for the sentMemeView to display the SentMemeImageView.
class MemeDetailViewController: UIViewController {
var meme = SentMemeImageView?.self
#IBOutlet weak var sentMemesBtn: UIBarButtonItem!
#IBOutlet weak var editBtn: UIBarButtonItem!
#IBOutlet weak var sentMemeView: UIImageView!
func displayMeme(_ meme: SentMemeImageView) {
}
#IBAction func launchMemeEditorViewController(_ sender: Any) {
_ = navigationController?.popViewController(animated: true)
}
//unwinding to the view before (the collectionView, or the tableView)
#IBAction func unwindVC(for unwindSegue: UIStoryboardSegue, towardsViewController subsequentVC: UIViewController) {
self.dismiss(animated: true, completion: nil)
}
}
First of all, sad to say that, your code is quite messed up:
You have with three different kinds of data sources, some are ignored, some you work with, but not in a consistent way:
data from the app delegate (deletion of data)
memeData property (display of data)
memes property (ignored)
You should really focus on where the data is stored and how to access it.
Secondly, you won't send views from one view controller to the other, but data. So rather use UIImage than UIImageView. So you'll hand in a SentMemeImage to the details controller.
I tried to clean up the code a little, but just to answer your explicit question. Please refactor it!
struct Meme {
let topText: String
let bottomText: String
let originalImage: UIImage
let memedImage: UIImage
}
struct SentMemeImage {
var memedImage: UIImage
}
class SentMemesTableViewController: UITableViewController {
var memeData: [Meme] = []
// ----8<---- snipp ----8<----
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MemeDetailViewController" ,
let nextScene = segue.destination as? MemeDetailViewController ,
let indexPath = tableView.indexPathForSelectedRow {
let selectedMeme = memeData[indexPath.row]
let sentMemeImage = SentMemeImage(memedImage: selectedMeme.memedImage)
nextScene.meme = sentMemeImage
}
}
// ----8<---- snipp ----8<----
}
class MemeDetailViewController: UIViewController {
var meme:SentMemeImage?
#IBOutlet weak var sentMemesBtn: UIBarButtonItem!
#IBOutlet weak var editBtn: UIBarButtonItem!
#IBOutlet weak var sentMemeView: UIImageView!
func displayMeme() {
self.sentMemeView.image = self.meme?.memedImage
}
}