Im new to programming and trying to build my own app, I wonder how Im I supposed to link the info I get from the addTask viewcontroller to the tableview cell? At the moment Im just trying to get the text from the textfield and Im going to add the other features later.
What Im trying to do
Refer This :-
Create a global array add remove element from that array on click of your button
You can pass data from one view controller to another using Delegates. Check my ans here. You can set your table view class as delegate of your task view controller. Implement the protocol methods of task view controller get the data and reload table.
Hope it helps.
Happyy Coding!!
You can pass data using Delegation .
In Second ViewController
import UIKit
protocol secondViewDelegate: class {
func passData(arrData : [Any])
}
class SecondViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
weak var delegate: secondViewDelegate? = nil
#IBAction func clickOnButton(_ sender: Any) {
self.delegate.passData([]) // replace your array here
}
}
In FirstViewController
class FirstViewController: UIViewController, UITableViewDataSource, secondViewDelegate
let objectSecondVC: SecondViewController? = storyboard?.instantiateViewController(withIdentifier: "secondVCID") as! SecondViewController?
objectSecondVC?.delegate = self
navigationController?.pushViewController(objectSecondVC?, animated: true)
Second ViewController Delegate Method in FirstViewController
func passData(arrData : [Any]){
// append to your main array
}
It seems like your add task view controller is connected with your table view controller though a segue. So when you moving back from add task view controller, you can use unwind to pass data back. Here is a detailed tutorial with simple instructions and pictures.
Related
I'm making an expense tracking app. I have added a table view in the main view controller, and added an "add" button in the navigation controller. On clicking this, it shows a view controller in which you type in the data.
On clicking add at the end, it should save the entered data through coreData, and then be presented in the tableView, but my app crashes saying that a nil value was found, even though I have integrated the "??" safe guard."
You tableview in startingViewController is nil this is the problem. When you call the MainVC.getAllItems() your tabview is not initialized. Probably you are re creating startingViewController on your second controller to reach it getAllItems function but it is a wrong approach. You need to update previous viewController datas with protocols or notifications.
First you need to create a protocol like below
protocol AddViewControllerDelegate {
func updateTableView()
}
After that you need to define a variable in your addViewcontroller with this protocol type and call protocol's function when user adds new expense.
class AddViewController: UIViewController {
var delegate: AddViewControllerDelegate?
func callUpdateTableView() {
delegate?.updateTableView()
}
}
In your StartingViewController must conform this protocol. So you need to add updateTableView function. Also you need to say the delegate of your second class is your first class in where you show your addViewController.
class StartingViewController: UIViewController, AddViewControllerDelegate {
func goToAddViewController() {
let vc = AddViewController()
vc.delegate = self
show(vc, sender: nil)
}
func updateTableView() {
// Reload Tableview
}
}
So basically, when you call the protocol function from your secondViewController, your firstViewController's updateTableView function called and you can reload your tableview in this function.
I am taking reference from tutorial here.
I have one View Controller which contains a Container View. On this Container View I have added Custom Segue i.e. FirstViewController. That means when I opens View Controller it by default shows FirstViewController. And on FirstViewController, I have a Button. And by clicking on this button I want to show SecondViewController but I am not able to get this achieved. I have also added print command on Button click and it prints on console and not switch to another View. Please help.
I have created a delegate in FirstViewController and aa function which reference through ViewController.
Code for FirstViewController.swift
protocol FirstViewDelegate: class {
func sendToSecondViewController()
}
class FirstViewController: UIViewController {
weak var delegate: FirstViewDelegate? = nil
#IBAction func goToSecond(_ sender: UIButton) {
print("test1")
delegate?.sendToSecondViewController()
}
}
Code for ViewController.swift
extension ViewController: FirstViewDelegate{
func sendToSecondViewController() {
container.segueIdentifierReceivedFromParent("second")
}
}
And main.storyboard
Seems you have not set delegate of FirstViewController in ViewController. Something like:
<object of FirstViewController>.delegate = self (Inside ViewController)
I have a searchViewController where I search for users and UITableView gets updated dynamically with user information. The cell for the UITableView is custom - it has a UIImage, the usernameLabel, and a button called "Add".
What I want is that if the user clicks on the add button of the cell, it should pass the user information on that cell (image and username) to another view controller that has a UITableView that is a friend list.
However, so far the only way I know is by using performSegue to pass the data on to the other viewController holding the friendlist UITable. But by this method, every time I click the add button it segues to the other view controller which I don't want. I want it to stay on the searchViewController when the add button is clicked - I only want the data to be passed.
Is there any way I can do this? Is using NSUserDefaults advisable for passing data of this sort?
For simplicity I will use FriendListVC and AddVC
If you are going to your AddVC from FriendListVC via a bar button item or something and your stack looks like:-
FriendListVC -> AddVC
There are two approaches you can use:-
1) Create a delegate of your friendListVC in your addVC and modify the friendListVC datasource on any changes there
2) Or, and I recommend this approach, just reload your FriendListVC datasource on it's viewWillAppear. viewWillAppear will get called even if you navigate back. Thus even if you add a deleteVC in the future and navigate back, the viewWillAppear will perform the updates and it will be independent of any other VC
Hope that helps
Use delegate for passing data between view controllers. you can find this useful
Passing data between 2 UIViewController using delegate and protocol
you can use NSUserDefaults but delegate pattern is better than this.
You can use callback method best and easy way to pass data one controller to another
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
let viewControllerB = segue.destinationViewController as! ViewControllerB
viewControllerB.callback = { message in
//Do what you want in here!
}
}
In ViewControllerB:
var callback : (String -> Void)?
#IBAction func search(sender: AnyObject) {
callback?("Pass data to view controller1")
self.dismissViewControllerAnimated(true, completion: nil)
}
The easiest way to do this is by making an instance of the view controller that you want to pass data to, in the current view controller. I will write you a sample code for this.
class yourTableViewController: UITableViewController {
var controllerToPassData: UIViewController()
func clickTableButton(sender: UIButton) {
controllerToPassData.count += 1
}
}
class controllerwhereDataisPassed: UIViewController {
var count: Int!
}
Pick the instance of the controller where you want to pass data to from the navigationController stack and use this code.
A portion of my app has an embedded master-detail section. Each detail view is using a custom UIViewController. When I change the value of something inside one of these UIViewControllers I need to be able to grey out one of the table rows in the master UITableViewController.
The closest I have seen to a solution is to use NSNotificationCenter to bubble up any changes, though this feels a little untidy..
Another solution is to use delegates? But I haven't come across any example solutions or tutorials in how to use this in Swift?
I've also experimented just trying to access the table view by navigating back up the hierarchy:
let navController = self.splitViewController!.viewControllers[0];
navController.tableView.reloadData()
I know the example above is wrong, but I don't know how to access the master view that way, or even if it is the right approach.
Oh, I am trying to call reloadData() because in the master view there is some logic which checks the condition as to wether to grey out a table row is applicable (i'm using Core Data)
I've seen that you figured this one out already. However a cleaner and more future proof way would be to use a delegate protocol:
protocol DetailViewControllerDelegate: class {
func reloadTableView()
}
Then add a delegate property to your DetailViewController class and implement the call to the delegate:
class DetailViewController: UIViewController {
weak var delegate: DetailViewControllerDelegate?
....
func reloadMasterTableView() {
delegate?.reloadTableView()
}
}
And then in your MainViewController implement the delegate method:
extension MainViewController: DetailViewControllerDelegate {
func reloadTableView() {
tableView.reloadData()
}
}
Don't forget to set the delegate on your DetailViewController instances when you create them:
let detailViewController = DetailViewController()
detailViewController.delegate = self
I would suggest you use NSNotificationCenter .
If you want to to do it via Navigation controller here is to code should work for you in swift.
let navController: UINavigationController = self.splitViewController!.viewControllers[0] as! UINavigationController
let controller: MasterViewController = navController.topViewController as! MasterViewController
controller.tableView.reloadData()
Since I was able to access my viewController, I was able to access the parent viewcontroller like so:
func reloadMasterTableView(){
let navVC: UINavigationController = self.splitViewController!.viewControllers[0] as! UINavigationController
let sectionsVC : UIMasterViewController = navVC.topViewController as! UIMasterViewController
sectionsVC.tableView.reloadData()
}
I've recently been playing around with swift segues and I'd love to incorporate one in my latest app, the problem is I can't seem to get them to work. So far I've created another view controller SecondViewController and referenced in my ViewController & SecondViewController files as so:
ViewController.swift
import UIKit
class ViewController: UIViewController {
var secondViewController: SecondViewController!
var viewController: ViewController!
override func viewDidLoad() {
//lots more code here
SecondViewController.swift
import UIKit
class SecondViewController: UIViewController {
var secondViewController: SecondViewController!
var viewController: ViewController!
override func viewDidLoad() {
Them in storyboard view I've crtl+dragged a segue from viewController to secondViewController and once that's been created given that segue an identifier using the right hand panel, the segue identifier is GameOver and the segue type is show.
Now I want to call the segue automatically with no interaction from the user, in the final app once the user hits the game over func it would trigger the segue and display a new UIView where the highscore could be displayed with a few other items.
The code I'm using to call the segue is:
self.viewController.performSegueWithIdentifier("GameOver", sender: self)
I receive the following error...
Thread 1:EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, subcode=0x0
I also have this error in the output field...
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
I've played around with the names of the segues and the file names and I still get the same error, I'm sure I'm missing something fundamental so hopefully someone can help me work this out.
I've created a new project and uploaded it to GitHub, if anyone could tell me what I'm missing that would be great, here is a link to my GitHub repository https://github.com/rich84ts/TestSingleView
Thank you.
You cannot just throw in some instance properties and expect them to magically do something:
class ViewController: UIViewController {
var secondViewController: SecondViewController!
var viewController: ViewController!
}
Those properties are nil, and sending a message to them will crash your app. You have to give them values.
In your case, the segue emanates from this view controller, so what you actually want to say is
self.performSegueWithIdentifier("GameOver", sender: self)
The other big mistake you are making is that you are saying all this in viewDidLoad. That is way too early! You can't do any segue-ing yet; your view is not even in the interface! Move your code into viewDidAppear: and it will actually work:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.performSegueWithIdentifier("GameOver", sender: self)
}
Your code is still silly and useless, but at least you will see something happen and you can continue developing from there.
What I actually recommend is that you delete your viewDidLoad implementation and put this:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
delay(1) {
self.performSegueWithIdentifier("GameOver", sender: self)
}
}
That will allow the first view controller to appear, wait one second, then summon the second view controller. And so you will learn that everything is hooked up correctly, and can proceed to do something more practical.
You can create a manual segue from the storyboard by control-clicking the ViewController object and dragging from the manual segue to the destination view controller. You can then call this segue with the designated identifier from your source controller. You don't need a reference to the destination view controller to achieve this.
To reference anything from the storyboard in your view controller you need to declare your properties like this:
#IBOutlet var someProperty : UIView?
The #IBOutlet bit makes the property visible on the storyboard and you can control-drag from it to a corresponding object in a view. You can't do this with view controllers though. To access the destination view controller in your source view controller before the segue you need to override func prepareForSegue(_ segue: UIStoryboardSegue,
sender sender: AnyObject?). This allows you to access the destination view controller from the segue-instance before the actual segue (if you need to pass it data for example).
Firstly your self.viewController is a nil object as you only created the variable and didn't initialize it. You can't call a method with nil object. Secondly you have created a push segue from storyboard but you don't have navigation controller in storyboard so self.performSegueWithIdentifier("GameOver", sender: self) will also not work. To use push segue you should have you current viewcontroller in UINavigationController's stack, so first add a UINavigationController in storyboard and make that initial view controller and set ViewController to the rootViewController of the navigation controller then call self.performSegueWithIdentifier("GameOver", sender: self)
Then the code will work. Hope this help.