Connecting drill-down NSSplitView outlets using Storyboards - objective-c

I have an NSSplitView that manages a drill-down hierarchy. The parent/left side displays groups, while the child/right side receives notification that the group selection has changed, and updates to display the child items.
However: When creating an NSSplitView using storyboards, there are 3 scenes created: One for the split view itself, and one for each of the right/left NSViewController instances.
The problem here is that I have two controllers that are also acting as NSTableViewDataSource items, and the parent controller should have an IBOutlet to the child controller so that it can provide direct notification that the selection has changed.
But! Because these controllers are in different scenes, I cannot connect them. Nor can I move them both up to the parent scene for the split view, because they would then not have access to the NSTableView outlets. (Nor would the tables reference the controllers as delegates/datasources.)
Do I need to use NSNotification here? It seems so indirect and wishy-washy, and I haven't found a segue-based approach on the Mac.

The new NSSplitViewController and NSTabViewController class are what Apple is calling container controllers. you can get references to other controllers within the container using the new property's in NSViewController
#property (readonly) NSViewController *parentViewController
#property (copy) NSArray *childViewControllers
so for example if you would like to get a reference to your child Table controller from your parent table controller. In your parent table controller viewDidLoad you can do the following
myChildTableController = [self.parentViewController childViewControllers][1];
or vice versa from your child table controller
myParentTableController = [self.parentViewController childViewControllers][0];

As you say, Storyboards on OS X (and iOS before that), won't let you connect IBOutlets between scenes so you need to convey the actions in some other way.
I would configure the actions/delegates properties on the awakeFromNib method of any of the three controllers. At this moment the NSSplitView and its child controllers will all be created and connected.
I wouldn't use notifications when there are no multiple target objects, or the target objects are known and fixed, as in your case.

You don't necessarily want to use an NSSplitViewController. Instead, just drag an NSSplitView into the main view of your Storyboard.
As Cory explained in his answer, the new NSSplitViewController and NSTabViewController have methods for accessing parent and child controllers. This is useful if you really do want to divide your interface into different Storyboard scenes.
In your example though, you want all of the views to be in the same scene so that you can connect up all their IBOutlets to the same view controller. So, just use an NSSplitView and don't bother with an NSSplitViewController at all.

I was having the same problem and have found a workaround. It is not extremely elegant and it does not allow you to connect outlets with storyboard, but it does allow you to create relationships identical to those with IBOutlets.
The key is to implement a singleton class:
import Foundation
import Cocoa
class SplitViewConnector {
static let sharedInstance = SplitViewConnector()
var masterController:NSViewController?
var detailController:NSViewController?
}
Now in awakeFromNib method of your master controller you can set a master relationship and, similarly, you can set a relationship for your detail controller.
If, for example, your master controller needs to have a direct relationship to your detail controller you can also implement it. Assuming that the class of your master controller is YourViewController and the property for accessing your details controller is called myDetail it would look like:
var masterController:YourViewController? {
didSet {
self.assignDirectConnections()
}
}
var detailController:NSViewController? {
didSet
self.assignDirectConnections()
}
}
func assignDirectConnections() {
if let master = self.masterController {
if let detail = self.detailController {
master.myDetail = detail
}
}
}

Related

How to add a subview that has its own UIViewController in Objective-C?

I am struggling with subviews that have their own UIViewControllers. I have a UIViewController with a view (light pink) and two buttons on a toolbar. I want blue view to display when the first button is pressed and the yellow view to display with the second button is pressed. Should be easy if I just wanted to display a view. But the blue view will contain a table, so it needs it's own controller. That was my first lesson. I started off with this SO question where I learned I needed a controller for the table.
So, I am going to back up and take some baby steps here. Below is a picture of a simple starting point with my Utility ViewController (the main view controller) and the other two controllers (blue and yellow). Imagine that when the Utility ViewController (the main view) is first displayed the blue (default) view will be displayed where the pink view is located. Users will be able to click the two buttons to go back and forth and the pink view will NEVER be displayed. I just want the blue view to go where the pink view is and the yellow view to go where the pink view is. I hope this makes sense.
I'm trying to use addChildViewController. From what I have seen, there are two ways to do this: The Container View in the storyboard or addChildViewController programmatically. I want to do it programmatically. I don't want to use a NavigationController or a Tab bar. I just want to add the controllers and shove the correct view into the pink view when the associated button is pressed.
Below is the code I have so far. All I want to do is display the blue view where the pink view is. From what I have seen I should be able to just addChildViewController and addSubView. This code is not doing that for me. My confusion is getting the better of me. Can somebody help me get the blue view displayed where the pink view is?
This code is not intended to do anything other than display the blue view in viewDidLoad.
IDUtilityViewController.h
#import <UIKit/UIKit.h>
#interface IDUtilityViewController : UIViewController
#property (strong, nonatomic) IBOutlet UIView *utilityView;
#end
IDUtilityViewController.m
#import "IDUtilityViewController.h"
#import "IDAboutViewController.h"
#interface IDUtilityViewController ()
#property (nonatomic, strong) IDAboutViewController *aboutVC;
#end
#implementation IDUtilityViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.aboutVC = [[IDAboutViewController alloc]initWithNibName:#"AboutVC" bundle:nil];
[self addChildViewController:self.aboutVC];
[self.aboutVC didMoveToParentViewController:self];
[self.utilityView addSubview:self.aboutVC.aboutView];
}
#end
--------------------------EDIT------------------------------
The self.aboutVC.aboutView is nil. But I wired it up in the storyboard. Do I still need to instantiate it?
This post dates from the early days of modern iOS. It is updated with current information and the current Swift syntax.
In iOS today "everything is a container view". It is the basic way you make apps today.
An app may be so simple that it has just the one screen. But even in that case, each "thing" on the screen is a container view.
It's this easy...
version notes
2020. These days you usually just load a container view from a separate storyboard, which is dead easy. It's explained at the bottom of this post. If you are new to container views, maybe familiarize with the 'classic style' ('same storyboard') container tutorial first.
2021. Updated syntax. Used SO's new '###' pretty headlines. More detail on loading from code.
(A) Drag a container view in to your scene...
Drag a container view into your scene view. (Just as you would drag in any element such as a UIButton.)
The container view is the brown thing in this image. It is actually inside your scene view.
When you drag a container view into your scene view, Xcode automatically gives you two things:
You get the container view inside your scene view, and,
you get a brand-new UIViewController which is just sitting around somewhere on the white of your storyboard.
The two are connected with the "Masonic Symbol Thing" - explained below!
(B) Click on that new view controller. (So that's the new thing Xcode made for you somewhere on the white area, not the thing inside your scene.) ... and, change the class!
It's really that simple.
You're done.
Here's the same thing explained visually.
Notice the container view at (A).
Notice the controller at (B).
Click on B. (That's B - not A!)
Go to the inspector at the top right. Notice it says "UIViewController"
Change it to your own custom class, which is a UIViewController.
So, I have a Swift class Snap which is a UIViewController.
So where it says "UIViewController" in the Inspector I typed in "Snap".
(As usual, Xcode will auto-complete "Snap" as you start typing "Snap...".)
That's all there is to it - you're done.
How to change the container view - say, to a table view.
So when you click to add a container view, Apple automatically gives you a linked view controller, sitting on the storyboard.
Currently (2019) it happens to make it a UIViewController by default.
That's silly: it should ask which type you need. For example, often you need a table view.
Here's how to change it to something different:
At the time of writing, Xcode gives you a UIViewController by default. Let's say you want a UICollectionViewController instead:
(i) Drag a container view in to your scene. Look at the UIViewController on the storyboard which Xcode gives you by default.
(ii) Drag a new UICollectionViewController to anywhere on the main white area of the storyboard.
(iii) Click the container view inside your scene. Click the connections inspector. Notice there is one "Triggered Segue". Mouse over the "Triggered Segue" and notice that Xcode highlights all of the unwanted UIViewController.
(iv) Click the "x" to actually delete that Triggered Segue.
(v) DRAG from that Triggered Segue (viewDidLoad is the only choice). Drag across the storyboard to your new UICollectionViewController. Let go and a pop-up appears. You must select embed.
(vi) Simply delete all of the unwanted UIViewController. You're done.
Short version:
delete the unwanted UIViewController.
Put a new UICollectionViewController anywhere on the storyboard.
Control-drag from the container view's Connections - Trigger Segue - viewDidLoad, to, your new controller.
Be sure to select "embed" on the popup.
It's that easy.
Entering the text identifier...
You will have one of these "square in a square" Masonic symbol things: it is on the "bendy line" connecting your container view with the view controller.
The "masonic symbol" thing is the segue.
Select the segue by clicking on the "masonic symbol" thing.
Look to your right.
You MUST type in a text identifier for the segue.
You decide on the name. It can be any text string. A good choice is often "segueClassName".
If you follow that pattern, all your segues will be called segueClockView, seguePersonSelector, segueSnap, segueCards and so on.
Next, where do you use that text identifier?
How to connect 'to' the child controller...
Then, do the following, in code, in the ViewController of the whole scene.
Let's say you have three container views in the scene. Each container view holds a different controller, say "Snap", "Clock" and "Other".
Latest syntax
var snap:Snap?
var clock:Clock?
var other:Other?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "segueSnap")
{ snap = (segue.destination as! Snap) }
if (segue.identifier == "segueClock")
{ clock = (segue.destination as! Clock) }
if (segue.identifier == "segueOther")
{ other = (segue.destination as! Other) }
}
It's that simple. You connect a variable to refer to the controllers, using the prepareForSegue call.
How to connect in the 'other direction', up to the parent...
Say you're "in" the controller which you have put in a container view ("Snap" in the example).
It can be a confusing to get to the "boss" view controller above you ("Dash" in the example). Fortunately, it is this simple:
// Dash is the overall scene.
// Here we are in Snap. Snap is one of the container views inside Dash.
class Snap {
var myBoss:Dash?
override func viewDidAppear(_ animated: Bool) { // MUST be viewDidAppear
super.viewDidAppear(animated)
myBoss = parent as? Dash
}
Critical: Only works from viewDidAppear or later. Will not work in viewDidLoad.
You're done.
Important: that only works for container views.
Tip, don't forget, that only works for container views.
These days with storyboard identifiers, it's commonplace to just pop new views on the screen (rather as in Android development). So, let's say the user wants to edit something...
// let's just pop a view on the screen.
// this has nothing to do with container views
//
let e = ...instantiateViewController(withIdentifier: "Edit") as! Edit
e.modalPresentationStyle = .overCurrentContext
self.present(e, animated: false, completion: nil)
When using a container view, IT IS GUARANTEED that Dash will be the parent view controller of Snap.
However that is NOT NECESSARILY THE CASE when you use instantiateViewController.
Very confusingly, in iOS the parent view controller is not related to the class which instantiated it. (It might be the same, but usually it is not the same.) The self.parent pattern is only for container views.
(For a similar result in the instantiateViewController pattern, you have to use a protocol and a delegate, remembering that the delegate will be a weak link.)
Note though that these days it's pretty easy to dynamically load a container view from another storyboard - see last section below. It's often the best way.
prepareForSegue poorly named...
It's worth noting that "prepareForSegue" is a really bad name!
"prepareForSegue" is used for two purposes: loading container views, and, segueing between scenes.
But in practice, you very rarely segue between scenes! Whereas almost every app has many, many, container views as a matter of course.
It would make more sense if "prepareForSegue" was called something like "loadingContainerView".
More than one...
A common situation is: You have a small area on the screen, where you want to show one of a number of different view controllers. For example, one of four widgets.
The simplest way to do this: just have four different container views all sitting inside the same identical area. In your code, simply hide all four and turn on the one you want visible.
Easy.
Container views "from code" ...
... dynamically load a Storyboard in to a container view.
2019+ Syntax
Say you have a storyboard file "Map.storyboard", storyboard ID is "MapID", and the storyboard is a view controller for your Map class.
let map = UIStoryboard(name: "Map", bundle: nil)
.instantiateViewController(withIdentifier: "MapID")
as! Map
Have an ordinary UIView in your main scene:
#IBOutlet var dynamicContainerView: UIView!
Apple explain here the four things you have to do to add a dynamic container view
addChild(map)
map.view.frame = dynamicContainerView.bounds
dynamicContainerView.addSubview(map.view)
map.didMove(toParent: self)
(In that order.)
And to remove that container view:
map.willMove(toParent: nil)
map.view.removeFromSuperview()
map.removeFromParent()
(Also in that order.) That's it.
Note however in that example, the dynamicContainerView is simply a fixed view. It does not change or resize. This would only work if your app never rotates or anything else. Usually, you would have to add the four usual constraints to simply keep the map.view inside dynamicContainerView, as it resizes. In fact, here is the "world's handiest extension" which one needs in any iOS app,
extension UIView {
// it's basically impossible to make an iOS app without this!
func bindEdgesToSuperview() {
guard let s = superview else {
preconditionFailure("`superview` nil in bindEdgesToSuperview")
}
translatesAutoresizingMaskIntoConstraints = false
leadingAnchor.constraint(equalTo: s.leadingAnchor).isActive = true
trailingAnchor.constraint(equalTo: s.trailingAnchor).isActive = true
topAnchor.constraint(equalTo: s.topAnchor).isActive = true
bottomAnchor.constraint(equalTo: s.bottomAnchor).isActive = true
}
}
Thus, in any real app the code above would be:
addChild(map)
dynamicContainerView.addSubview(map.view)
map.view.bindEdgesToSuperview()
map.didMove(toParent: self)
(Some folks even make an extension .addSubviewAndBindEdgesToSuperview() to avoid a line of code there!)
A reminder that the order must be
add the child
add the actual view
call the didMove
Removing one of those?
You've added map dynamically to the holder, now you want to remove it. The correct and only order is:
map.willMove(toParent: nil)
map.view.removeFromSuperview()
map.removeFromParent()
Often you will have a holder view, and you want to swap different controllers in and out. So:
var current: UIViewController? = nil
private func _install(_ newOne: UIViewController) {
if let c = current {
c.willMove(toParent: nil)
c.view.removeFromSuperview()
c.removeFromParent()
}
current = newOne
addChild(current!)
holder.addSubview(current!.view)
current!.view.bindEdgesToSuperview()
current!.didMove(toParent: self)
}
I see two problems. First, since you're making the controllers in the storyboard, you should be instantiating them with instantiateViewControllerWithIdentifier:, not initWithNibName:bundle:. Second, when you add the view as a subview, you should give it a frame. So,
- (void)viewDidLoad
{
[super viewDidLoad];
self.aboutVC = [self.storyboard instantiateViewControllerWithIdentifier:#"aboutVC"]; // make sure you give the controller this same identifier in the storyboard
[self addChildViewController:self.aboutVC];
[self.aboutVC didMoveToParentViewController:self];
self.aboutVC.view.frame = self.utilityView.bounds;
[self.utilityView addSubview:self.aboutVC.aboutView];
}

How to set UITableViewController custom class programmatically?

This is my storyboard:
The UITableViewController, has a generic UITableCell (MMSwitchTableCell) that has an image, a label and switch.
The idea is to be able to create different UITableViewControllers that present different data with the same layout i.e with the same cell object and same behavior. for example one time the UITableView has a list of cells that helps you select fruits, second UITable helps you select furniture.
The two UITablesViewController have no relation between them (no inheritance or aggregation), they are different instances in different viewControllers, I only want to re-use the designed control and the UITableCell code.
So my code has a UIViewController where I declare a property:
#property (strong, nonatomic) MMGoSeePopoverTableViewController* goSeePopoverTableViewController;
and lazy load it:
-(MMGoSeePopoverTableViewController*) goSeePopoverTableViewController
{
if(_goSeePopoverTableViewController == nil)
{
_goSeePopoverTableViewController =(MMGoSeePopoverTableViewController*)
[self.storyboard instantiateViewControllerWithIdentifier:#"switchPopover"];
}
return _goSeePopoverTableViewController;
}
and a second UIViewController in which I declare a property:
#property (strong, nonatomic) MMLayersPopoverTableViewController* layersPopoverTableViewController;
and lazy load it:
-(MMLayersPopoverTableViewController*) layersPopoverTableViewController
{
if(_layersPopoverTableViewController == nil)
{
_layersPopoverTableViewController =(MMLayersPopoverTableViewController*)
[self.storyboard instantiateViewControllerWithIdentifier:#"switchPopover"];
}
return _layersPopoverTableViewController;
}
In the storyboard I've set the custom class to MMLayersPopoverTableViewController, instead I wish to leave it blank and somehow set it in the code. I guess I should do this inside the lazy loaders, but I can't figure how.
Edit
The suggested "This question may already have an answer here:" is not the same as what I'm asking. I have amended the post to explain my problem better.
The idea is to be able to create different UITableViewControllers that
present different data with the same layout i.e with the same cell
object & same behavior.
This sounds like a case where you should use a .xib file instead of a storyboard. The advantage of storyboards compared to .xib files is that you can see the structure of the app in terms of views and the corresponding view controllers. In your case, though, you're trying to reuse the same view with different view controllers. Putting your table in a .xib file that's owned by the view controller will let you load the same table, cell, etc. with whatever view controller you decide to instantiate.
In your .xib file, set the type of the File's Owner proxy to some common superclass of all your view controller classes which contains all the necessary functionality. For example, if all your view controllers are derived from UITableViewController and you don't need any special outlets, set the type to UITableViewController and connect the table to the proxy's tableView outlet. If your view controllers have other common behavior, put all that in a subclass of UITableViewController, use that as the proxy's type, and derive the other view controllers from that class.
Once you've done all that, you can use the -initWithNibName:bundle: method to initialize any of your view controllers and load the same view:
// in one place...
MMGoSeePopoverTableViewController *goSeeVC = [[MMGoSeePopoverTableViewController alloc]
initWithNibName:#"CommonTableView.xib" bundle:nil"];
// and in some other place...
MMLayersPopoverTableViewController *layersVC = [[MMLayersPopoverTableViewController alloc]
initWithNibName:#"CommonTableView.xib" bundle:nil"];

Change the custom class in a storyboard using code when instantiating

I have a tab bar controller and a bunch of the same tabs. Each tab only differs in functionality, but the UI's are all the same. In the storyboard I designed the flow and UI of one tab and set it base class. Then when I create the tabs I tried typecasting them before adding them to the tab bar but it didn't work.
In the storyboard the View Controller indentified "TabView" has the custom class "TabColor"
TabRed *red = (TabRed *)[storyboard instantiateViewControllerWithIdentifier:#"TabView"];
TabBlue *blue = (TabBlue *)[storyboard instantiateViewControllerWithIdentifier:#"TabView"];
However the loadView method in TabColor gets called, not the TabRed/TabBlue.
Also if I nslog it the result is a TabColor object:
NSLog(#"%#", red)
Expected: TabRed
Actual: TabColor
tl;dr:
Storyboards and xibs contain collections of serialized objects. Specifying a class in a storyboard means you will get an instance of that class when you load the storyboard. A way to get the behavior you're looking for would be to use the delegation pattern common in cocoa/cocoa-touch.
Long Version
Storyboards, and similarly xib/nib files, are actually sets of encoded objects when you get down to it. When you specify a certain view is a UICustomColorViewController in the storyboard, that object is represented as a serialized copy of that an instance of that class. When the storyboard is then loaded and instantiateViewControllerWithIdentifier: gets called, an instance of the class specified in the storyboard will be created and returned to you. At this point you're stuck with the object you were given, but you're not out of luck.
Since it looks like you're wanting to do different things you could architect your view controller such that that functionality is handled by a different class using delegation.
Create a protocol to specify the functionality you'd like to be different between the two view controllers.
#protocol ThingDoerProtocol <NSObject>
-(void) doThing;
#end
Add a delegate property to your viewcontroller:
#interface TabColor
...
#property (strong, nonatomic) thingDoerDelegate;
And then have your new objects implement the protocol and do the thing you want them to.
#implementation RedTabDoer
-(void) doThing {
NSLog(#"RedTab");
}
#end
#implementation BlueTabDoer
-(void) doThing {
NSLog(#"BlueTab");
}
#end
Then create and hook up those objects when you load the storyboard.
TabColor *red = [storyboard instantiateViewControllerWithIdentifier:#"TabView"];
red.thingDoerDelegate = [[RedTabDoer new] autorelease];
TabColor *blue = [storyboard instantiateViewControllerWithIdentifier:#"TabView"];
blue.thingDoerDelegate = [[BlueTabDoer new] autorelease];
This should then allow you to customize the functionality of the view controller by changing the type of object that is assigned to the controllers delegate slot.
TabRed *red = (TabRed *)[storyboard instantiateViewControllerWithIdentifier:#"TabView"];
TabBlue *blue = (TabBlue *)[storyboard instantiateViewControllerWithIdentifier:#"TabView"];
Casting doesn't change values, it only changes the way the compiler interprets those values (and stops it from complaining when you use type in place of another). So casting a TabColor* to a TabRed* tells the compiler to pretend that your first pointer points to a TabRed instance, but it doesn't transmogrify the object that the pointer refers to into an instance of TabRed.
As waltflanagan explains, storyboards and .xib files contain actual objects, and the type of each object is determined when you create the file; you can't change it at run time. What you can do, though, is to have each of your several view controllers load the same view hierarchy. You don't even have to write any code to do this. Just create a .xib file containing your tab controller and the view controllers for each tab:
Be sure to set the type for each view controller appropriately in the .xib so that the right kind of view controller will be created for each tab:
Set the "NIB Name" field for each view controller to specify a .xib file that contains the view hierarchy that these controllers will use. If you specify the same .xib file for each controller, each controller will instantiate its own copies of those views:
Specify any IBOutlets in the common superclass of your view controllers so that all your view controllers have the same outlets. You can specify that superclass as the type of "File's Owner" in the common .xib file so that IB knows what outlets are available. File's owner is really a proxy for the object that's loading the .xib, so when one of your view controllers (TabRed for example) loads the common view .xib, that controller will be the one that the views in the .xib are connected to. When TabBlue loads the .xib, that object will be the one that those views are connected to.
This might seem confusing at first, but play with it. Understanding this will really help you understand .xib files (and therefore storyboards). They're a lot less magical than they seem when you're a beginner, but once you get it they'll seem even cooler.

Push view to navigation controller from button in other class

I have a UIButton that I create in my sub class ViewController, and add it to my MainViewController.
Now, I added a target method to this button that should push another view controller to my Navigation controller (the one that in the MainViewController).
I know that the method did call when I push the button, but the view wasn't push to the Navigation Controller.
I scanned this drawing - this is the drawing (I also added part of my code):
This is the code I'm using in my button:
(remember it's in a deferent ViewController).
- (void)buttonPressed:(UIButton *)sender
{
Photo_ScreenGlobalView *photo = [[Photo_ScreenGlobalView alloc] init];
[self.navigationController pushViewController:photo animated:YES];
}
Usually I solve these situations with delegation. If there is a view controller which is subordinate to another (i.e. a "sub" view controller) but should have the ability to trigger navigation changes, etc... then it should have a weak pointer back to it's 'parent'. Then the parent VC should implement an appropriately named protocol with a callback for the child to use. The names of these things can be generic, such as #property navigationDelegate and requestNavigationToViewController: or they can be more semantic, such as #property userFormDelegate and userFormDoneButtonPressed:
Generally speaking, a subordinate view controller should not be able to directly modify navigation at it's parent's level; but it can trigger it via more loosely-coupoled interfaces like these.
i came back to let you all know how i actually did it.
after googling a lot found this nice and quick guide how to make DELEGATE
and working with delegate solved all my problems. if you need any help don't hesitate to send me PM.
this is the guide:
http://css.dzone.com/articles/do-not-publishcreating-your

iOS Dev, How can I call a method in the super class of my UIViewController?

i have two UIViewControllers for example A and B.
On A there is a button when pushed loads view B as a sub view. Using the standard initWithNibName. On B there is a button which dismisses View B and should call a method in view A (its Parent View). I thought it should be a simple [super methoddName:perimeter] but this doesn't work. after placing a NSLog printing out the name of the parentViewController it returned null which is odd as surly it should return View A as the Parent view.
any Ideas Cheers
super refers to your class's superclass, not its parent view controller.
A way to quickly do this is create a new init method that takes an instance of view controller A, use that to set the target of your button.
The best way is to define a new protocol on view controller B. Make view controller A conform to it. On init of B, pass in a refernce to A to use as your delegate. Then on the button push in B call the desired method on the delegate.
You might be better off by letting view controller A be a delegate on view controller B.
A can set the delegate property on B when after loading it but before pushing it into view, and when B needs to call the required method, it can just call it on the delegate object.
That makes the coupling between the controllers looser, and if you let the delegate conform to a protocol, you can keep B's knowledge of A to a minimum.
It's better design, if you ask me.
parentViewController won't work if you add view controller B's view as a subview. You will have to find the view controller A in a different way.
Say, if you had added B's view as a subview like this,
[A.view addSubview:B.view];
then you can call view controller A like this.
[[B.view.superview nextResponder] methoddName:perimeter];
If you have added B.view as a subview somewhere within the view hierarchy of A.view.
UIResponder * responder = B.view.superview;
do {
responder = [responder nextResponder];
} while ( ![responder isMemberOfClass:[A class]] );
// `responder` is referencing your view controller A. You will have to cast it.
Easiest way to do this especially if it is a static method:
ViewControllerB
#import "ViewControllerA.h"
- (void)dismissView{
//implementation to close the view
[ViewControllerA methodName:perimeter];
}
Good Luck!