I have a mapview that is connected to several viewControllers.
I needed to implement an 'unwind' from another button (as opposed to just the back button), so I used:
self.navigationController?.popViewControllerAnimated(true)
My question is: as the above works, should I even bother trying to implement unwind in code using prepareForUnwind and canPerformUnwindSegueAction in the parent view controller + ctrl-drag from the viewController to exit in Storyboard? If so, why?
Basically if you are just dimissing a presented UIViewController or popping a UIViewController from the navigation stack,you don't need to use unwind segues although it does the same for you.
But think of a case where there are UINavigationControllers A,B,C and then a RootViewController R
Then think of a condition
(Present) R -> A
(Push) AR -> XVController
(Push)XVController -> B
(Present)BR-> YVContoller
...and so on..
Now if you want to go back to YVController to your project RootViewController where you just started up.There is no way as popToRootViewController pops to the navigation stack of the B UINavigationController.Now what you do..Either use delegate or change window RootViewController which is not a good idea.
So you need to use unwind segue to overcome this.. A sample project to demonstrate the power of using it..
Related
Design mode everything is fine but in live Navigation Bar goes under the ScrollView How am I gonna solve this problem ? Please help
Again, without seeing any code at all it is hard to say, but I believe that the navigationBar isn't displaying at all. If you're presenting the UIViewController directly from another view, you're not instantiating the UINavigationController it is embedded in.
A solution would be to either create a segue in your storyboard from your presenting view to the UINavigationController, or add a storyboardId to the UINavigationController and instantiating as:
let nav = storyboard.instantiateViewControllerWithIdentifier("STORYBOARD_ID_HERE") as! UINavigationController
presentViewController(nav, animated: true, completion: nil)
If you can show some code on how the view is presented you'll receive better help, since it's impossible to know exactly what is wrong based on just one image.
As shown from the diagram, I am trying to have two viewControllers connected to the viewController on the right that serves as a menu between the viewControllers.
When the app is first run, it will load the first view controller on top. A button will show the menu modally and depending on which button is pressed (I am planning to add more) the corresponding viewController will be shown. The FIRST time that VC2 is pressed, it will load the second viewController, but when the menu is called again and VC2 is pressed, I want it to unwind instead of reload the view controller.
I have managed to unwind to the first viewController but when trying to unwind to the second viewController, the action is ignored. Your help will be greatly appreciated. Thank you.
You cannot unwind to a viewcontroller that has not been previously presented, because the desired destination VC will not be held in the navigation controller stack. Unwind segues are designed to 'pop' several steps back in a navigation history. In your scenario, the only possible pop available is to VC1.
You might also want to review your 'menu' concept, considering that UINavigationController objects can provide this behaviour with little fuss. You also don't need two separate UINavigationController objects to achieve what you want.
To do this, simply connect your menu VC as the root view controller for the UINavigationController, then connect the menu buttons to VC1 & VC2. Then you'll be able to move between the three screens with ease (see below for example).
Hi I've seen this question asked a few times already but with no definite answer yet so I created it for xcode 7 and swift2 (which may have changed things a bit anyway).
I created a project using Xcode 7 and Cocoa OSX Story boards + swift2, so my project started with a NSWindowController that Connects to a NSViewController (as expected!). I added a NSToolbar to my window controller and added a NSButton to the toolbar. I changed my NSViewController to be one of the new NSSplitViewController that links to three NSViewControllers and displays their views horizontally - with vertical dividers - (similar to the layout you see in the photo app or pages in Yosemite +). My final goal will be that the button in My toolbar shows and hides the first split.
It is my understanding is, and I would expect that to achieve this I should create an action in the NSSplitViewController that changes the auto layout constrains more or less in the way they are working it out here: How to do collapse and expand view in mac application?.
And then somehow link this action to the NSButton that is in the Toolbar... which happens to be in the NSWindowController (far up and isolated in the hierarchy)...
I have already gone through other questions about NSToolbar and storyboards and failed to accomplish my goal:
The YouTube video: Cocoa Programming L17 - NSToolbar which is the closest I found to solve the problem, but his method does not work for storyboards, only creating your own xib file.
In this question: How to use NSToolBar in Xcode 6 and Storyboard? One person proposes to make the link using the first reponder and expecting everything to hook up at run-time (which looks a bit dodgy and not the way apple would implement it I think...). A second person suggested to create a view controller variable in the NSWindowController and manipulate its properties from there... but again, a bit dodgy too.
One latest comment I saw in that question which seems the best way to tackle the problem (but still not as good as I guess it could be) is to add a NSObjectController to the dock of each scene and when the scene loads, set the values of the objects to the other secene's controller. Is this really the best way to go ahead? If so, how could I achieve this one?
Apple did mention (again) in WWDC15 that they created storyboards for osx and the split-view controller that owns view-controllers so that you can move your logic and work to the specific view-controller, so I would be expecting to do everything from inside my split-view controller as this is the target that needs to change.
Does anyone know how to achieve this from the view controller itself? I really haven't been able to find a way to connect my ToolBarItem to it.
OK, I've created this question quite a few days ago and no answer so far so I've answer with what I recently did to overcome the problem.
After I created my Xcode project I did this:
Created a subclass MySplitViewController for the NSSplitViewController
Added an IBOutlet for each NSSplitViewItem. For example:
#IBOutlet weak var mySplitViewItem: NSSplitViewItem!
Created a subclass WindowController for the NSWindowController
Added an IBAction in the WindowController class that links to the NSToolbarItem (my button)
Added a property that gets the Window Controller's content as MySplitViewController
var mySplitViewController: MySplitViewController {
return self.window?.contentViewController as! MySplitViewController
}
Now I can access the split view controller's property from the Window Controller in the action I created:
mySplitViewController. mySplitViewItem.collapsed = true
I created some sample code that does this (but using a view controller and changing the text for a label here, just in case someone wants to see a working project with this behaviour. And a blog post about it too :)
One person proposes to make the link using the first reponder and expecting everything to hook up at run-time (which looks a bit dodgy and not the way apple would implement it I think...).
I think this first responder method is actually the proper way.
As an example:
Add something similar to the following, in whichever view controller makes sense.
#IBAction func doSomething(_ sender: AnyObject?) {
print("Do something.")
}
This will magically show up in the first responder:
In your storyboard, right-click the orange "first responder" icon above your window controller, and you should see doSomething in the very long list. You just need to connect that up to your toolbar button.
In the following screen capture, you can see my "Toggle Sidebar" button is connected to the toggleSidebar action in my first responder.
I didn't even have to write this method — it's provided by NSSplitViewController:
#IBAction open func toggleSidebar(_ sender: Any?)
So, I was working this same issue and finding no solution as you experienced. I read your post and was trying to figure how I would implement your solution when it occurred to me to use a notification. In about 30 seconds, I had a perfectly fine working solution:
In your windowController add an IBAction to post a notification like so
-(IBAction)toggleMasterViewClicked:(id)sender
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"TestNotification" object:nil];
}
Hook up that action to your NSToolbarItem, then in the viewController add self as an observer for that notification like so
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(toggleMasterView:) name:#"TestNotification" object:nil];
In your case, selector would be updateMyLabelText
I don't really see any downside here. No reference to other objects needed, no dependancies. Works flawlessly for me
While connectiong IBActions works by using either the First Responder or by adding an "Object" to the scene, then changing its class to the window's view controller class, this doesn't help with IBOutlets and delegates that you'd like to point to the view controller.
Here's a work-around for that:
Add the Toolbar to the View Controller, not to its Window. That way, you can make all the IBOutlet connections in the View Controller Scene easily. I've done that for years and found no issues with it, even when using Tabs.
You'll have to assign the window's toolbar in code, then. E.g. like this:
#interface ViewController ()
#property (weak) IBOutlet NSToolbar *toolbar; // connect this in your storyboard to the Toolbar that you moved to the View Controller Scene
#end
- (void)viewWillAppear {
[super viewWillAppear];
self.view.window.toolbar = self.toolbar;
}
I started learning Objective C and programming late last year and I’m dealing with Core Data, so I need some help.
I’m trying to select an object in VC3 and pass it to VC1 when the ‘Select’ button is pressed. Once pressed then it pops back to VC1 and display the object in the cell. I don’t think I can use a segue, because I want to go back and not forward. Also, I'm not going back to the root view controller.
Everything else works with segues, protocols and delegates except this issue. I’ve tried delegates, but they don’t seem to work, perhaps because there’s a view controller in between them or something. However, I’m very new to programming so I could be wrong.
I would appreciate some help and guidance.
The modern way (ios6+) to do this is with an unwind segue
You create one of these by defining a method that takes a single argument of a storyBoardSegue.
- (IBAction)unwindOnSelect:(UIStoryboardSegue *)segue;
Add this method to the viewController you want to unwind to.
Then, in the storyboard scene you want to unwind from... CTRL-drag from your 'Select' button to the green 'exit' symbol in the bar at the bottom of the scene. The unwind segue should appear in the popup as an option. Select it. Your button will unwind you to that destination, and code in the unwind segue method will be executed.
The segue object holds a reference to both source and destination viewControllers, so that is how you can get the data from one to the other.
- (void)unwindOnSelect:(UIStoryboardSegue *)segue
{
self.myObject = segue.sourceViewController.myProperty;
}
You will probably get an error here, "no known instance method for selector "myProperty". One way round this is to #include "viewController3" at the top of VC1's .h or .m file. You may have to typecast as well:
self.myObject = [(ViewController1*)segue.sourceViewController myProperty];
Another way is a bit of indirection:
if ([segue.sourceViewController respondsToSelector:#selector(myProperty)]) {
self.myObject = [segue.sourceViewController performSelector:#selector(myProperty)];
}
This has the benefit that you do not have to #include "viewController3", so you are a bit more decoupled, a bit more Objectified.
Is it possible to have an IBAction link to a button in view controller A and run a command on view controller B? Thanks!
This is for iOS.
From my understanding, you should not act from one UIViewController to another one ... this does not sounds like a valid pattern and a valid way of computing stuff. Note that the ViewControllers should contain only code related to the View interaction and should NOT contain business related code.
I guess you want to do this kind of stuff because UIViewController B has already been opened and is on the navigation queue. Then you want to act on content which is common to both VC A and VC B.
You should then consider having a business class that will compute the task when IBAction will be triggered. Then if you need to refresh any content when getting back to the UIViewController B you should handle it within the UIViewController life cycle : viewWillAppear would be a nice choice ( cause viewDidLoad won't get fired again if the view has already been loaded ).
Hope this helps.