Hiding an Outlet from outside its parent View Controller

Greetings everyone.


I posted a problem very much like this one a while ago which was to call a method found in V.C.1 from V.C.2.


bob133 was kind enough to help me on this. The conversation was cut short due to me moving from one country to another.

In this conversation bob133 offered me the code to do this.


  1. I had to import ViewController1Birth.h into ViewController2.h so I could access the - (void) hideBirthFrameOutlet2 method.
  2. I had to declare the hideBirthFrameOutlet2 method in the ViewController1Birth.h file to make it 'public' so it can be accessed by the (IBAction) goBackToP1 method in ViewController2.m.
  3. Then I went to the ViewController2.m file and keyed in the goBackToP1 method that bob gave me like so:


- (IBAction) goBackToP1:(id)sender
  {
    NSArray <__kindof UIViewController *> *viewControllers = self.navigationController.viewControllers; / get the current list of view controllers */
    ViewController1birth *previousController = viewControllers [viewControllers.count - 2]; / Retrieve the previous view controller in the stack: remember that the last object in an array will be
                                                                                              at index count - 1, so I'll grab the second-to-last because the last one is the view controller on top.
                                                                                              Store this in a local variable because there's no need to hang on to the reference after we're done here. */
    [previousController hideBirthFrameOutlet2]; / this hides the BirthFrameOutlet2 method in ViewController1  */
    [previousController outsideStop];
    [self.navigationController popViewControllerAnimated: YES];
  }


And it worked!! What it did was when I popped the current View Controller off the stack and went back to the previous View Controller the birthFrameOutlet2 would be hidden from view.


Well, when I settled back into my work after the move I tired to do a very similar thing, by hide/unhiding an Outlet in the next View Controller rather than the previous one.


So I went through almost the same steps

  1. I imported ViewController2.h into ViewController1birth.h so I could access the - (void) showF13Outlet method.
  2. I declared the showF13Outlet method in the ViewController2.h file to make it 'public' so it can be accessed by the (IBAction) showFleaOutletOnNextPage method in the ViewController1birth.m file.
  3. Then I went to ViewController1birth.m file and keyed in the showFleaOutletOnNextPage method like so:
- (void) showFleaOutletsOnNextPage
  {
    NSArray <__kindof UIViewController *> *viewControllers = self.navigationController.viewControllers;
    ViewController2 *nextViewController = viewControllers [viewControllers.count + 1];
    [nextViewController showF13Outlet];
  }

In this new attempt the app starts up without problems. However, when I turn to the birth page and tap the button that calls the showFleaOutletOnNextPage method, I get this in the console: "-[__NSArrayI objectAtIndex:]: index 3 beyond bounds [0 .. 1]".


I think that the first line "NSArray <__kindof UIViewController *> *viewControllers = self.navigationController.viewControllers;" Creates an NSArray of View Controllers that have been put on the stack and because I am trying to call a method on a View Controller that hasn't been put on the Stack with the code "[viewControllers.count + 1]" I'm getting this error message.


I know this was a long winded explanation but I had to get across my problem which is, how do I call a method from one method to the next or following method.


Any suggestions?


JR

Look at the thread just below this - "how to open webView url..." - I posted an answer to a similar issue.

You are in danger of creating spaghetti code. That's not a good thing.


You should take a step back and think about the purpose of a view controller. ALL interactions with a VC's view should be handled by that VC alone. Another VC has NO business knowing about specific subviews hidden / shown in another view controller's view.


The message you send to the other VC should be at the application level - "here is the data / state that needs to be displayed". It should be up to the other VC to translate that into hiding / showing / updating specific views. In general you should not expose IBOutlets as public properties or even think of them that way (as in your example of a method "showXYZOutlet"). I know it's a subtle difference but it's crucial to maintain good encapsulation so when you inevitably change your UI down the road, you only have to do it in one place.


As for the details of sending the message, that has been discussed plenty here. You can look for the post Simpsonics mentioned and search for the many other threads about how to use prepareForSegue to pass data to another VC and set a delegate to pass data back.

Hello junkpile.

I understand that I don't want a complex tangle of code but I don't know of any other way. Besides, it won't be that tangled. When you wrote: "The message you send to the other VC should be at the application level." I don't know what you mean by that I'm very sorry to say.


I looked at the post that Simpsonics did and although I see what he means about it having something to do with my problem, I didn't find the solution. I admit this is probably from my lack of skill.


The solution bob133 gave me worked with no problems I could see. No warnings from the compiler. It worked perfectly on my iPads. It didn't even demand more memory usage.


My first problem was this situation - the user was in ViewController2 and wanted to go back to ViewContoller1. I wanted to find a way for the code in ViewController 2 to hide an Outlet in ViewController1 so when the user arrived that Outlet was gone. bob133 gave me the solution that worked.


When I tried to do this in reverse it wouldn't work. I'll pull up the code bob133 suggested so you don't have to scroll up to see it:

- (IBAction) goBackToP1:(id)sender
  {
    NSArray <__kindof UIViewController *> *viewControllers = self.navigationController.viewControllers; / get the current list of view controllers */
    ViewController1birth *previousController = viewControllers [viewControllers.count - 2]; / Retrieve the previous view controller in the stack: remember that the last object in an array will be
                                                                                               at index count - 1, so I'll grab the second-to-last because the last one is the view controller on top.
                                                                                               Store this in a local variable because there's no need to hang on to the reference after we're done here. */
    [previousController hideBirthFrameOutlet2]; / this hides the BirthFrameOutlet2 method in ViewController1  */
    [previousController outsideStop];
    [self.navigationController popViewControllerAnimated: YES];
  }


I think the probem is the line 03. The NSArray is an array of all the ViewControllers on the stack. Please look at that line of code. Am I correct there? If I am, that is the possible way to a solution. What I need is a NSArray that would include the ViewControllers to come. Then I could target that ViewController with a line of code like this:

ViewController1birth *previousController = viewControllers [viewControllers.count + 1];

Adding 1 to the .count rather than subtracting 2 as was done with the first code example.


JR

You are correct, the issue is identifying a pointer to the correct view controller in your array. It is not count+1.That object doesn't exist in the array since any array only contains "count" items, from objectAtIndex:0 to objectAtIndex:count-1. The object you may want is most likely at count-1 (the last view controller added to the navigation controller) depending on how they were added into the navigation controller. However, if you added that view controller yourself then don't look for the pointer to the view controller in the array of the navigation controller. Instead, if you added it yourself like in the other post on this forum, then you have a pointer to that viewController when you created it and you can use that pointer itself rather than grabbing it from the array of the navigation controller.

Hiding an Outlet from outside its parent View Controller
 
 
Q