I'm trying to create a custom segue. It doesn't work in the first time. I found a solution afterward by creating a new file extends UIStoryboardSegue and create a method called "perform". It works right now without using prepareSegue in ViewController. I'm copied my previous codes from preparedSegue to "Perform" func in new UIStoryboardSegue file. It print out the message but the delegate doesn't work.
View Controller
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Custom Segue
class CustomSegue: UIStoryboardSegue {
let transitionManager = TransitionManager()
override func perform() {
NSLog("Perform");
let toViewController = self.destinationViewController as UIViewController
toViewController.transitioningDelegate = self.transitionManager
}
}
I placed breakpoint in every func in Transition Manager, none of them execute and stop.
Segue settings:
Problem: TransitioningDelegate is not working
Full sources codes: here
The problem is that your perform implementation doesn't do anything:
override func perform() {
let toViewController = self.destinationViewController as UIViewController
toViewController.transitioningDelegate = self.transitionManager
}
All you do is create a view controller, give it a transitioning delegate, and then throw it away. You do not perform any transition! But that is exactly the job of a segue. It isn't clear what you can possibly be expecting to happen.
If the idea is that this is supposed to be a present (modal) segue, then you should make it a present (modal) segue in the storyboard, specify your custom segue class, and then, in your perform implementation, call super to do the actual presentation:
override func perform() {
let toViewController = self.destinationViewController as UIViewController
toViewController.transitioningDelegate = self.transitionManager
super.perform() // this will work, _if_ you have specified a present (modal) segue
}
Alternatively, your perform could perform the presentation itself, by calling presentViewController:... on the source view controller with the destination view controller as parameter.
But your perform does nothing. Nothing will come of nothing.
Related
SCENARIO
Xcode 11.5, Swift 5
Using Core Data
User wants to update their profile. VC2 is dismissed after user taps save. VC1 area highlighted in yellow should reflect the change.
PROBLEM
Data is being saved correctly. However, VC1 elements highlighted in yellow doesn't automatically update. If I go to another tab then come back, the view elements refresh with the updated changes.
MY CODE
I have a setupUI() method that lays out the elements and have tried adding it to VC1's viewWillAppear method, but no luck.
//VC1:
override func viewDidLoad() {
super.viewDidLoad()
fetchUser()
setupUI()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
setupUI()
}
viewWillAppear is not called when you dimiss the modal that fill the data you need to use a delegate
1- When you show the modal vc
let vc = SomeVC()
vc.delegate = self // declare property delegate inside the modal of that type / protocol
// present
2- when you dimiss the modal
self.delegate?.setupUI()
// dimiss
You could use a delegate method to perform some changes in VC1 in response to some action in VC2. In this case you will set the delegate in VC1 and call the delegate method in VC2. Ideal place to make this call would be in completion block of dismiss.
//VC1
public protocol MyProtocol: class {
func delegateMethod()
}
In the viewDidLoad method set the delegate for VC2
override func viewDidLoad() {
super.viewDidLoad()
fetchUser()
setupUI()
//VC2 is the instance of view controller you are going to push from this page
VC2.delegate = self
}
Make sure VC1 confirms to MyProtocol protocol
extension VC1: MyProtocol {
func delegateMethod() {
// reload view here
}
}
Declare the delegate in VC2
//VC2
var delegate: MyProtocol?
Then call the delegate method in completion of dismiss
self.dismiss(animated: false, completion: {
self.delegate?.delegateMethod()
})
Alternatively you could use observers to respond to any changes as well, but that might be an overkill. Check out this article, they discuss the whole thing in detail.
setting vc2.modalPresentationStyle = .fullScreen will solve it without the need to make any delegates.
I have gone through all the other posts about this topic but they don't seem to help me.
I have a UITabBarController that is launching two tabs. I want to pass data collected in tab1 to the UITabBar ViewController. I am trying to use delegete protocol for this but I am having trouble setting the delegate variable in the sending ViewController. The prepare for segue never gets called. I cannot even cycle through the viewcontrollers of the tabs inside the ViewDidLoad() of the Tabbar controller as they are not created yet and so nil.
I have used delegates before and it seems rather straightforward. Does it matter that I am using it in a Tabbar?
When I run the code the viewDidLoad() in TabBarViewController is called but not the preparefor segue.
The IBAction donePressed in the MeViewController is called but the delegate is not called as its not set.
Here is the code --
protocol DetailsDelegate: class {
func myDetailsGathered( myDetails: MyDetails )
}
/// RECEIVING VIEW CONTROLLER
class TabBarViewController: UITabBarController, DetailsDelegate
{
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
print("prepare for segue called\n");
if let destinationViewController = segue.destination as? MeViewController
{
destinationViewController.delegate = self
}
}
override func viewDidLoad()
{
print("ViewDidLoad Called \n")
}
func myDetailsGathered(myDetails: MyDetails)
{
self.myDetails = myDetails
print("My details gathered \n")
}
}
---------------
/// SENDING VIEW CONTROLLER
class MeViewController: UIViewController
{
weak var delegate: DetailsDelegate?
override func viewDidLoad()
{
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
// I have UIButton in the view and this is invoked when its pressed.
#IBAction func donePressed(_ sender: Any)
{
var infoToPass = MyDetails()
print("looks like we are done")
delegate?.myDetailsGathered(infoToPass: myDetails)
}
}
prepareForSegue is called when you perform a segue. Which you don´t do and that´s why it does not get called.
A segue defines a transition between two view controllers in your
app’s storyboard file.
You should use a singleton class to store variables and access them between different controllers. You declare one like this:
class Singleton {
static let sharedInstance = Singleton()
var name = ""
}
Assign to Singleton:
Singleton.sharedInstance.name = "Some name"
To read from it from whatever controller:
let name = Singleton.sharedInstance.name
First of all, why do you want your tabbarController to receive some info/data though?
The prepare for segue never gets called.
prepareForSegue method will be invoked right after the performSegue. So where's your performSegue method? Or are you sure that that kind of segue going to MeViewController is being performed?
One more option you have is to use NotificationCenter.
I have a view controller which is nested inside of another view controller using a container view. Is it possible for me to segue from the view which is currently in the container view and replace it with another view controller in the same container view. I.e. the content that is around the container view is not removed by another view controller taking up the entire view.
Yes it is. You can read about that in the Apple Docs.
Considering your containerView currently only has one viewcontroller, here is a very basic example:
func loadVCWithId(idToLoad: String){
childViewControllers[0].willMoveToParentViewController(nil)
childViewControllers[0].view.removeFromSuperview()
childViewControllers[0].removeFromParentViewController()
let secondViewController = self.storyboard?.instantiateViewControllerWithIdentifier(idToLoad)
UIView.transitionWithView(yourContainer, duration: 0.5, options: UIViewAnimationOptions.TransitionFlipFromRight, animations: {self.yourContainer.addSubview((secondViewController?.view)!)}, completion: nil)
secondViewController!.view.frame = firstContainer.bounds
// do initialization of secondViewController here
secondViewController?.didMoveToParentViewController(self)
}
loadVCWithId(idToLoad:String)is a method within your host viewcontroller.
In this code fragment I delete the current content of the container (probably not the best way to just access index 0, but for the sake of this example, this should be enough), instantiate a new ViewController by ID (this one is present in my storyboard but not accessbile yet), animate the transition and actually add the new VC to the container.
Hope this helps.
this my solution maybe helpful for
first i create a protocol on childViewController
protocol ChildViewControllerDelaget
{
func performForSegue(SegueIdentifier:String)
}
class ChildViewController: UIViewController {
var delaget:ChildViewControllerDelaget?
override func viewDidLoad() {
super.viewDidLoad()
}
init()
{
}
#IBAction func myAction(sender: AnyObject) {
if delaget != nil {
deleget.performForSegue("mySegueIdentifier")
}
}
and on MainViewController
class ViewController: UIViewController,ChildViewControllerDelaget {
override func viewDidLoad()
{
super.viewDidLoad()
let child = ChildViewController()
child.delaget = self
}
func performForSegue(segueIdentifier:String)
{
self.performSegueWithIdentifier(segueIdentifier, sender: nil)
}
}
My delegate protocol never called
My first controller - ViewController
class ViewController: UIViewController,testProtocol {
#IBAction func btInit(sender: AnyObject) {
println("Bt Init")
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initViewController: UIViewController = storyBoard.instantiateViewControllerWithIdentifier("viewTarget") as targetViewController
self.presentViewController(initViewController,animated: false, nil)
}
var targetController = targetViewController();
override func viewDidLoad() {
super.viewDidLoad()
self.targetController.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func testDelegate(){
println(" in my view controller delegate ")
}
}
In my second view controller - targetViewController
protocol testProtocol {
func testDelegate() // this function the first controllers
}
class targetViewController: UIViewController {
#IBAction func BtTarget(sender: AnyObject) {
println("bt target pressed")
delegate?.testDelegate()
}
var delegate : testProtocol?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func testDelegate(){
println(" in my target view controller delegate ")
}
}
Why is testDelegate() never called on ViewController? What am I doing wrong? Thanks.
I have read a lot of posts about this, but all of the examples are given with segue transition, and I don't want use a segue.
Typically you set a new view controller's delegate property in prepareForSegue:. You said you're not using a segue, so you'll need to instantiate the second view controller and present it somehow. You can do this by doing something like:
let storyboard = UIStoryboard(name: "AStoryboardName", bundle: nil)
let secondVC = storyboard.instantiateViewControllerWithIdentifier(anIdentifier) as! targetViewController
secondVC.delegate = self
presentViewController(secondVC, animated: true, completion: nil)
You have a testDelegate() method in both view controllers, but you only want it in the first view controller. Then your second view controller can call delegate?.testDelegate() at the appropriate time.
Finally, you typically want to make delegate properties weak, so I would recommend changing var delegate : testProtocol? to weak var delegate: testProtocol?
I would read up on delegation. Here is a relatively simple 5 step process to delegation that may help you:
Delegation in 5 Steps:
object A is the delegate for object B, and object B will send out the messages:
Define a delegate protocol for object B.
Give object B an optional delegate variable. This variable should be weak.
Make object B send messages to its delegate when something interesting happens, such as the user pressing the Cancel or Done buttons, or when it needs a piece of information.
Make object A conform to the delegate protocol. It should put the name of the protocol in its class line and implement the methods from the protocol.
Tell object B that object A is now its delegate (in prepareForSegue(sender)).
Is it possible to use segues in Views of ViewControllers instantiated with instantiateViewControllerWithIdentifier?
Here is a minimal example of what I'm trying to do (I need something like that in a biger project):
In the Main Storyboard I have a rootViewController, a secondViewController with the StoryboardID "secondViewControllerID" and a thirdViewController. The secondViewController is connected with the thirdViewController via button through a show-Segue.
In the class of the rootViewController I instantiate the secondViewController and set its view as subview of my rootViewController:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let storyboard = UIStoryboard(name: "Main", bundle: nil);
var vc = storyboard.instantiateViewControllerWithIdentifier("secondViewControllerID") as UIViewController
self.view.addSubview(vc.view)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
When I now execute the programm I see correctly the subview (secondViewController). But now the segue doesn't work anymore (when I click on the button I don't get to the thirdViewController)
Does anyone has an idea why?
(It is not possible in my project just to use the secondViewController without the instantiateViewControllerWithIdentifier because the secondViewController is part of a pageViewController that is managing its viewControllers via instantiateViewControllerWithIdentifier )
You are adding one view controller's view as a subview of another view controller. But by default, taps and gestures are handled by your main view controller and are not passed on to the subsidiary VC - hence your problem. There are some functions you need to call to get this to work. Take a look at the Apple Docs here.
Look at the section on "Creating Custom Container View Controllers". In summary, you need to wrap your addSubview method call with self.addChildViewController(vc) and vc.didMoveToParentViewController(self) function calls.