Not able to show View Container in Container View in iOS - ios

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)

Related

Displaying text entered into textfield (in second view controller) in a label on first view controller?

I have been trying a challenge out alone in Swift to do programmatically - with no use of storyboards.
My first view controller has a label and a button to go to the second view controller.
I created a navigation controller within the app delegate file, and then push to the second view controller when the button is pressed.
The second view controller has a textfield, a button to save the text, and a button to go back to the first view controller.
The UI is fine, but I cannot figure out how to save the text that is entered into the textfield which is on the second view controller, and then display this text in the label in the first view controller.
Can someone give me a clue as to how to do this?
Currently in my button going back to the first view controller I have this:
func handleBackToVC1() {
self.navigationController?.popViewController(animated: true)
}
Is this where I pass the data that is in the textfield and display it in the label?
Also, should I give the label some text to start off with? But which would then get changed to what was entered into the textfield?
use protocol delegate for passing data from SecondViewController to FirstViewController
protocol TextFieldDataDelegate: class {
func saveText(_ text: String)
}
class SecondViewController: UIViewController {
weak var delegate: TextFieldDataDelegate?
//on save button tap call the delegate method
delegate?.saveText(textField.text)
}
//set FirstViewController as delegate for TextFieldDataDelegate protocol
class FirstViewController: UIController, TextFieldDataDelegate {
// when you create instance of SecondViewController assign the delegate as self
let secondVC = SecondViewController()
secondVC.delegate = self
//in saveText set label text as passed from SecondVC
func saveText(_ text: String) {
self.textLabel.text = text
}
}
Note: There are many similar questions like this, always search before asking the question. Thanks

Xcode 8/Swift 3: how to make ViewController save state when segue occurs?

App has two View Controllers: ViewController (this is the main View Controller that displays the majority of the app's content) and SecondViewController (accessible via a UIButton on ViewController; SecondViewController is only used to display a user's inventory, and a UIButton within SecondViewController allows the user to return to the original view, ViewController). Currently, the app uses the "Show" action segue to switch between View Controllers when the user presses the appropriate UIButton. However, after switching from ViewController to SecondViewController, and then pressing the UIButton to return to ViewController, the properties of ViewController have been reverted to the properties that occur when the app launches (background color is changed, certain text fields appear that shouldn't).
So, how do I "save the state" of ViewController when the user moves to SecondViewController, so that the user resumes where they left off when they return to ViewController?
What you are looking for is an unwind segue. Here's the simplest way of how to create it:
In your ViewController (or, basically any other view controller you are willing to pop to) create an IBAction that accepts an instance of a segue (function name doesn't really matter):
#IBAction func unwindToThisVC(segue: UIStoryboardSegue) { }
In the storyboard, go to SecondViewController, and control + drag from your UIButton to the Exit outlet of ViewController and then select the IBAction you've created in step 1:
More on Unwind Segues
The way you are doing it now (using Show from the second to get back to the first) actually brings up a third VC.
What you want to do is dismiss the second view controller.
The normal way is to implement a protocol for the second one that the first one implements and then to have a function in that protocol for the second one to let the first one know it is done.
When the function is called, the first one dismisses the second and then it will be shown again with its state intact.
Here is a simple example of segue and unwind that you can adapt to your problem... Assume that you have ViewController with label and a button and a SecondViewController with label and a button.
For the first ViewController...
import UIKit
//steps to receive data back from SecondViewController...
//1. create protocol in the SecondViewController (see SecondViewController code)
//2. conform to the protocol
class ViewController: UIViewController, UnwindSegue {
//3. method that gets triggred.
func dataReceived(dataSegued: String) {
labelOne.text = dataSegued
}
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var labelOne: UILabel!
var textReceived : String = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btPressed(_ sender: Any) {
performSegue(withIdentifier: "goToSecondController", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToSecondController" {
let destinationVC = segue.destination as! SecondViewController
destinationVC.textSegued = textField.text!
//4. create delegate in the SecondViewController (see SecondViewController code)
//5. set ourselves up as delegate of SecondViewController
destinationVC.delegate = self
//6. then dismiss the SecondViewController (see SecondViewController code)
}
}
}
Then for your SecondViewController...
import UIKit
//1. create protocols and delegates to transfer data back
protocol UnwindSegue {
//single required method with a single parameter
func dataReceived(data:String)
}
class SecondViewController: UIViewController {
var textSegued : String?
//4. create delegate of the protocol of type CanReceive that can be a nil. If it is nil, it doesn't go anywhere when BT is pressed
var delegate : UnwindSegue?
#IBOutlet weak var label: UILabel!
#IBOutlet weak var secondTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
label.text = textSegued
}
#IBAction func btTwoPressed(_ sender: Any) {
//this is not triggered if var delegate is nil (as defined as optional)
delegate?.dataReceived(data: secondTextField.text!)
//6. dismiss the 2nd VC so you can see the fist VC
dismiss(animated: true, completion: nil)
}
}

Insert to tableViewCell when button pressed in another view controller

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.

error performing segue in Swift iOS

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.

Pushing to a new Viewcontroller from an inactive Viewcontroller (programatically)

Short explanation.
I have a ContainerViewController that I'm pushing to the navigationStack.
The ContainerViewController has 2 child ViewControllers. A SlidePanelViewController (a slide-out menu) and a CenterViewController (the content)
I have a button in my menu to "sign Out". When this button is clicked I want to push ContainerViewController (and it's 2 childViewControllers) to my LandingPageViewController.
Here's the function I am trying to call:
func signOut() {
println("signOut")
// Set up the landing page as the main viewcontroller again.
let mainTableViewController = LandingPageVC()
mainTableViewController.navigationItem.setHidesBackButton(true, animated: false)
mainTableViewController.skipView = false
self.navigationController!.pushViewController(mainTableViewController, animated: true)
// Disable menu access
menuEnabled = false
// change status bar style back to default (black)
UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.Default
}
At first I tried putting this in my SlidePanelViewController. That didn't work. So I put it where I'm assuming it belongs in the ContainerViewController.
However when I click my signOutButton in my menu. I'm presented with the error:
fatal error: unexpectedly found nil while unwrapping an Optional value
When looking into the error. This is the line causing it:
self.navigationController!.pushViewController(mainTableViewController, animated: true)
After the error I checked that the function works, by adding a UINavigationBarButtonItem that called the function (in my ContainerViewController). It did exactly what I wanted.
However when I call this function from my Menu (again my menu is a childViewController of the ContainerViewController). It does not work.
I'm attempting to call it like so:
ContainerViewController().signOut()
I also tried adding a Delegate to my SidePanelViewController like this:
Before the class:
#objc protocol SidePanelViewControllerDelegate {
optional func needsSignOut(sender: SidePanelViewController)
optional func toggleLeftPanel()
optional func collapseSidePanels()
}
in viewDidLoad():
// Make sure your delegate is weak because if a ContainerViewController owns
// a reference to a SidePanelViewController and the container view controller
// is its delegate, you'll end up with a strong reference cycle!
weak var delegate: SidePanelViewControllerDelegate?
in my tap gesture function:
func signOutTapGesture() {
println("signOutTapGesture")
selectView(signOutView)
delegate?.needsSignOut?(self)
println(delegate)
}
before my ContainerViewController class:
var leftViewController: SidePanelViewController?
my ContainerViewController class:
class ContainerViewController: UIViewController, CenterViewControllerDelegate, SidePanelViewControllerDelegate, UIGestureRecognizerDelegate {
in my ContainerViewController's viewDidLoad()
leftViewController?.delegate = self
And I changed the signOut function in the ContainerViewController class to this:
func needsSignOut(sender: SidePanelViewController) {
println("needsSignOut called")
self.signOut()
}
However using the delegate like above, doesn't seem to do anything either.
Any help as to How I can successfully push my LandingPageVC from the menu would be greatly appreciated! (I'm not using storyboards)
You're attempting to call signOut with ContainerViewController().signOut(). This will create a new ContainerViewController and because you haven't pushed it onto the navigation controller's stack, navigationController is nil. Try just calling self.signOut(). (I'm assuming signOut in a method of ContainerViewController)
Update - delegates
Your delegate property should go in SidePanelViewController. I'll give you and example of how to implement it:
SidePanelViewController:
(Note - the protocol doesn't have to go here but I think it keeps things organised)
#objc protocol SidePanelViewControllerDelegate {
optional func needsSignOut(sender: SidePanelViewController)
}
class SidePanelViewController: UIViewController {
// Make sure your delegate is weak because if a ContainerViewController owns
// a reference to a SidePanelViewController and the container view controller
// is its delegate, you'll end up with a strong reference cycle!
weak var delegate: SidePanelViewControllerDelegate?
// Called when the UIButton is pressed.
func myButtonWasPressed() {
delegate?.needsSignOut?(self)
}
}
ContainerViewController:
class ContainerViewController: UIViewController {
var sidePanel: SidePanelViewController!
// Setup the side panel...
override func viewDidLoad() {
super.viewDidLoad()
sidePanel.delegate = self
}
func signOut() {
// Sign out stuff here.
}
}
// The ContainerViewController needs to conform to the SidePanelViewControllerDelegate
// protocol if we want the delegate to work. (This could have gone in the initial
// class declaration.)
extension ContainerViewController : SidePanelViewControllerDelegate {
func needsSignOut(sender: SidePanelViewController) {
self.signOut()
}
}
Hope that helps.
The problem seems to be that navigationController is nil and you're trying to force unwrap it (as indicated by your error).
One problem I discussed in my other answer.
Another problem may be you haven't added a navigation controller. To do this you need to:
If you're using Storyboards
You need to make sure you've embedded your UINavigationController. After that, when you use navigationController it won't be nil and you'll be able to push your view controller.
When you're on your storyboard:
Also, if you're using storyboards, have you considered using segues to move around instead of calling presentViewController? I've found it makes everything much easier.
If you're not using Storyboards
Have a look at this post: Programatically creating UINavigationController in iOS

Resources