Pass object from tableview to destination with revealviewcontroller in between - swift

I am trying to pass an object from my tableview to the detail view. I am using the revealviewcontroller framework to have a slide out menu. Therefore I need to create a segue from the tableview to the revealviewcontroller and from here another one to the final detailviewcontroller.
That is why I can´t set the object in the detail view - any idea how to do so?
This is the used code:
if segue.identifier == "communityDetailSegue" {
// Get the cell that generated this segue.
if let selectedCommunityCell = sender as ? UITableViewCell {
let destination = segue.destinationViewController as!CommunityViewController
if let communityIndex = self.tableView.indexPathForCell(selectedCommunityCell) {
destination.community = self.communitiesOfCurrentUser[communityIndex.row]
print(self.communitiesOfCurrentUser[communityIndex.row].name)
}
}
}
And this is the exception.
Could not cast value of type 'SWRevealViewController' (0x10027b9f0) to 'CommunityViewController'

You get the error because the segue's destination VC is the SWRevealViewController and not the CommunityViewController.
One way of solving your problem would be to pass the value in two steps:
First, in prepareForSegue() you pass the value to the SWRevealViewController (you'll need a subclass for this one, e.g. MyRevealViewController):
if segue.identifier == "communityDetailSegue" {
// Get the cell that generated this segue.
if let selectedCommunityCell = sender as ? UITableViewCell {
let destination = segue.destinationViewController as! MyRevealViewController
if let communityIndex = self.tableView.indexPathForCell(selectedCommunityCell) {
destination.community = self.communitiesOfCurrentUser[communityIndex.row]
print(self.communitiesOfCurrentUser[communityIndex.row].name)
}
}
}
Then, in MyRevealViewControlleryou can pass the value as soon as it is set:
class MyRevealViewController : SWRevealViewController {
// Let's assume this is the outlet to your final VC:
IBOutlet let communityViewController: CommunityViewController!
var community: YourCommunityType {
didSet {
if let communityVC = self.communityViewController {
communityVC.community = self.community
}
}
}
}

Related

Passing data through multiple viewcontrollers

I want to pass data from firstViewController -> secondNavigationcontroller -> secondTabBarController -> secondViewcontroller.
I can pass it to my navigationcontroller like this:
if segue.identifier == "StartMatchSegue" {
if let destination = segue.destination as? SecondNavigationController {
destination.delegate = self as? UINavigationControllerDelegate
destination.testString = "lets play"
}
}
but I cant figure out how to pass it all the way.
storyboard
You could also create a singleton which can be accessed within each view controller.
You can pass data in different ways two of them i have mentioned below.
NotitificationCenter
Closure

My cells are duplicating themselves

I am new to swift and I am trying to make this note app. I have split view controller that goes in my first view controller and that view controller connects to a table view controller. Everything works perfectly is just that when I launch the app I have all the notes like I want but when I try to go back to my first view controller and come back to my table view controller, all the notes are duplicated every single time I do it. I tried everything I can try, is there anyone who can help me
my MasterViewController is
import UIKit
class MasterViewController: UITableViewController {
var detailViewController: DetailViewController? = nil
override func viewDidLoad()
{
super.viewDidLoad()
Note.loadNotes() // The problem is here, I think
noteTable = self.tableView
// Do any additional setup after loading the view, typically from a nib.
let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(insertNewObject(_:)))
navigationItem.rightBarButtonItem = addButton
if let split = splitViewController
{
let controllers = split.viewControllers
detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}
}
My loadNotes function is
class func loadNotes()
{
let defaults:UserDefaults = UserDefaults.standard
let saveData: [NSDictionary]? = defaults.object(forKey: kAllNotes) as? [NSDictionary]
if let data:[NSDictionary] = saveData
{
for i:Int in 0 ..< data.count
{
let n:Note = Note()
n.setValuesForKeys(data[i] as! [String : Any])
allNotes.append(n)
}
}
}
Your loadNotes method keeps appending. The first line of loadNotes should be:
allNotes = [Note]()
Then it starts with an empty array and fills it up.
And why is loadNotes a static method? That's a bad design. Make Notes a normal class and make loadNotes an instance method.
On an unrelated note (no pun intended), do not use UserDefaults to store app data. Only use it to store little bits of information.

Swift: Accessing current navigation controller from a UICollectionViewCell

I have a UICollectionViewCell class "ProductCell"; I am trying to access the current navigation controller in order to update a barbuttonicon. I have tried the following code as this is what I use in my other UIViewControllers:
let nav = self.navigationController as! MFNavigationController
nav.updateCartBadgeValue()
However it states that the
value of type ProductCell has no member navigationController
I am aware that this is not a UIViewController but surely you should be able to access the current navigation controller the same way?
I also know that you can access the navigation controller by using UIApplication in the following way:
let navigationController = application.windows[0].rootViewController as! UINavigationController
I am not sure if that is a good way of doing it though.
Any help is much appreciated
Thanks
UIResponder chain will help here.
You can search the responder chain to find the controller for any view
extension UIView {
func controller() -> UIViewController? {
if let nextViewControllerResponder = next as? UIViewController {
return nextViewControllerResponder
}
else if let nextViewResponder = next as? UIView {
return nextViewResponder.controller()
}
else {
return nil
}
}
func navigationController() -> UINavigationController? {
if let controller = controller() {
return controller.navigationController
}
else {
return nil
}
}
}
controller() will return the closest responder that is of type UIViewController
Then on the returned controller you just need to find its navigation controller. You can use navigationController() here.
The simplest way is to add a property to you cell class that weakly references a UINavigationController
weak var navigationController: UINavigationController?
you will need to assign it in your cellForRow(atIndexPath:_) method.
let cell = tableView.dequeueReusableCell(withIdentifier: "yourReuseID") as! YourCellClass
cell.navigationController = navigationController //will assign your viewController's navigation controller to the cell
return cell
Unless things change, this is a good way to do it. To give you an example of a messier solution... You could add a
let hostViewController:UIViewController
property to your cell and add an initializer to handle it
let cell = ProductCell(vc: self)
But I don't think that's a better way to do it. your suggestion works fine.
let navigationController = application.windows[0].rootViewController as! UINavigationController

How do I display the data fetched from called view controller into a dynamic tableviewcell of the calling view controller while using unwind segue.?

I have dynamic tableview, wherein one of the cell (duration) when tapped opens another view controller which is a list of duration viz (30 min, 1 hour, 2 hours and so fort). One of the durations when selected should display the selected duration in the first view controller. I am able to pass the data back to first view controller using unwind segue but unable to display the passed value. DOn't know whats missing.
I am displaying the code below:
FIRST VIEW CONTROLLER (CALLING)
#IBAction func unwindWithSelectedDuration(segue:UIStoryboardSegue) {
var cell = tableView.dequeueReusableCellWithIdentifier("durationCell") as! durationTableViewCell
if let durationTableViewController = segue.sourceViewController as? DurationTableViewController,
selectedDuration = durationTableViewController.selectedDuration {
cell.meetingDurationCell.text = selectedDuration
duration = selectedDuration
}
SECOND VIEW CONTROLLER (CALLED)
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SaveSelectedDuration" {
if let cell = sender as? UITableViewCell {
let indexPath = tableView.indexPathForCell(cell)
if let index = indexPath?.row {
selectedDuration = durationList[index]
}
}
}
}
tableView.dequeueReusableCellWithIdentifier should only be called within tableView:cellForRowAtIndexPath:. It has no use outside this context.
The easiest fix is to just reload the table once you have stored the selected duration:
#IBAction func unwindWithSelectedDuration(segue:UIStoryboardSegue) {
if let durationTableViewController = segue.sourceViewController as? DurationTableViewController {
selectedDuration = durationTableViewController.selectedDuration
tableView.reloadData()
}
}
Note that this assumes you only need one selectedDuration for your whole table, rather than one per row. If you need one per row, I assume you have them stored in an array somewhere, so it is that array that should be updated instead before the reloadData.

error "secondViewController does not have a member named "mastername"

I am using a sample from IOS 8 App Development Essentials. I added a variable to my second controller but keep getting this error.
Second controller code:
Class SocondDetailController: UIViewController{
var mastername: String?
...
}
First controller code:
override func prepareForSegue(segue: UIStoryboardSegue,sender: AnyObject?)
{
if segue.identifier == "ShowDetails"
{        
let detailViewController = segue.destinationViewController as SocondDetailController
let myIndexPath = self.tableView.indexPathForSelectedRow()
let row = myIndexPath?.row
SocondDetailController.mastername = tableData[row!]
}
}
I am new to Swift and IOS development. Just starting at age 71.
I have been using VB.Net for a long time.
Please help.
Thanks.
This row:
SocondDetailController.mastername = tableData[row!]
should be:
detailViewController.mastername = tableData[row!]
mastername is an instance property, and as such you have to access to it through an instance of SocondDetailController, and not the SocondDetailController type itself.
Also, although probably not needed due to the logic in your view controller, I'd avoid using the forced unwrapping operator !, preferring a safer optional binding:
let row = myIndexPath?.row
if let row = row {
detailViewController.mastername = tableData[row]
}
or, more concisely:
if let row = myIndexPath?.row {
detailViewController.mastername = tableData[row]
}