navigationController is nil while performing segue? - ios

I am using a library called SwiftPages. It works fine. But when i perform a Push Segue from this View Controller to another the navigation bar self.navigationController is nil ?
How can i add the navigation bar into the pushed VC ?
ViewController
class CoursePageController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var ChapterTable: UITableView!
#IBOutlet weak var courseDesc: UITextView!
var CourseName : String!
var ChapName : [String] = []
var ChapId : [String] = []
override func viewDidLoad() {
super.viewDidLoad()
self.title = CourseName
self.courseDesc.text = CourseDescriptionC
self.courseDesc.setContentOffset(CGPointZero, animated: true)
HUD()
ChapterTable.estimatedRowHeight = 120
ChapterTable.rowHeight = UITableViewAutomaticDimension
getData()
}
func HUD(){
progress.show(style: MyStyle())
}
func getData(){
Alamofire.request(.GET, "http://www.wve.com/index.php/capp/get_chapter_by_course_id/\(CourseIdinController)")
.responseJSON { (_, _, data, _) in
let json = JSON(data!)
let catCount = json.count
for index in 0...catCount-1 {
let cname = json[index]["CHAPTER_NAME"].string
let cid = json[index]["CHAPTER_ID"].string
self.ChapName.append(cname!)
self.ChapId.append(cid!)
}
self.ChapterTable.reloadData()
self.progress.dismiss()
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return ChapName.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : UITableViewCell = self.ChapterTable.dequeueReusableCellWithIdentifier("ChapCell") as! UITableViewCell
cell.textLabel?.text = self.ChapName[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//Here the navigationController is nil
self.navigationController?.pushViewController(self.storyboard!.instantiateViewControllerWithIdentifier("LessonController") as! UIViewController, animated: true)
// performSegueWithIdentifier("ChapSegue", sender: nil)
}
EDIT
I have added the Navigation controller as my Initial VC. The Course page Controller is shown from the SwiftPages.
I can't find how it is being presented. Please take a look at this file.
https://github.com/GabrielAlva/SwiftPages/blob/master/SwiftPages/SwiftPages.swift
Thanks in Advance!

Did you put CoursePageController in NavigationController? If yes then check how it is presented?
If it is presented modally then you won't get the navigationController reference.
This will work if you have navController as rootViewController
if let navController = UIApplication.sharedApplication().keyWindow?.rootViewController as? UINavigationController {
//get the nav controller here and try to push your anotherViewController
}

Related

How to press on a tableview cell to present a view controller with the text in navigation controller

Essentially I have a view controller called FirstViewController, this view controller contains a table view within it called listTableView.
I would like to tap on one of the cells in the table view listTableView and present whatever text was in the cell as the navigation controller title.
The navigation controller that appears when the cell is tapped is called showDetailsViewController.
How can this be done?
The following is what I have written in the FirstViewController
import UIKit
import AudioToolbox
class FirstViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, FeedModelProtocol {
var feedItems: NSArray = NSArray()
var selectedStock : StockModel = StockModel()
let tableView = UITableView()
#IBOutlet weak var listTableView: UITableView!
#IBOutlet weak var refreshButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
//set delegates and initialize FeedModel
self.listTableView.delegate = self
self.listTableView.dataSource = self
let feedModel = FeedModel()
feedModel.delegate = self
feedModel.downloadItems()
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
#IBAction func reloadData(_ sender: Any) {
print("reload pressed")
listTableView.reloadData()
viewDidLoad()
_ = AudioServicesPlaySystemSound(1519)
}
func itemsDownloaded(items: NSArray) {
feedItems = items
self.listTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of feed items
print("item feed loaded")
return feedItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Retrieve cell
let cellIdentifier: String = "stockCell"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
myCell.textLabel?.textAlignment = .center
myCell.textLabel?.font = .boldSystemFont(ofSize: 18)
// Get the stock to be shown
let item: StockModel = feedItems[indexPath.row] as! StockModel
// Configure our cell title made up of name and price
let titleStr = [item.customer].compactMap { $0 }.joined(separator: "-")
print(titleStr)
// Get references to labels of cell
myCell.textLabel!.text = titleStr
return myCell
}
}
UPDATE:
What is the issue with this code:
NOTE:
The restoration id of the tableview is scheduleTable
var homeworkIdentifierFromTableViewCell = ""
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
homeworkIdentifierFromTableViewCell = feedItems[indexPath.row].myCell
self.performSegue(withIdentifier: "scheduleTable", sender: self)
listTableView.deselectRow(at: indexPath, animated: true)
}
UPDATE 2
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item: StockModel = feedItems[indexPath.row] as! StockModel
let titleStr = [item.customer].compactMap { $0 }.joined(separator: "-")
print(titleStr)
}
You can use the didSelectRowAt to notice what cell was clicked and store what the text in the cell was (homeworkArray is the list of cells from a struct. Homeworkidentifier is a value in the struct).
var homeworkIdentifierFromTableViewCell = ""
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
homeworkIdentifierFromTableViewCell = homeworkArray[indexPath.row].homeworkIdentifier
self.performSegue(withIdentifier: "homeworktoExpandHomework", sender: self)
homeworkTableView.deselectRow(at: indexPath, animated: true)
}
Then, you could use a prepare for a segue function to pass the text of the table view cell to the next view controller. You do this by creating a variable in the other view controller (the one that you are going to pass data to) and later accessing it from the other view controller and changing its value.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "reportBug" {
let destinationViewController = segue.destination as! WebViewController
destinationViewController.reason = "reportBug"
}
else if segue.identifier == "provideFeedback" {
let destinationViewController = segue.destination as! WebViewController
destinationViewController.reason = "provideFeedback"
}
}
Here is more about passing data between viewcontrollers : Passing data between View Controllers in Swift (From TableView to DetailViewController)
Hope this helps
EDIT:
Here is the struct I am using :
struct homeworkTableViewCellData {
let homeworkName : String!
let className : String!
let dateName : String!
let colorImage : UIImage!
let homeworkIdentifier : String!
}
I have initialized my homeworkArray with this struct. When I am calling a value from the cell, I am picking one from in the struct.
To set the table view with a struct is more organized. This is a good video that teaches you how to set it up (if you are want to do that) : https://www.youtube.com/watch?v=zAWO9rldyUE&list=LL--UalPCi7F16WzDFhMEg7w&index=20&t=921s

How to send clicked item of Table View to other view controller when datasource and delegate are separate from View Controller

I am a beginner to iOS coming from the android background and just learned about table view (for me it's an Android ListView). I am trying to separate data source & delegate from view controller. I found some tutorials on how to do so but stuck at figuring out how to send the clicked item to another view controller. The code is below:
class PictureTableViewController: UIViewController {
#IBOutlet weak var pictureTableView: UITableView!
private let picsDataSource: PicturesDataSource
required init?(coder aDecoder: NSCoder) {
self.picsDataSource = PicturesDataSource()
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
pictureTableView.dataSource = picsDataSource
pictureTableView.reloadData()
pictureTableView.delegate = picsDataSource
}
}
class PicturesDataSource: NSObject, UITableViewDataSource, UITableViewDelegate{
private var pictureModels = [PictureModel]()
override init(){
let picModelsDataController = PictureModelsDataController()
pictureModels = picModelsDataController.pictureModels
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pictureModels.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PictureCell.self)) as! PictureCell
let picModel = pictureModels[indexPath.row]
cell.pictureName = picModel.pictureName
cell.imageItem = picModel.imageItem
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//1 - try loading the "Detail" view controller and typecasting it to be DetailViewController
if let detailViewController = storyboard.instantiateViewController(withIdentifier: "PictureDetailView") as? PictureDetailViewController {
//2 - success! set its selecteImage property
detailViewController.selectedImgName = pictureModels[indexPath.row].pictureName
//3 - now push it onto the navigation controller
navigationController?.pushViewController(detailViewController, animated: true)
}
}
}
Error in: func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){ }. since "storyboard" & "navigationController" are not available in PicturesDataSource class, how can I send clicked item(picture name) to the DetailsViewController
There are StackOverflow answers about separating data source and delegate but did not solve my problem.
Using: Xcode 8.3 beta 6
You can include a reference to main view controller at your table view events handler. Below is a playground code I derived from your example:
import UIKit
// MARK: - Model
struct Picture {
let title: String
let image: UIImage
}
struct PictureModelsDataSource {
let pictures = [
Picture(title: "exampleTitle", image: UIImage(named: "exampleImage")!),
Picture(title: "exampleTitle", image: UIImage(named: "exampleImage")!)
]
}
// MARK - View
class PictureCell: UITableViewCell {
#IBOutlet weak var pictureTitleLabel: UILabel!
#IBOutlet weak var pictureImage: UIImageView!
}
// MARK: - Controller
class PictureTableViewController: UIViewController {
// MARK: - Properties
#IBOutlet weak var pictureTableView: UITableView!
private var pictureListController: PictureListController?
// MARK: - View lifecycle
override func viewDidLoad() {
super.viewDidLoad()
pictureListController = PictureListController()
pictureListController?.viewController = self
pictureTableView.dataSource = pictureListController
pictureTableView.delegate = pictureListController
pictureTableView.reloadData()
}
}
class PictureDetailViewController: UIViewController {
var selectedPictureTitle: String?
}
class PictureListController: NSObject, UITableViewDataSource, UITableViewDelegate {
// MARK: - Properties
weak var viewController: PictureTableViewController?
private let pictures: [Picture] = {
let pictureModelsDataSource = PictureModelsDataSource()
return pictureModelsDataSource.pictures
}()
// MARK: - View setup
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
// MARK: - Event handling
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pictures.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PictureCell.self)) as? PictureCell else {
return UITableViewCell()
}
let picture = pictures[indexPath.row]
cell.pictureTitleLabel.text = picture.title
cell.pictureImage.image = picture.image
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let pictureTitle = pictures[indexPath.row].title
let storyboard = UIStoryboard(name: "exampleStoryboard", bundle: nil)
if let pictureDetailViewController = storyboard.instantiateViewController(withIdentifier: "PictureDetailView") as? PictureDetailViewController {
pictureDetailViewController.selectedPictureTitle = pictureTitle
viewController?.navigationController?.pushViewController(pictureDetailViewController, animated: true)
}
}
}
See StoryBoard object can be obtained by using this
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
Now your second question is about how to get navigation controller. It means how to get currentViewController in your case. This can be get by below code
func getCurrentViewController() -> UIViewController? {
if let rootController = UIApplication.shared.keyWindow?.rootViewController {
var currentController: UIViewController! = rootController
while( currentController.presentedViewController != nil ) {
currentController = currentController.presentedViewController
}
return currentController
}
return nil
}
Now your didSelectRowAt code will look like this
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let detailViewController = storyboard.instantiateViewController(withIdentifier: "PictureDetailView") as? PictureDetailViewController
detailViewController.selectedImgName = pictureModels[indexPath.row].pictureName
self.getCurrentViewController()!.pushViewController(detailViewController, animated: true)
}
You are trying to adhere to MVC, but you are confusing what your actual data source is.
PicturesDataSource is not your data source. It's the code that tells your table how to set itself up.
PictureModelsDataController() is the source from which you get the data that actually populates that table.
All of your posted code should be in the same class:
class PictureTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
Change these lines:
pictureTableView.dataSource = picsDataSource
pictureTableView.delegate = picsDataSource
to
pictureTableView.dataSource = self // Note use of self because this is now the dataSource, not another class
pictureTableView.delegate = self // Note use of self because this is now the delegate, not another class
and remove:
private let picsDataSource: PicturesDataSource
required init?(coder aDecoder: NSCoder) {
self.picsDataSource = PicturesDataSource()
super.init(coder: aDecoder)
}

How to move to other viewControllers when user tap on a tableView cell

I have a navigation bar with a table view controller that display 4 images (4 cells). I have 4 viewControllers that I need to show when the user clicks on the cell ( each image show a specific VC ) ??
any ideas would be much appreciated
here is my code :
class MenuTable: UITableViewController {
#IBOutlet weak var imgtable: UITableView!
var menuImage: [UIImage] = [
UIImage(named: "image1")!,
UIImage(named: "image2")!,
UIImage(named: "image3")!,
UIImage(named: "image4")!
]
override func viewDidLoad() {
super.viewDidLoad()
imgtable.dataSource = self
imgtable.delegate = self
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menuImage.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("menucell") as! Menucell
cell!.imageview.image = menuImage[indexPath.row]
return cell
}
}
You need to implement didSelectRowAtIndexPath delegate method of UITableView like this
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var viewController: UIViewController = UIViewController()
switch (indexPath.row) {
case 0:
viewController = self.storyboard?.instantiateViewControllerWithIdentifier("viewController1") as! viewController1
case 1:
viewController = self.storyboard?.instantiateViewControllerWithIdentifier("viewController2") as! viewController2
case 2:
viewController = self.storyboard?.instantiateViewControllerWithIdentifier("viewController3") as! viewController3
case 3:
viewController = self.storyboard?.instantiateViewControllerWithIdentifier("viewController4") as! viewController4
default:
print("default")
}
self.navigationController?.pushViewController(viewController, animated: true)
}

Passing coredata from tableview to another tableview

I am struggling with getting my care data to populate my second tableview controller. The data is populating the first tableview and I can select a row and the segue is used to go to the second table but the labels are not populated.
I've looked all over and have found older samples or obj-c but I cannot figure it out, so any help pointing this n00b in the right direction will be helpful.
Here is what I have, I think I am missing how to populate a variable to pass in prepareForSegue in the list tableview, but I could be wrong. I get a warning error in that function (Warning cannot assign value of type 'ListEntity' to type '[ListEntity]').
CoreData
Entity = ListEntity
Attributes = title, event & location (all as Strings)
listTableViewController
import UIKit
import CoreData
class ListTableViewController: UITableViewController {
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var lists = [ListEntity]()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "The List"
let addButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Add, target: self, action: #selector(ListTableViewController.addButtonMethod))
navigationItem.rightBarButtonItem = addButton
}
func addButtonMethod() {
print("Perform action")
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
reloadData()
tableView.reloadData()
}
func reloadData() {
let fetchRequest = NSFetchRequest(entityName: "ListEntity")
do {
if let results = try managedObjectContext.executeFetchRequest(fetchRequest) as? [ListEntity] {
lists = results
}
} catch {
fatalError("There was an error fetching the list!")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lists.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ListCell") as! ListTableViewCell
let list = lists[indexPath.row]
cell.configurationWithSetup(list)
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("DetailsSegue", sender: self)
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "DetailsSegue" {
let destinationVC = segue.destinationViewController as! DetailsTableViewController
let indexPath : NSIndexPath = self.tableView.indexPathForSelectedRow!
print(indexPath.row) // Print the Row selected to console
// Place the code to pass data here?
// destinationVC.lists = lists[indexPath.row]
// Warning cannot assign value of type 'ListEntity' to type '[ListEntity]'
}
}
}
listTableViewCell
import UIKit
class ListTableViewCell: UITableViewCell {
#IBOutlet weak var titleLabel: UILabel!
func configurationWithSetup(list: AnyObject) {
titleLabel.text = list.valueForKey("title") as! String?
}
}
detailsTableViewController
import UIKit
import CoreData
class DetailsTableViewController: UITableViewController {
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var lists = [ListEntity]()
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("DataCell") as! DetailsTableViewCell
let list = lists[indexPath.row]
cell.configurationWithSetup(list)
return cell
}
}
detailsTableViewCell
import UIKit
import CoreData
class DetailsTableViewCell: UITableViewCell {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var eventLabel: UILabel!
#IBOutlet weak var locationLabel: UILabel!
func configurationWithSetup(list: AnyObject) {
titleLabel.text = list.valueForKey("title") as! String?
eventLabel.text = list.valueForKey("event") as! String?
locationLabel.text = list.valueForKey("location") as! String?
}
}
The warning contains the answer - just change
var lists = [ListEntity]() to
var lists = ListEntity(), or var lists:ListEntity! and when you prepare for segue set that value.
Then you will need to change
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("DataCell") as! DetailsTableViewCell
// as data source is not array you can just you the item you passed
// let list = lists[indexPath.row]
cell.configurationWithSetup(lists)
return cell
}
You should use a static table view if you just want one cell
More info per you current issue
class DetailsTableViewController: UITableViewController {
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var theDetailListEntity:ListEntity!
override func viewDidLoad() {
super.viewDidLoad()
print(theDetailListEntity) // check that you passed it across
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("DataCell") as! DetailsTableViewCell
cell.configurationWithSetup(theDetailListEntity)
return cell
}
}
Don't forget to add prepare for segue in the listTableViewController otherwise theDetailListEntity won't be set... and then it will crash.
Depending on how you set up your segue, it may differ. But this is what you need
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("showMyDetailView", sender: indexPath)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showMyDetailView" {
guard let
vc = segue.destinationViewController as? DetailsTableViewController,
ip = sender as? NSIndexPath else { fatalError() }
let item = lists[ip.row]
vc.theDetailListEntity = item
// set the item in the next VC
tableView.deselectRowAtIndexPath(ip, animated: true)
}
}

unexpected found nil on the line tableView.datasourse = self

I have a navigationController and I have added a custom UIBarButtonItem. What I want to do is when the user tap my button , it present a viewController which has a tableView inside it.
For that ,I have written this :
let shopoingCarVC:shopingCartProductsList = shopingCartProductsList()
self.navigationController?.pushViewController(shopoingCarVC, animated: true)
shopingCartProductsList is my ViewController I intended to navigate to and when It navigate to that It gave me unexpected found nil error at this line :
tableViewProducts.dataSource = self
I have done it before on my other viewControllers but I got this problem when navigating without segue and with using pushViewController mehod.
This is my targetViewCOntroller :
import UIKit
import Alamofire
import Haneke
class shopingCartProductsList: BaseViewController ,UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var ShopingCartProducts: UITableView!
var products = dataService.instance.shopingCartProduct
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
}
override func viewDidLoad() {
super.viewDidLoad()
ShopingCartProducts.dataSource = self
ShopingCartProducts.delegate = self
self.view.backgroundColor = COLOR_BACKGROUND
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let tblCell = tableView.dequeueReusableCellWithIdentifier("shopingCart_cell") as? shopingCart_cell {
if let prod = products[indexPath.row] as? productMD{
tblCell.configCell(prod)
}
return tblCell
}else{
return shopingCart_cell()
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("products count \(products.count)")
return products.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
}
What's wrong ?
Use are wrong while pushing viewcontroller user below code.
let secondViewController = self.storyboard?.instantiateViewControllerWithIdentifier("LoginViewController") as LoginViewController self.navigationController?.pushViewController(secondViewController, animated: true)
Set identifier in Identity inspector.

Resources