Use of unresolved identifier 'tableView' - swift

I had an a problem with this code, this identifier didn't run with me
import UIKit
class RestaurantDetailViewController: UIViewController {
#IBOutlet var restaurantImageView: UIImageView!
var restaurantImage = ""
override func viewDidLoad () {
super.viewDidLoad ()
restaurantImageView.image = UIImage(named: restaurantImage)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showRestaurantDetail" {
if let indexPath = tableView.indexPathSelectedRow {
let destinationController = segue.destinationViewController as! RestaurantDetailViewController
destinationController.restaurantImage = restaurantImageView[indexPath.row]
}
}
}
}
The error is : Use of unresolved identifier 'tableView'
Looking for a solution.
Thanks,

The prepareForSegue method is obviously in the wrong class (copy and paste error?).
It belongs to the Master controller.

Do you have a tableview in your VIEWCONTROLLER ? If yes then create a IBOOutlet between your class and viewcontroller

you can see this question or this this for reference.
This behavior is called "scope" and is crucial to any programming language. Variables declared inside a method are neither visible outside it nor do they persist when that method has finished running. and in your case for creating tableView variable you have to create an IBOoutlet or you can define it programmatically too you can get more detail from here
and here

Related

Checking Segue after it initiated

I have two UIViewController: A, B
Lets say there are two segues connecting them: C, D
Once a segue has been activated and I am in view B, can I know which segue got me here? C or D?
I don't know of any built-in mechanism for this, but you could have all your destination view controllers conform to a protocol SourceSegueProtocol that has a var to contain the invoking segue.
Then in the source view controller's prepare(for:sender:) method you could set that variable for destination view controllers that conform to the SourceSegueProtocol.
There is a prepare(for: segue) function that allows you to set a property in the new ViewController.
class OriginViewController : UIViewController {
...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? SegueProtocol {
destination.transitionSegue = segue.identifier
}
}
}
class DestinationViewController : UIViewController, SegueProtocol {
var transitionSegue: String = ""
override func viewDidLoad() {
print("Segue: ", transitionSegue)
}
}
protocol SegueProtocol {
var transitionSegue : String { get set }
}
Edit: As per comment suggestion, it's better to expect a destination that conforms to a protocol rather than one of a specific type.

Pass data using TabBarController

I know this question has been ask a lot on stack overflow. So I have a TabBarController that has 2 NavigationController, which both NavigationController have a TableViewController. I am using firebase to get a user, saving the user into a variable called currentUser. Now my problem starts here, I want to set the 2nd Navigation/Tableview controller title to the user's name. I know how to pass data using the prepare for segue, however there is no segue in TabBarController.
I've found a solution, not sure if its good or bad. What I did was make the first controller to be the delegate of the tab bar. Then I added tabBarController did select method. Here is the code.
class FirstTableVC: UITableViewController, UITabBarControllerDelegate {
var currentUser: User?
override func viewDidLoad() {
super.viewDidLoad()
tabBarController?.delegate = self
}
//Code that saves user
func code() {
...
...
...
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if viewController == tabBarController.viewControllers![1] {
let navController = tabBarController.viewControllers![1] as? UINavigationController
let secondTableVC = navController?.topViewController as! SecondTableVC
secondTableVC.currentUser = currentUser
}
}
}
class SecondTableVC: UITableViewController {
var currentUser: User?
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title =currentUser?.name
}
}
This works but not sure if this is a good way to do. I was wondering if there is a better way or a more efficient way. Thanks :)
Added:
Okay read this article about passing data using tabController. The author says that we should pass data using the state of the app. I am not really sure what he means by this. This is what I though he meant.
Example code:
class Person {
var name: String
var email: String
static var currentPerson: Person?
init(name: String, email: String) {
self.name = name
self.email = email
}
}
Can some please help me clarify . Thanks.
There's nothing wrong with this solution. Another way would be to have an abstracted class responsible for the user login object (instead of FirstTableVC having responsibility) that is accessible from both FirstTableVC and SecondTableVC
Add this in your FirstTableVC
func code() {
...
...
...
self.changeTabbarTitle()
}
func changeTabbarTitle() {
if let items = self.tabBarController?.tabBar.items {
items[1].title = currentUser?.name
}
}
You can use with delegate protocol
create NavigationTitle Protocol in FirstViewController
protocol NavigationTitle{
func setTitle(name:String)
}
class FirstViewController: UITableViewController,UITabBarControllerDelegate{
var delegate: NavigationTitle?
func setCurrentUser(){
delegate.setTitle(name:self.currentUser?.name)
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if viewController == tabBarController.viewControllers![1] {
let navController = tabBarController.viewControllers![1] as? UINavigationController
let secondTableVC = navController?.topViewController as! SecondTableVC
self.delegate = secondTableVC.self
}
}
}
implement protocol in SecondVC
class SecondVC: UITableViewController,NavigationTitle{
func setTitle(name:String){
navigationItem.title = name
}
}

Segue w/ Tab View Controller Keeps Passing Value

I am working on an iOS application that is built around a Tab View Controller. I have created a "Contacts" tab, where a user can find and select a contact from a list. When the user selects the contact, it takes the contact's name and passes it to a different tab. That function is being done like so:
func passName(name: String) {
let navTab = self.tabBarController!.viewControllers![2] as! UINavigationController
let homeTab = navTab.viewControllers[0] as! MainController
homeTab.passedName = name
tabBarController?.selectedIndex = 2
}
Everything works as it should so far (name is loaded into text field). My issue is that the value seems to keep coming back every time I change tabs and then go back to my Home tab. For example, if I select "John" from my contacts, it will take me to the Home Tab and put John's name in a textfield. Let's say I delete the last two letters of the name, so now it is "Jo". If I load a different tab and come back, the name field has been reset to "John". It's as if the value gets re-passed every time I open the Home Tab. Also, every time I load the Home Tab after passing a name, my console prints: "Name Passed: John", so it shows that this is being processed every single time the tab appears. Here is my code for processing the name:
var passedName: String!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//Checks if name was passed to controller
if let validName = passedName {
print("Name passed: \(validName)")
nameTextField.text = validName
}
}
Am I passing the data incorrectly? I was thinking it might be because I have the above code being called in the viewWillAppear method, but that doesn't make sense, as essentially the data is only being passed one time from the Contacts tab. Thanks!
The problem is that you're not actually passing the value back to the original view. Apple's recommendation for passing information between classes is to use the delegate pattern. This allows the modal view to call the delegate class's function, which changes the name local to the original view because that function is declared in the original view's viewController. You can read more about the pattern in this tutorial, but I've also included a brief example relevant to your use case below.
mainViewController:
class namesTableViewController: UITableViewController, editNameDetailsViewControllerDelegate {
var name : String
#IBAction func editButtonPressed(_ sender: UIBarButtonItem) {
performSegue(withIdentifier: "editPerson", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "editPerson" { //Modal segue
let navController = segue.destination as! UINavigationController
let controller = navController.topViewController as! editNameViewController
controller.delegate = self
if let person = sender as? Person {
print("Sending person to edit")
controller.personToEdit = person
}
} else {
super.prepare(for: segue, sender: sender)
}
}
//Protocol function
func changeName(n: String, controller: UIViewController) {
name = n
dismiss(animated: true, completion: nil)
}
}
editNameViewController:
class editNameViewController: UIViewController {
#IBOutlet weak var personNameTextField: UITextField!
var personToEdit : Person?
weak var delegate : PersonTableViewController?
override func viewDidLoad() {
super.viewDidLoad()
if personToEdit != nil {
personNameTextField.text = personToEdit?.name
}
}
// Button Actions
#IBAction func saveButtonPressed(_ sender: UIBarButtonItem) {
delegate?.personDetailsView(n: personNameTextField.text, controller: self)
}
}
Finally, the protocol class :
protocol editNameDetailsViewControllerDelegate : class {
func personDetailsView(n: String, controller: UIViewController)
}
Hope this helps.
The problem is "passedName" variable doesn't changed its value every time you edit it in your UITextField. Keep in mind that every time you change tabs, the UIViewController will call viewWillAppear and viewDidAppear. So your UITextField will always show passedName value once you select other tab and return.
I suggest that every time you edit the textfield you should update passedName value.
Sorry for my bad english.

Swift weak var scenario

I was reading the CoreData docs here and came across the following example illustrating how to implement a segue from a parent list to a child using dependency injection and was a little confused by the code sample given.
class DetailViewController: UIViewController {
weak var employee: AAAEmployeeMO?
}
and in the MasterViewController
let CellDetailIdentifier = "CellDetailIdentifier"
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
switch segue.identifier! {
case CellDetailIdentifier:
let destination = segue.destinationViewController as! DetailViewController
let indexPath = tableView.indexPathForSelectedRow!
let selectedObject = fetchedResultsController.objectAtIndexPath(indexPath) as! AAAEmployeeMO
destination.employee = selectedObject
default:
print("Unknown segue: \(segue.identifier)")
}
}
I've got a decent understanding of weak variables but am a bit confused with this particular case because I don't see how it is necessary in this scenario.
Can anyone enlighten me as to why it is used here? Where is the potential for a strong reference cycle?

Swift; delegate embedded view controller and parent

Sorry in advance that I can’t explain myself very well. I’m really new to programming and the topic of delegation still eludes me. I had some great help with this once before, but now I am trying to use a delegate in a different situation and I can’t get it right. I pieced together a bit of code that doesn’t work, and no matter how much I search I can’t find a way to fix it.
I have a view controller (MainController) with and embedded view controller (EmbeddedController) in a container view. I am trying to have a button in the embedded controller manipulate the container view (containerView).
EmbeddedController:
protocol ControllerDelegate {
func hideContainerView()
}
class EmbeddedController: UIViewController {
var delegate: VControllerDelegate?
#IBAction func button(sender: AnyObject) {
delegate?.hideContainerView()
}
}
MainController:
class MainController: UIViewController, ControllerDelegate {
#IBOutlet var containerView: UIView!
func hideContainerView() {
containerView.hidden = true
}
override func viewDidLoad() {
super.viewDidLoad()
var vc = EmbeddedController()
vc.delegate = self
}
}
Does anyone have any idea what I am doing wrong? And why this isn’t working?
What I ended up doing is adding this to the MainController:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "mySegue") {
let vc = segue.destinationViewController as! EmbeddedController
vc.delegate = self
}
}
In storyboard I selected the segue from the MainController to the EmbeddedController, and set the identifier to "mySegue".
Without the code above the delegate kept returning nil. I didn't look into this solution at first as I thought segues were only for transitioning between view controllers, and in my mind I didn't see the embedded controller as a transition. Maybe someone more knowledgable than me (which is practically anyone on here at this point) can explain how this is all fitting together.
In any case, this is how I solved my issue and hopefully someone else can benefit from this as well :)
First of all, to avoid strong reference cycles:
protocol ControllerDelegate: class {
func hideContainerView()
}
class EmbeddedController: UIViewController {
weak var delegate: ControllerDelegate?
And you haven't added your newly instantiated VC view to container view, and not added it as a child VC:
let vc = EmbeddedController()
vc.delegate = self
containerView.addSubview(vc.view)
self.addChildViewController(vc)
vc.didMoveToParentViewController(self)