0

I am making an app where I have a container view that is half the screen of my view controller. From the container view controller's class I am trying to access and manipulate a view that sits out side of the container view. (picture below)

enter image description here

I am trying to access and add items to the scroll view from the container view class like so:

parent = (GFProfileViewController*)[self parentViewController];
UIScrollView  *scroll = (UIScrollView *)[parent.view viewWithTag:222];
parent.titleHolders.contentSize = CGSizeMake(320 * 4,60);
UILabel *testLabel = [[UILabel alloc] init];
[testLabel setFrame:CGRectMake(0, 0, 100, 40)];
[testLabel setText:@"My Test label"];
[parent.titleHolders addSubview:testLabel];
scroll.backgroundColor = [UIColor blueColor];

how ever does not work. I tried even accessing the view from the parents "view with tag" method.

neither works.

I know the code is fine because when I move it to the parent vc all works as expected. I need to be able to manipulate the view from the container though. Can anyone help?

nhgrif
  • 61,578
  • 25
  • 134
  • 173
Dnaso
  • 1,335
  • 4
  • 22
  • 48
  • I'm not immediately sure why this code isn't working from the child view controller... but good programming practice dictates that one view controller should control another view controller's view... put this code in the parent view controller and give the parent view a public method that the child can call (and send arguments to) in order to do this work. – nhgrif Feb 05 '14 at 00:41
  • @nhgrif, don't you mean "... good programming practice dictates that one view controller should **NOT** control another view controller's view..." – Duncan C Feb 05 '14 at 00:58
  • @DuncanC Yes, absolutely. Thanks for the correction. – nhgrif Feb 05 '14 at 01:25
  • @nhgrif i kind of get it but since i am using a container view w a page view controller i have no choice – Dnaso Feb 05 '14 at 01:56
  • Of course you have a choice. One view controller can tell another view conroller "Hey, you need to update your views!" but it certainly shouldn't be the one to actually update its views. Consider the difference between telling to change their clothes... and actually changing their clothes for them – nhgrif Feb 05 '14 at 02:01
  • 1
    @ngrif that made me laugh out loud . Ibam going to chrck out duncans answer.. I might just need to change my structure a bit . And i agree (since i am new) i want the easy answer but i might as well do things correctly from the start – Dnaso Feb 05 '14 at 12:57
  • 30 minutes of doing things correctly from the start can be worth 30 hours of untangling `GOTO:` statements later... – nhgrif Feb 06 '14 at 00:24
  • @ngrif I could not agree more and that is why I want to learn it and learn it once. – Dnaso Feb 06 '14 at 00:34

2 Answers2

3

As nhgrif says, don't do that.

You should treat another view controller's views as private.

Again, as nhgrif says, create a public method in the parent view controller that takes the information needed and does the displaying itself.

If the view controllers are just being initialized then the parent view controller's view hierarchy may not exist yet. In that case you'd want to set properties to hold the value(s) you want to display, and then display them in your viewWillAppear method.

With storyboards and iOS >= 6, you can set up the child view controller using an embed segue, and then in your prepareForSegue method you can set the parent view controller up as the child view controller's delegate. That's a clean way to have the child communicate back to the parent. (I have a sample app on github that demonstrates this technique if you need a more detailed explanation.)

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • 1
    Upvoting the answer for telling me I'm write twice. The delegate relationship is good too. – nhgrif Feb 05 '14 at 01:26
  • Interesting... Can i achieve this using storyboard ?? – Dnaso Feb 05 '14 at 01:58
  • Yes... did you look at the code in his sample app? Duncan, perhaps the answer could be improved by posting some of the most-relevant code snippets. – nhgrif Feb 05 '14 at 02:00
  • Reread the last paragraph of my answer. The sample app I link to even shows a working example using storyboards. – Duncan C Feb 05 '14 at 03:28
  • Sorry was looking from my iphone i am going to check it out now – Dnaso Feb 05 '14 at 12:54
  • @DuncanC I did check out your app. I am doing the same (embedding the suege). I like this obviously clean. My question is do you think it is better to create a protocol vs if I just access my parent controllers methods via methods declared in the header file? is there a difference to the two approaches? I only ask because I want to do this once and never do it the "wrong" way again – Dnaso Feb 05 '14 at 19:16
  • @DuncanC Will this approach allow me to have real time translation of my scroll view? in the container i have a page view controller. I am looking to translate or change scroll pos of my scroll view as i scroll the page view controller. is this possible via methods? IN js i would just translate x amount as the scroll view changes which is easier when I have direct access to a view object rather than having to call methods to achieve some sort of ui change in real time. – Dnaso Feb 05 '14 at 19:19
  • Using methods should be just as good as manipulating the scroll view directly. It's just a single level of indirection, and you'd need very sensitive equipment to detect the additional delay it would cause (very likely less than a microsecond). – Duncan C Feb 05 '14 at 19:28
  • A protocol just defines the methods that will be used to communicate between two objects. It's like a specific lingo for a particular task. Using a protocol and a delegate makes for looser coupling between the parent and child view controllers, which makes the code more general-purpose and more likely to be reusable. It's not that hard to do. My sample app is a working example using a protocol. – Duncan C Feb 05 '14 at 19:30
  • yes no its not hard at all @Duncan C just a little long winded. Question... just to test I added -(void)addLabel to my header file of the parent vc.. i then call the method from the pageview controller (the child vc). I am able to fire the method (i have it logging), however It will NOT update the UI. do you know why? I am sure there is a reason for it but I am not sure why. I am calling the parents methods, unless obj thinks that the scroll view belongs to the child controller? – Dnaso Feb 05 '14 at 19:38
  • @DuncanC I actually think this is a design flaw by apple. I think when a controller is embedded it should act as "one" with the parent vc and be able to access all its views because it is now a part of the view. I do understand the issue with decoupling but you should be able to interface via embed segues easier. – Dnaso Feb 05 '14 at 20:06
  • I did find your answer to be very useful, so I accepted thank you for the help – Dnaso Feb 05 '14 at 20:18
  • Impossible to tell from a general description. Post your code as an edit to the original question. – Duncan C Feb 05 '14 at 20:23
0

As far as programming ethics, correctness, good practices and whatnot, I think others have that covered. What you want to do surely isn't the clean way to go, but I often find myself going for such shortcuts, but you really need to know what you're doing, and if it isn't coming back to bite you later. The good thing is that it takes one line of code to achieve and has very low overhead. The problem is that such approach is highly dependent on the view structure, so if you change it, it will no longer work.

With that being said. What you tried doesn't work because the 'parentViewController' property isn't set on any arbitrary view. It should only be defined on the main view of the view controller, the one you can access from 'viewController.view'.

I will assume from your comment that your view structure is something like:

UIView
    UIView
        UIScrollView
    Container

So basically starting from Container you need to go up one level and then down to UIScrollView like this:

UIScrollView = [((UIView*)[self.superview.subviews objectAtIndex:0]).subviews objectAtIndex:0];

I am unsure if you can search by tag from the upper view, since the one you're searching for is somewhere nested inside, farther than one level down. Assuming you can actually do that, this should also work and be more fail proof.

UIView *upperView = self;
while([upperView superview]){
    upperView = [upperView superview];
}
UIScrollView *scroll = [upperView viewWithTag:222];
manecosta
  • 8,682
  • 3
  • 26
  • 39
  • They sre both in the same vc . The scrollview is inside another view but would that matter? Can i still acesss from superview? How come what i did did not work?? – Dnaso Feb 05 '14 at 01:54
  • I don't know whether or not this actually works, but I certainly can't upvote an answer that promotes horrid programming practices. See my comments and Duncan C's answer for why this is bad. – nhgrif Feb 05 '14 at 02:02